
Rust is not fast - cjbprime
http://cananian.livejournal.com/69096.html
======
pcwalton
Unsurprisingly, I disagree with most of this post.

* The part about Rust having no shared memory is just wrong. Rust has a very rich set of concurrency primitives at this point: you can use immutable shared memory (Arc), mutexes (MutexArc), reader-writer locks (RWarc), and atomic variables (AtomicInt and friends). And if you're willing to drop down to unsafe code, you get the full set of LLVM concurrency primitives.

* The complaint about smart dereferencing strikes me as odd. Almost every new systems language—Objective-C, D, Go, and Rust (and also Nimrod I think?)—does the same thing that Rust does in that the "." operator also works on pointers. It's type-directed: if you don't know whether you're working with a pointer or not, just go look at the type of the value you're working with. If you don't know a value's type, then you have much bigger problems than knowing whether you'll get an extra memory access on ".".

Hiding the difference between a memory access and a cheaper operation is
something compilers have done ever since the invention of register allocation.

* I don't know what the complaint about "bugs with small objects" is, but all word-sized objects were made into LLVM Values a while back, making us as efficient there as you can be. Unless I'm misunderstanding the complaint.

* Doing code duplication to accommodate different types of smart pointers doesn't work. The smart pointers have different semantics: that's why they exist in the first place. You can't just copy and paste your code.

* The GC in Rust does need work. But so does C++'s GC and nobody is saying "C++ is not fast" because of it.

About the only part I agree with is is "fork-join parallelism and work-
stealing should be better supported". This is an area we'll need to flesh out
more fully at some point. Note that there is now a much more advanced work-
stealing scheduler. (It's not using the most efficient data structures for
work stealing yet though.)

~~~
richardwhiuk
> The GC in Rust does need work. But so does C++'s GC and nobody is saying
> "C++ is not fast" because of it.

C++ doesn't have a garbage collector, so I'm not sure what you are getting at
here.

~~~
kibwen
Sure it does, here's one:

[http://www.hpl.hp.com/personal/Hans_Boehm/gc/](http://www.hpl.hp.com/personal/Hans_Boehm/gc/)

Rust has a GC in the same sense that C++ has one, which is to say that its GC
can be provided by libraries rather than being baked-in to the language.

~~~
cwzwarich
Rust has GC that is part of the standard runtime / library with special
language syntax to go with it. C++ has no such thing.

~~~
kibwen
I'm about to blow your mind here: Rust has _never_ had a GC. Managed pointers
were always backed by reference counting, and the cycle collector stopped
working sometime in 2010 and was never brought back online. Note that I said "
_were_ backed by reference counting" (emphasis mine), because managed pointers
no longer exist in the language (unless you turn on the backwards-
compatibility flag for them, which is only being provided temporarily).

So there's no longer even special syntax for a theoretical GC. Instead the
plan is to greatly expand our support for custom pointer types in general,
which will make all user-provided pointer types (implemented in whatever
libraries our users come up with) first-class citizens. Our own stdlib will
include several pointer types of this nature, including: `Rc` (reference
counting), `Arc` (atomic (thread-safe) reference counting), `RWArc` (memory-
safe Arc for mutable objects), and, yes, a `Gc` type.

And while it's true that we may provide hooks into the runtime to allow the
`Gc` type to be implemented more efficiently, any user library will be able to
use these hooks to implement its own GC. There won't be any implementation in
the language itself.

------
kibwen
> This post is written from my experience with Rust in May 2013.

This post is based on an experience with a version of Rust with the old
runtime, with pervasive internal iterators, with managed pointers (which we
haven't recommended using for _years_ , and are in the process of chucking
out), and is missing all the perf work we've achieved over the past six
months. Suffice to say, any experience with Rust ( _especially_ in terms of
performance) from that long ago is so dated by this point as to be completely
unrepresentative.

That isn't to say that Rust is _fast_ yet, because we refuse to say that until
we're as fast as C++. But a headline like this seems needlessly sensational
(perhaps consider "Performance pitfalls in the implementation of Rust 0.6").

------
cscottnet
Oh, look, my rant finally got noticed. ;)

First off -- yes, it is a rant. I actually had some more nuanced in-person
conversations, but those aren't nearly as fun to post. So you get the version
that was fun for me to write. Sorry.

That said, I'm glad to hear Rust is all wonderful now. It sounds like pcwalton
agrees with most of my points and got them fixed. Kudos. I look forward to
better GC, fork-join parallelism, and work-stealing.

It doesn't seem like anything significant was done with the type system,
though. How does Rust handle "find_and_insert" now?

If folks are interested in actually critiquing code style, feel free to check
out the rusty-turtle code at
[http://cananian.livejournal.com/68747.html](http://cananian.livejournal.com/68747.html).
I disagree that I was trying to write Rust as if it were some different
language -- my frustration was in part because I was _trying_ to write things
with Rusty ownership types, etc, and getting that to work properly was so
frustrating (and the error messages so opaque). If I just stuck managed boxes
around everything my life would have been simpler -- but then I would not have
been programming in Rust, really.

I'm interested to hear more discussion on the primary point made in my blog:
is Rust supposed to be 'safe and fast'? Or just 'safe'?

~~~
kibwen
> I'm glad to hear Rust is all wonderful now.

Nope, we're not saying that yet, Rust still needs a ton of work!

> How does Rust handle "find_and_insert" now?

Google doesn't return any obvious results for this string, what is it supposed
to do?

> is Rust supposed to be 'safe and fast'? Or just 'safe'?

If Servo (written in Rust) isn't faster than Gecko (written in C++), then
Mozilla won't continue funding Rust. Rust _needs_ to be fast in a very
existential way. :) And it will be! Initial benchmarks of Servo's performance
are incredibly promising (pcwalton could go into more detail here), and
there's still bushels of low-hanging fruit to be plucked.

~~~
mscottmcbee
>If Servo (written in Rust) isn't faster than Gecko (written in C++), then
Mozilla won't continue funding Rust. Rust needs to be fast in a very
existential way.

Ouch, I didn't know the project had a deadline. By when does it need to be
faster?

~~~
pcwalton
kibwen is not speaking for the Rust team.

~~~
kibwen
Indeed I'm not! Just an over-enthusiastic community member here. :)

~~~
mscottmcbee
Ok, that makes me feel better. I've been watching and waiting for Windows
support to get better, and I'd hate to have it fizzle before I really got to
play with it.

------
sixbrx
I've been using Rust for finite element method research, and I've been pretty
happy with the results, the code is faster than my previous effort in Julia.
That may be somewhat of a second system effect, though. I had trouble with
detecting in Julia what the effect of the garbage collector was, I don't know
if that's improved or not. The code in Rust has no shared/gc usage at all, so
I'm pretty sure what the effect of garbage collection is :)

One thing I like about Rust is that it makes doing allocation-free
performance-critical calculations somewhat easier and safer. This is because I
can preallocate my data before critical loops, _inside_ the constructors of
implementation objects, then hand out borrowed, non-mutable pointers to this
data to clients from these objects. That way the clients don't have to pass in
buffers to use, which is none of their business -- it would break
encapsulation for them to even know what kind and how large of buffers to
pass. The clients cannot detect that they are actually receiving a pre-
allocated, shared buffer.

In other languages that would ordinarily be very dangerous without extra
effort, because if they or some other code turned around and called routines
that used that same pre-allocated buffer again, then the data would be
silently written over, in an "action at a distance" way which would be a
nightmare to maintain.

But Rust "freezes" the contents behind the immutable borrow, making sure that
it can't be borrowed mutably again and thus cannot be changed, while the
immutable borrow is in use, from _anywhere_ in the program, all resolved at
compile time.

This allows getting very aggressive with the pre-allocation strategy.

~~~
kibwen
This is fascinating. If you ever have the time, would you be willing to post
this along with some of your code to the mailing list
([https://mail.mozilla.org/listinfo/rust-
dev](https://mail.mozilla.org/listinfo/rust-dev))? We love getting informative
feedback like this. And if you have any suggestions for improvement, we
entertain those as well. :)

~~~
sixbrx
I'd be happy to, I'll make the code somewhat presentable and post a message to
the list sometime soon.

------
tete
And Go is even slower, so what? If you want to squeeze out every little bit of
performance, use C and not a fairly young, mostly unoptimized, heavily
changing, not-yet-1.0 language.

------
Pxtl
The "message-only parallelism oops we need shared memory" thing seems to be a
common problem - at the beginning every language thinks they don't need the
hellish headache of shared memory. This is not new - Ada went through this
like 25 years ago.

~~~
pcwalton
Yeah, we went through the same thing. But what's curious about this post is
that Rust had safe shared memory for almost a year at the time it was written.

I actually think one of the most interesting parts about Rust is its shared
memory story: if you use mutex-protected shared memory, the compiler enforces
that you take the locks properly, eliminating data races.

~~~
DanWaterworth
> the compiler enforces that you take the locks properly, eliminating data
> races.

I assume you don't mean the compiler prevents deadlocks, but I'd be very happy
to be proved wrong.

~~~
kibwen
Here's a rather old post that describes the kinds of races that the Rust
compiler prevents:

[http://winningraceconditions.blogspot.com/2012/10/what-is-
da...](http://winningraceconditions.blogspot.com/2012/10/what-is-data-race-
and-when-is-race-not.html)

 _" [...] Rust's type system guarantees that concurrent tasks cannot share
state but instead must use message-passing to communicate, which precludes the
possibility of data races completely by enforcing happens-before relationships
on all data accesses (or in the case of this post,[1] by enforcing mutual-
exclusion relationships). Yet it's still possible to write nondeterministic
programs in Rust (using select2, failure propagation, etc), and so race
conditions are still possible."_

...Though I'm not sure if this is all that pcwalton is referring to.

[1]
[http://winningraceconditions.blogspot.com/2012/09/rust-4-typ...](http://winningraceconditions.blogspot.com/2012/09/rust-4-typesafe-
shared-mutable-state.html)

------
gnur
I cannot know for sure but this seems like a post of someone trying to write
rust like you write a different language. The idioms are different and if you
stick to your old routine it won't work like you would want to.

~~~
chalst
And the reason you can't know is that he says nothing concrete, in the sense
that none of his criticisms are grounded in examples.

~~~
cscottnet
Please see
[http://cananian.livejournal.com/68747.html](http://cananian.livejournal.com/68747.html)
and [https://github.com/cscott/rusty-turtle](https://github.com/cscott/rusty-
turtle).

Yes, the post is brief since it was written from notes several months after I
had been talking to the rust guys. I couldn't justify the time to spend making
the examples more concrete (and wasn't exactly encouraged to do so at the
time). Sorry, that is as far as I've carried the load, you've got to take it
from here.

------
cliffbean
He complains about message-passing being prone to races, when earlier in that
same paragraph he complains about the lack of shared memory between tasks? It
sounds like he has some legitimate concerns, but this blog post reads more
like a rant than a considered criticism.

------
emillon
The big problem for me is the compilation time, which is very slow (this may
or may not be because the compiler itself is written in Rust).

I dedicated a few days to hacking on rustc this summer, and while the tooling
and community are very nice, the compilation times are really high. Git pull
&& make ? 20 min. Edit a file && make ? 5 min. IIRC the major was that
metadata got serialized/unserialized all the time, but anyway it's one of the
priorities. I'll hack again on it with pleasure when I'll have more time.

~~~
kibwen
> this may or may not be because the compiler itself is written in Rust

Well, due to the nature of self-hosting compilers, it's certainly greatly
exacerbated by being written in Rust. :)

To wit, a self-hosting compiler must actually compile itself no less than
three times to ensure that the generated artifacts "converge" upon a single
point. This is as true for Rust as it is for GCC.

~~~
graue
Three times, so:

    
    
        1. Old version compiles new version
        2. New version, compiled by old version, compiles new version
        3. New compiled by new-compiled-by-old compiles new version
    

I hadn't heard of this before and initially didn't see why #3 is special. But
I guess the idea is, the output object code is a function of both the input
source code and the compiler version. And those two are the same for #2 and
#3. So you expect the output of both #2 and #3 to be identical, and if it's
not, that's a bug in the compiler. Is that how it works?

~~~
asdfs
Yes, the third compile is effectively a single very large test.

------
eonil
I don't care performance a lot if it's sane enough.

At least for me, I would use Rust if it supports (1) safe and nice symmetric
coroutine with automatically growing stack (2) devices for correct programming
- such as const-correctness. (3) a device guarantees safe communication
between threads - like Go channel. (4) stable implementation. (5) full support
for C interop.

AFAIK, Rust now has all except #4. So I am waiting it to be stabilized.

Actually I want something like C++ with symmetric coroutine, but D is too
unstable for coroutine…

