Live data from Hacker News

The Rust standard library no longer has any scheduling baked into it

mail.mozilla.org

1–10 of 71 posts

Re: The Rust standard library no longer has any scheduling baked into it

#3
> libstd is that much closer to being used in a "bare metal" context. It's still aways off, but we're getting closer every day!

This is so cool. Most programming language implementations come with a lot of baggage. They assume that the VM can stop the world, or that the VM will be in control of all threading, or that the VM will use global variables, or that the VM can move objects around in memory whenever it feels like it. VMs like this are like demanding houseguests that insist that everything conform to their expectations. VMs like this are not easy to embed and do not "play nicely" with other software.

Doing the work to factor these assumptions out of the core language/VM/implementation and into libraries is not easy work, but you end up with something so much more versatile. Lua is this way, and of course C. It sounds like Rust is going this route too, and that's so cool to see.

Re: The Rust standard library no longer has any scheduling baked into it

#4
I've also have come full circle on N:M / green threads. Now I'm back to thinking they aren't worth the effort / pain. My experience comes from C / C++ land and not Go and Rust but I think same lessons apply. In experience I ended up using a few different framework from libcoro to Mordor to raw swapcontext().

I was initially sold on the N:M model as a means of having event driven programming without the callback hell. You can write code that looks like pain old procedural code but underneath there's magic that uses userspace task switching whenever something would block. Sounds great. The problem is that we end up solving complexity with more complexity. swapcontext() and family are fairly strait-forward, the complexity comes from other unintended places.

All of a sudden you're forced to write a userspace scheduler and guess what it's really hard to write a scheduler that's going to do a better job that Linux's schedules that has man years of efforts put into it. Now you want your schedule to man N green threads to M physical threads so you have to worry about synchronization. Synchronization brings performance problems so you start now you're down a new lockless rabbit hole. Building a correct highly concurrent scheduler is no easy task.

A lot of 3rd party code doesn't work great with userspace threads. You end up with very subtle bugs in you code that are hard to track down. In many cases this is due to assumptions about TLS (but this isn't the only reasons). In order to make it work you now can't have work stealing between your native threads and then you end up with performance problems and starvation problems.

Next thing you realize is that you're still spending lots of memory on creating stacks for your green threads. Then you realize pthreads just let you create small stacks for your native threads and then you realize that 8Mb stacks don't mater much due to delayed allocation. I think that both Rust and Go have back tracked on spaghetti stacks since they are a lot of work require the compiler to generate extract code and they have some bad worst case scenario behavior (where you can get in a look of growing and shrinking a stack reputably due to a function call in a loop).

The final nail in the coffin for me was disk IO. The fact is that non network IO is generally blocking and no OS has great non-blocking disk IO interfaces (windows is best but it's still not great). First, it's pretty low level, eg. difficult to use. You have to do IO on block boundaries. Second, it bypasses the page cache (at least on Linux) which in most cases kills performance right there. And in many cases this non-blocking interface will end up blocking (even on windows) if the filesystem needs to do certain things under the covers (like extend the file or load metadata). Also, the way these operations are implement require a lot lot of syscalls thus context switches which further negate any perceived performance benefits. The bottom line is that regular blocking IO (better yet mmaped IO) outperforms what most people are capable of achieving using the non-blocking disk IO facilities.

This is clearly based on my own experiences. It looks like the Rust folks had similar experiences. So my hope is that anybody thinks long and hard before going down the N:M rabbit hole. I ended up studying my mistakes and history is chock full of people abandoning the N:M model. You can read about the history of NTPL (which is the threading model in Linux 2.6+/ glibc) versus NGPT which was the N:M threading model purposed. The 1:1 NTPL model was simpler and performed better. Freebsd and Solaris moved from their N:M threading models to 1:1 models.

I think the N:M model is going to keep rearing it's head in academic papers about performance of highly scalable systems but in the real world it's benefits / performance will keep being elusive. The only counter point to this is Go that seams to be making a run for it with Go routines.

I should have titled this comment "How I learned to stop worrying and love plain old threads."

Re: The Rust standard library no longer has any scheduling baked into it

#5
post #3

> libstd is that much closer to being used in a "bare metal" context. It's still aways off, but we're getting closer every day! This is so cool. Most programming language implementations come with a lot of baggage. They assume that the VM can stop the world, or that the VM will be in control of all threading, or that the VM will use global variables, or that the VM can move objects around in memory whenever it feels…

On the flip side, though, those assumptions are also what lets a runtime actually do things for you. In the limit, a runtime that assumes nothing is simply machine language. Even C has a runtime, whose assumptions can be violated quite thoroughly by other languages.

The less the language actually specifies, the more likely you are to encounter the situation where two Rust libraries can't work together because they actually work under Rust-Alpha (some twiddling of the knobs) and Rust-Beta (some other incompatible twiddling of the knobs).

I don't know that this is a problem for Rust yet, but it is the first announcement from the Rust team which has actually dropped my level of interest in the language. Is there recognition that this is a tradeoff and not something you can think of as purely a "feature"? Do they realize that this was a huge step towards potential fragmentation of the language? (Not just in this particular decision, either, but the general mindset that leads towards this decision leads to fragmentation.)

Re: The Rust standard library no longer has any scheduling baked into it

#6
post #2

For context, there's been some discussion over the last two months about whether Rust should use 1:1 or M:N threading or both. https://mail.mozilla.org/pipermail/rust-dev/2013-November/00...

Half a year ago: "I wouldn't be surprised to see Rust at least abandon M:N soon though once they start really optimizing performance."

With Rust I'm sure they'll drop M:N, if not now then eventually, like essentially everybody else in CS history. It seems great on a superficial level, but when you really start caring about performance and latency and fairness it's the pits.

My question is when Go will drop it. Imagine how easy it would be to have C call Go code if Go didn't have its own threading and segmented stacks; simply push the arguments and call/jmp. Maybe reference a few objects. None of that allocating a stack, locking a goroutine to a thread, stealing work to another thread, etc. You could even embed Go into a C program instead of having to do it the other way around, and you wouldn't have the problem of adapting existing programs or rewriting them in Go.

But I bet Go will continue with M:N and segmented stacks for a decade because it seems like they actually want it to be difficult to interface with anything else. Like Pure Java, they want you to rewrite everything in Go instead of just using some existing library.

Re: The Rust standard library no longer has any scheduling baked into it

#7
post #5
post #3

> libstd is that much closer to being used in a "bare metal" context. It's still aways off, but we're getting closer every day! This is so cool. Most programming language implementations come with a lot of baggage. They assume that the VM can stop the world, or that the VM will be in control of all threading, or that the VM will use global variables, or that the VM can move objects around in memory whenever it feels…

On the flip side, though, those assumptions are also what lets a runtime actually do things for you. In the limit, a runtime that assumes nothing is simply machine language. Even C has a runtime, whose assumptions can be violated quite thoroughly by other languages. The less the language actually specifies, the more likely you are to encounter the situation where two Rust libraries can't work together because they ac…

This strikes me a pragmatic decision driven by wanting to reduce complexity and be interoperable with 3rd party libraries (mostly C code). If you read my other comment here, I outlined the problems I ran into with the N:M threading model (although mostly in C land). It strikes me as Rust ran into the same issues.

I'm beginning to think that you can't make N:M well unless you take the approach that language X itself is the platform and everything needs to be done in language X. Java has taken this approach (yes I knot there's the JNI -- we know how fun to use it us -- but the Java community is pretty bent on writing almost everything in Java). Go seams to be going that direction by rewriting all the standard library, runtime and compilers slowly in Go. I don't know Erlang's stance on this.

It looks like Rust is taking the approach of we want to be a systems language AND we want to be interoperable with tons of existing 3rd party code (which implies mostly other C libraries).

P.S: Nice to see you commenting here Jermey. It's been a long time since we talked (cuda days).

Re: The Rust standard library no longer has any scheduling baked into it

#8
post #2

For context, there's been some discussion over the last two months about whether Rust should use 1:1 or M:N threading or both. https://mail.mozilla.org/pipermail/rust-dev/2013-November/00...

Half a year ago: "I wouldn't be surprised to see Rust at least abandon M:N soon though once they start really optimizing performance." With Rust I'm sure they'll drop M:N, if not now then eventually, like essentially everybody else in CS history . It seems great on a superficial level, but when you really start caring about performance and latency and fairness it's the pits. My question is when Go will drop it. Imagi…

Erlang has been using M:N very successfully and managed to have good latency and fairness while sacrificing performance. The hard part about M:N is doing it without a VM.

EDIT: To clarify I believe you are too harsh about M:N, and that Erlang is excellent proof that M:N has its use cases.

Re: The Rust standard library no longer has any scheduling baked into it

#9
post #4

I've also have come full circle on N:M / green threads. Now I'm back to thinking they aren't worth the effort / pain. My experience comes from C / C++ land and not Go and Rust but I think same lessons apply. In experience I ended up using a few different framework from libcoro to Mordor to raw swapcontext(). I was initially sold on the N:M model as a means of having event driven programming without the callback hell.…

A lot of the issues surrounding M:N threads are a result of poor operating system support. The central problem is that a syscall blocks a kernel thread even when there are more user level threads to run.

If operating systems supported something like scheduler activations (a 20 year old technique), then this becomes less of a problem. The gist of it is that every time the kernel thread would block, or gets scheduled, instead of returning back to where it was executing, it upcalls into a user level scheduler which can then choose to schedule user threads. Its a shame that this technique isn't more common.

Re: The Rust standard library no longer has any scheduling baked into it

#10
post #2

For context, there's been some discussion over the last two months about whether Rust should use 1:1 or M:N threading or both. https://mail.mozilla.org/pipermail/rust-dev/2013-November/00...

Half a year ago: "I wouldn't be surprised to see Rust at least abandon M:N soon though once they start really optimizing performance." With Rust I'm sure they'll drop M:N, if not now then eventually, like essentially everybody else in CS history . It seems great on a superficial level, but when you really start caring about performance and latency and fairness it's the pits. My question is when Go will drop it. Imagi…

Go is moving to contiguous stacks in 1.3.
Post reply on HN