

Computer Language Benchmarks Game – Rust vs. Go - riccomini
http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=rust&lang2=go

======
jerf
Probably more interesting is Rust vs. C++. Rust is actually beating C++ on
several benchmarks, by a comfortable margin.

Go is a modern scripting language, stripped of the features that make
scripting languages impossible to multithread and intrinsically slow,
statically typed, and with things built on top of the multithreading. (The
Perl/Python/PHP/etc series of languages is very heavy weight, huge VMs,
dynamic everything, and while if you started from day one to write one of them
to be threaded you could probably do it, bodging it on after the fact was
virtually impossible. IMHO "dynamic everything" turned out to be a great deal
more dynamic than was really necessary. But it was a worthy experiment!) Rust
is a C++ replacement. Go is very fast relative to its real competition, but it
is certainly not a C++ replacement. It also seems to me that Go is likely to
converge on being easier to develop; I don't think Rust is going to get all of
this quite for _free_. Both will sit on an interesting part of the bang-for-
the-buck curve that is currently not well-occupied. That is, it _is_ occupied,
but it is not _well_ occupied.

~~~
shanemhansen
Please illuminate me as to how you have arrived at the conclusion go is a
scripting language. It doesn't check any of the scripting language boxes I've
ever heard of.

~~~
jerf
"It doesn't check any of the scripting language boxes I've ever heard of."

Well, as I sort of implied but will now spell out, the 1990s produced a
certain stereotypical definition of a "scripting language" that I think is
ultimately overspecified. A 1990s scripting language is _extremely_ dynamic,
including the type system, everything's boxed, everything's a hash table of
one form or another, and in general everything converges to produce a language
that is very fluid, but ultimately very slow. These were overreactions to the
languages of the day, which were, I think, easy to forget how hard they were
to use in practice. Even when you use C or C++ today, you're still using it
with radically better community best practices, libraries, and better tool
support. 1990s C++ was some hard stuff to work with even before you let
threading in. Into that world, Perl and Python were a breath of fresh air!

It is easy to get deceived by the constant stream of benchmarks that are 50%
faster than last month and the constant stream of incredibly-micro micro-
benchmarks where JS or something is as fast as C at adding together small
integers in an array or something. The reality is that after years and years
and years of work, they're fundamentally slow. Yes, even Javascript; remember,
for asm.js to produce such fantastic speedups, Javascript must be leaving that
much performance on the table.

Go ends up in practice working very like a scripting language; line counts are
virtually identical in my experience, logic is very similar, and the fluidity
is there, partially by virtue of ignoring the very things that Rust brings out
and makes explicit (the ownership and such). I can't say with a straight face
that the logic comes out the _same_ , the way a Perl program can be virtually
transliterated into Python, because the ability to use concurrency seriously
means that you don't have to bend into the contortions you sometimes do to
simulate it in those older languages, but otherwise it works similarly, except
that you use structs and interfaces instead.

Scripting 1.0, being made when this world was younger and 150MHz was a fast
machine, made some mistakes in thinking that compilers would eventually be
able to overcome their fundamental weaknesses. It was a neat experiment to
make everything wildly dynamic, but in the end, you end up paying and paying
and paying and paying and paying for that dynamism, but you only use it
rarely. You may be inclined to object, saying "I use it all the time! I use
Ruby gems to assemble all sorts of complicated and interesting objects!", but
that's because we humans are really bad at order of magnitudes inside of
programs... you set up your objects essentially once at the "beginning" of
some computation, then you pay for that dynamism _billions of times_. You pay
for the awesome exception handling all the time, yet only use it rarely. You
pay... etc.

Scripting 1.0 also accidentally interpreted the failures of the languages of
the day, which were all statically typed, as being significantly caused by
static typing. I'd suggest we've since learned that the static typing of the
time was simply bad; the languages are not all that great, and the ways they
were used even worse (inheritance favored over composition, etc.). So they
whiplashed wildly to the opposite, which turns out to have been an
overreaction similar to the previous paragraphs. You pay for all that typing
dynamism all the time, yet in practice, legal inputs to a given function are
characterized by a limited number of types, legal outputs also, and all this
dynamism is really going to waste. (Go's interfaces capture %95 of the use
cases here while still being perfectly statically typed. There are other
possibilities as well for other languages to explore.)

Scripting 2.0 is fixing that problem by creating languages that judiciously
use dynamism instead of indiscriminately slathering it everywhere, that are
merely _somewhat_ slower than C rather than the night-and-day of 1.0, are
still fairly easy to program in even if they are not as wildly dynamic, and
execute an order of magnitude faster than Python or Perl or JS while also
obtaining the advantages of more static typing for software quality. Go is
merely the most prominent of this line of language, there's a whole bunch of
them bubbling up right now. Julia is trying to fit into this for numeric
computing, LuaJIT is sitting there sipping the punch wondering why all the
other languages are so many years late to the party, there's another language
that's popped up on HN every so often in the past few months that fits in here
but escapes my mind, and I think we're going to see more of this. With
computer processors speeds having stalled it becomes a difficult proposition
to go up to a programmer and promise them the moon, and all they have to do is
be willing to be 20-100x slower than the competition, and, uh, well, no, you
won't just catch up in 5 years of processor advancement because raw clock
speeds are stalled.

So I find in practice Go is a Scripting 2.0 language. You write it at roughly
Python speeds, it runs much faster and works much better in the cloud where
that actually matters, and part of the way it accomplishes this is to do
things like gloss over memory management details and exactly how interfaces
are satisfied (dynamic lookup at runtime, compare with wycat's comments in the
thread on how Rust does it), and generally focuses on being relatively easy
and fast to write. Looking at it from another direction, compare Go to Rust
and Go looks more like a scripting language than a competitor to Rust.

I could write a similar post on how Rust is also a harbinger of a new wave of
languages that are trying to replace C++, but make it easier to write correct
code than to write incorrect code. (C++, alas, makes both quite easy, and
makes it hard to tell which is which. It is true that correct code is
possible, but it takes more skill to get there than it should.) Nim, for
instance, fits into this mold, though a great deal less popular. I expect
more. The dependent type community is also struggling mightily in the
direction of putting this sort of thing out, and I wouldn't be surprised to
see something moderately practical pop out in about 5 years or so.

I know the narrative right now is that Rust and Go are in some sort of fierce
competition, but personally I see a world where they live in harmony, with a
great deal less overlap than most people currently see, just as Python would
still not displace C even if it were in fact 25 times faster.

It's really quite an exciting time and I can't wait to get my hands on these
languages. I am _ready_ to leave C and C++ behind. Of course I've already got
Go in hand, but I look forward to Rust. (As I am personally a "cutting edge"
but not a "bleeding edge" sort of guy in language choice, Rust and I are not
yet a match. I consider this just a matter of time.)

All just my opinion, of course. I am not the keeper of the term "scripting
language." YMMV.

~~~
dcposch
I agree with most of what you said, but Go is not a scripting language. The
defining characteristic of scripts is that they are interpreted. Go compiles
ahead of time to a statically linked binary.

~~~
jerf
"Scripts are interpreted" is a bit 1990s; as the Scripting 1.0 languages have
tried to squeeze every last bit of performance out of the constraints they
labor under this is less true than it used to be. And more to the point,
rather than a binary distinction like you could have claimed in 1995, it's
much, _much_ more a continuum now, and with computers running a bazillion
times faster, much less important. I'm not that worried about applying a
useless criterion to languages that dates from an era whose desktop computers
would aspire to keep up with a low-end 'feature' phone.

~~~
laumars
I think the former poster meant that scripts are JIT compiled rather than
interpreted. But either way, with scripting languages, you redistribute the
source code. With AOT compiled languages such as Go and Rust, you redistribute
the binary (albeit some will always prefer to compile on the target PC).

That's not to say that I think Go and Rust are the same classification of
languages though. I think "scripting vs compiled" is an overly simplistic
view. Go compares better against the byte-code AOT compiled languages such as
C# and Java. These kind of languages are also seen in some traditionally
scripting-dominated markets (eg web development) while still being used as
systems languages as well.

------
lazyjones
Yes, LLVM is good these days. But look at the source code and decide for
yourself what kind of code you'd rather read/work on. Personally, I'll stay
away from Rust for the sake of my eyes:
[http://benchmarksgame.alioth.debian.org/u64/program.php?test...](http://benchmarksgame.alioth.debian.org/u64/program.php?test=spectralnorm&lang=rust&id=3)
(Go counterpart:
[http://benchmarksgame.alioth.debian.org/u64/program.php?test...](http://benchmarksgame.alioth.debian.org/u64/program.php?test=spectralnorm&lang=go&id=1))
- Go code I've written will eventually perform better as the compiler is
optimized, but optimized Rust will always be an ugly mess.

~~~
TheHydroImpulse
It's highly unlikely Go will be able to compete in terms of compiler
optimizations because of it's goal of fast compilation speeds. The majority of
the time Rust spends compiling code is within LLVM last time I checked,
because LLVM does quite a bit of optimizations.

Then if you take into account the fact that Go's language features do not
promote highly-performant code (GC, heap allocation, dynamic dispatching) that
a few people in this thread has touched on.

While better looking code is highly subjective, Rust has indeed traded off
some aesthetics for safety, performance, etc... However, that's not to say
Rust code is ugly, you can certainly write very nice looking code in Rust.

~~~
lazyjones
> _It 's highly unlikely Go will be able to compete in terms of compiler
> optimizations because of it's goal of fast compilation speeds._

For a given piece of code as written today, its compilation speed will
naturally improve over time as processors get faster. Optimizing it to a
certain degree becomes faster for the same reason. Therefore, assuming a fixed
threshold for what constitutes "fast [enough] compilation", potential for more
optimizations becomes available over time.

> _However, that 's not to say Rust code is ugly, you can certainly write very
> nice looking code in Rust._

That's true for many languages, including PHP or Perl. The problem is: once
you use these languages more, you discover that most people don't do it (for
various reasons, one of them often being that they're trying to be clever).

------
digitalzombie
I blame it on Rob Pike saying it was a system language.

He later regret it but it's too late everybody just bench Go with any system
language there is out there.

Go is better off as a middleware language that rival that of Node.JS
(javascript) imo. Everybody see Node.JS (javascript) as somesort of web dev
language in the same vein as Ruby, Python and PHP. But the web frameworks out
there that I've seen are mostly incomplete or just very bare bones compare to
RoR, Django, and Laravel. Express is very very barebone and you wouldn't
prototype with that or do anything quick with that. You can argue that would
use Node.JS for long term stuff and my counter point would be if your code
have to survive for more than 6 months then use a type language. I don't
believe a dynamically loose type language such as Javascript is suitable. Go
would be better in this regard.

I'd like to see Rust vs Ada vs C++

~~~
TheHydroImpulse
> Express is very very barebone and you wouldn't prototype with that or do
> anything quick with that

The point of the Node ecosystem is to be library-based. It's quite rare to
have full-fledge frameworks dictate an environment like Rails does (and a
method I personally hate). Go has the same principle afaik with it's web
libraries.

I'd personally rather use a dynamic language rather than a statically-typed
one with a weak type system. While the former can produce more errors at
times, the later is often times just too inflexible and painful to use
properly.

------
anonfunction
This lines up with my experience with the two languages as well. Usually I
could get Rust to be virtually just as fast as C or C++ while Go would be
roughly twice as slow. However I still prefer Go due to the quick compilation
times and ease of development.

------
SwellJoe
Had I been asked "which do you think is faster, on average?" I would have
guessed Go. I'm not sure _why_ but that was my gut feeling. Rust feels newer
and less developed (though they are both similar in age), to me, and also more
ambitious in some regards.

But, this turns that on its head. Microbenchmarks are, of course, to be taken
with a grain of salt, but it's still a useful data point. More useful would be
a discussion of _why_ it is so.

~~~
wycats
A design goal of Rust is to target the performance of well-written, safe C++.
It doesn't (today) even ship with a garbage collector, discourages heap
allocation (which is your only option in normal Go), and avoids dynamic
dispatch in the vast majority of cases. In contrast, idiomatic Go code is
garbage collected, heap allocation heavy, and dynamic dispatch heavy.

That isn't always going to mean that Rust code is faster, but for benchmark
games like this, when Rust is slower it almost always means that the benchmark
is wrong.

~~~
SwellJoe
I read an article recently about memory in Go (the "why is my go program
138GB!?" article), which was enlightening and also weird. I don't have any
experience with either language, but have plans to learn a smattering of both
in 2015.

As for dynamic dispatch...does Rust not support it, or is there some language
construct that allows solving the problems dynamic dispatch solves without
imposing annoying limitations or needless verbosity? And, aren't there fast
implementations of dynamic dispatch?

~~~
wycats
> As for dynamic dispatch...does Rust not support it, or is there some
> language construct that allows solving the problems dynamic dispatch solves
> without imposing annoying limitations or needless verbosity?

Rust supports dynamic dispatch if you truly need it, but the normal approach
is to use trait _bounds_ , which the compiler expands when used. Here's an
example:

    
    
        fn main() {
            let r = MemReader::new("hello world\n".as_bytes());
            get_line(&r);
        }
    
        fn get_line<R: Buffer>(r: &R) -> IoResult<String> {
            r.read_line()
        } 
    

In this very simple example, the `get_line` function takes any value that
implements the `Buffer` trait, which provides the `read_line` method. When
`get_line` is called inside of `main`, the compiler creates a special version
of `get_line` that takes a `MemReader`, and dispatches to its implementation
of `read_line` statically. In practice, this feels a lot like dynamic
dispatch, but using trait bounds _always_ results in static dispatch in
practice. Also, in this example, the `MemReader` is stack-allocated, and lent
to the `get_line` method.

In very rare cases, you may want dynamic dispatch. For example, imagine you
have an array of Buffers, and want to read a line from each of them. In this
case, you use a "trait object" (a `Box<Buffer>`), which moves the object to
the heap and allows dynamic dispatch.

In practice, I have written many thousands of lines of production Rust code
and have only encountered a need for trait objects a handful of times. This
means that idiomatic, normal Rust code is stack-allocated and statically
dispatched.

The verbosity of trait bounds has decreased more and more over time, and my
favorite proposal (by aturon) for making it pretty close to maximally
ergonomic is:

    
    
        fn main() {
            let r = MemReader::new("hello world\n".as_bytes());
            get_line(&r);
        }
    
        fn get_line(r: &impl Reader) -> IoResult<String> {
            r.read_line()
        } 
    

I hope this lands as new syntax after 1.0 :)

~~~
allan_s
it would indeed be great to have this syntax, can you link to the RFC if you
have it? so that we can follow how is this proposal going.

------
biggest_lou
The Rust benchmarks will become a lot more interesting (and realistic) when
they don't rely so heavily on unsafe calls to C code.

~~~
smosher_
The unsafe calls to C all seem to be in the gmp^H^H^Hpidigits benchmark. It's
unrealistic to avoid gmp in this scenario.

~~~
dbaupp
One can definitely make a case for pidigits mainly being an FFI benchmark.

