News for hackers. Stuff that matters.
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @04:09PM (#12271244)
> Being able to express your code in a sequential manor and still gain the performance offered by implicit fiber,goroutine,w/e scheduling is pretty awesome.

You don't gain as much performance. On Linux, you don't actually gain that much if anything over 1:1 threading. Most of the benefits of goroutines actually comes from the small stacks, which don't have anything to do with M:N and 1:1 to begin with—they're a feature of GC.

As the blog post states, our end goal is to achieve the ergonomics of Go-style M:N without sacrificing nginx levels of performance. With this futures library, we've established the foundations. It would make no sense to give up before even trying.

> And if I need anything better performing, I can still switch to C for specific use cases.

Why would you write networking code in C in 2016, when there are better alternatives available (like this one)?

Rust's philosophy is to have both performance and ergonomics. It rejects the idea that optimal performance requires a cumbersome programming model. I'm not about to give up on that.

Re:Zero-cost futures in Rust by gravypod on Friday August 12, 2016 @09:06AM (#12275259)
> Why would you write networking code in C in 2016, when there are better alternatives available (like this one)?

As someone who's done a bit of game development in the past I can easily say that you'll fall flat on your face in some fields if you don't write your own protocols. There are simply no protocols that operate well in every use case and there are also use-cases without standardized protocols.

For extremely high performance operations you NEED to use a custom protocol in some case to squeeze every bit of power out of your applications.

From a game development perspective you need to handle two types of content: content that isn't really ordered by time occurrence and content that needs to happen sequentially.

Because of this you can correctly optimize your networking stack for those needs and get your networking system really fast.

Some people have taken to using TCP with a JSON message passing system and that just doesn't work large scale. You'll have huge latency issues and with a big enough game you have to do some serious bit-packing to make it cost effective to host servers for.

Now I'm speaking generally and this is just from my experience but there is definitely still a reason to write your own network code.

Re:Zero-cost futures in Rust by kibwen on Friday August 12, 2016 @10:46AM (#12276092)
I believe pcwalton agrees that the ability to write one's own networking code is important. The quoted line is simply questioning why you'd write such code in C (which, if you're a game developer, means that "platform support" is a perfectly valid answer).
Re:Zero-cost futures in Rust by pcwalton on Friday August 12, 2016 @11:53AM (#12276615)
Sure, there are valid reasons to write your own network code. The question is whether you should write it in C. :)
Re:Zero-cost futures in Rust by derefr on Thursday August 11, 2016 @04:27PM (#12271332)
> On Linux, you don't actually gain that much if anything over 1:1 threading.

You use green threads instead of native threads because native threads have space overhead, not because they have time overhead. Attempting to spawn 100k OS threads will do strange things to most kernel scheduling algorithms; they're not optimized for that use-case.

Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @04:37PM (#12271400)
> You use green threads instead of native threads because native threads have space overhead, not because they have time overhead.

The main overhead of a thread, green or native, is the stack. The size of the stack is independent of whether you use native or green threads. Go's small stacks are actually made possible by its GC, not its choice of 1:1 or M:N. In musl, for example, you can have 2KB stacks [1] with 1:1.

I haven't seen a benchmark of huge numbers of native threads vs. a userland scheduler, but I have a hard time imagining that a userland scheduler will beat the kernel's scheduler. The kernel scheduler has a much more global picture of the system compared to userland.

[1]: https://github.com/rofl0r/musl/blob/d05aaedaabd4f5472c233dbb...

Re:Zero-cost futures in Rust by derefr on Thursday August 11, 2016 @04:46PM (#12271441)
> The kernel scheduler has a much more global picture of the system compared to userland.

In most of the comparisons I've seen (usually for Erlang), worst-case latency was the important factor, so interaction with the kernel scheduler was avoided as much as possible. In the Erlang runtime, you can pass a switch to cause the userland scheduler-threads to each get bound to a particular processor core, and to cause the kernel scheduler to avoid scheduling anything on those cores. Effectively, this partitions the processor into a set of cores the OS scheduler entirely manages, and a set of cores that the userland scheduler entirely manages.

Re:Zero-cost futures in Rust by eridius on Thursday August 11, 2016 @10:22PM (#12273061)
If the kernel scheduler completely hands control of the core over to the program, doesn't that mean you can only run one or two programs on the entire machine at once without running out of cores? Surely the kernel still schedules other threads on that core too.
Re:Zero-cost futures in Rust by derefr on Thursday August 11, 2016 @11:45PM (#12273284)
Yes, that's rather the point: we're talking about highly-multicore server machines (e.g. 16/32 cores, or perhaps far more) entirely dedicated to running your extremely-concurrent application. You want all but one or two of those cores just running the app and nothing else. You leave one or two cores for the "control plane" or "supervisor"—the OS—to schedule all the rest of its tasks on.

It's a lot like a machine running a hypervisor with a single VM on it set to consume 100% of available resources—but actually slightly more efficient than that, since a guest can intelligently pin allocate cores to itself and pin its scheduler-threads to them and then just stop thinking about the pinning, while a hypervisor-host is stuck constantly thinking about whether its vCPUs-to-pCPU mapping is currently optimal, with what's basically a black box consuming those vCPUs.

Re:Zero-cost futures in Rust by bluejekyll on Friday August 12, 2016 @11:53AM (#12276610)
Is this something Erlang supports, or is it something that you get merely because you've pinned threads to a specific core?

I know with cgroups in Linux you can pin processes to cores pretty easily. Just curious how Erlang makes this easier.

Re:Zero-cost futures in Rust by derefr on Friday August 12, 2016 @03:25PM (#12278127)
If you mean that in terms of "can you ask Erlang itself to do this for you", then yes: http://erlang.org/doc/man/erl.html#+sbt

If you mean that in terms of "does the Erlang runtime intelligently take advantage of the fact that its schedulers are pinned to cores to do things you don't get from plain OS-level pinning", I'm not sure.

I think it might, though. This is my impression from reading, a year or so back, the same docs I just linked; you can read them for yourself and form your own opinion if you think this sounds crazy:

It seems like ERTS (the Erlang runtime: BEAM VM + associated processes like epmd and heart) has a pool of "async IO" threads, separate from the regular scheduler threads, that just get blocking syscalls scheduled onto them. Erlang will, if-and-only-if it knows it has pinned schedulers, attempt to "pair" async IO threads with scheduler threads, so that Erlang processes that cause syscalls schedule those syscalls onto "their" async IO threads, and the completion events can go directly back to the scheduler-thread that should contain the Erlang process that wants to unblock in response to them.†

In the default case, if you don't tell ERTS any different, it'll assume you've got one (UMA) CPU with N cores, and will try to pin async IO threads to the same cores as their paired scheduler-threads. This has context-switching overhead, but not much, since 1. the async IO thread is mostly doing kernel select() polling and racing to sleep, and 2. the two threads are in a producer-consumer relationship, like a Unix pipeline, where both can progress independently without needing to synchronize.

If you want, though, you can further optimize by feeding ERTS a CPU map, describing how the cores in your machine are grouped into CPU packages, and how the CPU packages are further grouped into NUMA memory-access groups. ERTS will then attempt to schedule its async IO threads onto a separate core of the same CPU package, or if not possible, the same NUMA group* as the scheduler-thread, to decrease IPC memory-barrier flushing overhead. (The IPC message is still forced to dump from a given core's cache-lines into the CPU or to NUMA local memory, but it doesn't have to go all the way to main memory.)

ERTS will also, when fed a CPU map, penalize the choice in its scheduling algorithm to move an Erlang process to a different CPU package or NUMA group. (It will still do it, but only if it has no other choice.)

---

† This is in contrast to a runtime without "native" green-threads, like the JVM, where even if you've got an async IO pool, it just sees an opaque pool of runtime threads and sends its completion events to one at random, and then it's the job of a framework like Quasar to take time out of the job of each of its JVM runtime threads to catch those messages and route them to a scheduler running on one of said runtime threads.

The same is true of an HVM hypervisor: without both OS support (paravirtualization) plus core pinning inside each VM, a hardware interrupt will just "arrive at" the same pCPU that asked to be interrupted, even if the vCPU that was scheduled on that pCPU when it made the hypercall is now somewhere else. This is why SR-IOV is so important: it effectively gives VMs their own named channels for hardware to address messages to, so they don't get delayed by misdelivery.

Re:Zero-cost futures in Rust by DblPlusUngood on Thursday August 11, 2016 @05:12PM (#12271582)
The space overhead is significantly worse for native threading in the presence of many threads since each thread needs both a user and kernel stack.

For M:N, there are N kernel stacks.

Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @05:44PM (#12271801)
Fair enough, but kernel stacks are 8K. 10K user + kernel size is a far cry from the 2MB default pthread stack size people usually talk about when they talk about 1:1.

I don't know of any benchmark comparing Go vs. a 1:1 implementation with 2K pthread stack sizes, but I would be surprised if the performance difference is large at all.

Re:Zero-cost futures in Rust by JoshTriplett on Thursday August 11, 2016 @05:48PM (#12271828)
> Fair enough, but kernel stacks are 8K.

And getting even cheaper than that, with the effort to make kernel stacks use virtual memory. As I understand it, once kernel stacks use virtual memory, they'll start out at a single 4k page.

Re:Zero-cost futures in Rust by kibwen on Friday August 12, 2016 @10:48AM (#12276112)
Is there a writeup somewhere of the progress towards this, or a prospective timeline?
Re:Zero-cost futures in Rust by sciurus on Friday August 12, 2016 @02:25PM (#12277703)
Re:Zero-cost futures in Rust by ansible on Thursday August 11, 2016 @05:09PM (#12271551)
I haven't seen a benchmark of huge numbers of native threads vs. a userland scheduler, but I have a hard time imagining that a userland scheduler will beat the kernel's scheduler. The kernel scheduler has a much more global picture of the system compared to userland.

Doesn't using kernel threads imply lots of context switches? Doesn't that tend to be expensive in terms of time on modern architectures?

Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @05:31PM (#12271702)
> Doesn't using kernel threads imply lots of context switches?

No, it's actually fewer context switches. That's because each I/O completion event goes straight from the kernel to the code that was waiting on it (1 context switch), not from the kernel to the userland dispatcher to the code that was waiting on it (2 context switches).

Re:Zero-cost futures in Rust by wbl on Thursday August 11, 2016 @08:16PM (#12272683)
This is not quite right. The userland dispatcher to run next thread is not a context switch: the TLB doesn't need to get flushed for instance. Furthermore many userland threads can be woken at once, and don't incur context switches when they suspend, prompting the next runnable thread to run.

If you're willing to put in a lot of compiler work userland thread switching is a function call, literally. I don't 1:1 threads with small stack are going to compete with that.

Re:Zero-cost futures in Rust by naasking on Thursday August 11, 2016 @10:28PM (#12273075)
The real cost of a true "context switch" is the transition from user level to kernel level, which takes thousands of cycles. This cost isn't incurred on a userland context switch, so those costs aren't comparable.
Re:Zero-cost futures in Rust by kijiki on Thursday August 11, 2016 @11:15PM (#12273198)
Thousands of cycles for a SYSCALL/SYSRET on a reasonably modern Intel/AMD CPU? I think you should try to measure that.
Re:Zero-cost futures in Rust by DblPlusUngood on Friday August 12, 2016 @12:32PM (#12276923)
"Thousands of cycles" is definitely not true on modern CPUs. SYSENTER/SYSEXIT takes 150 cycles on my Xeon X3460.
Re:Zero-cost futures in Rust by derefr on Friday August 12, 2016 @04:33PM (#12278591)
Given the kind of "cloud"-backed startups most of HN are working on, I think the more practical measurement would be the overhead of a ring 0/3 separation in a VM, plus hypercalls, plus a ring 0/3 separation in the hypervisor (for paravirtualized syscalls); averaged against a ring 0/3 separation in a VM, plus SR-IOV virtualization (for HVM-backed syscalls.)

Yeah, this doesn't apply for context-switches that happen because of plain old pre-emption, but it does happen if the context-switch is because e.g. a network packet wants to arrive at another process in your VM than the one that's currently running.

I would imagine that there's a reason bare-metal IaaS providers have a business model. :)

Re:Zero-cost futures in Rust by naasking on Friday August 12, 2016 @02:02PM (#12277549)
My knowledge of current timings are admittedly a little out of date since my microkernel days are behind me. Even the 150 cycles another poster suggested is still a significant cost over a userland context switch.
Re:Zero-cost futures in Rust by anonymousDan on Thursday August 11, 2016 @06:05PM (#12271953)
What if you have async system calls (e.g. FlexSC)?
Re:Zero-cost futures in Rust by openasocket on Thursday August 11, 2016 @05:34PM (#12271723)
Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @05:38PM (#12271748)
> Well, one advantage the Go runtime has is the green threads are cooperatively scheduled, so switches are a lot more lightweight.

Switches occur on timeouts (which involve a round trip through the kernel), on I/O events (which also involve a round trip through the kernel), or on goroutine message passing. So in every case except goroutine message sends, the switches don't actually save you a trip through the kernel.

Re:Zero-cost futures in Rust by saynsedit on Thursday August 11, 2016 @08:00PM (#12272620)
System call context switching is cheaper than and different from thread/scheduler context switching.
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @08:23PM (#12272713)
OK, but that's irrelevant in this context because Go has to context switch either way.
Re:Zero-cost futures in Rust by zeeboo on Friday August 12, 2016 @12:00AM (#12273320)
It can also switch on memory allocations and function calls as well. I'd expect a large fraction of the context switches come from those events and don't go through the kernel.
Re:Zero-cost futures in Rust by gpderetta on Thursday August 11, 2016 @05:44PM (#12271793)
Except when the IO doesn't go though the kernel in the first place of course.
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @05:48PM (#12271832)
What I/O doesn't go through the kernel?
Re:Zero-cost futures in Rust by Matthias247 on Friday August 12, 2016 @05:53AM (#12274355)
For I/O you can buffer inside the application whether the respective file is readable/writeable or not. If it's not readable you can directly switch to another goroutine without invoking a syscall. The scheduler will later get notified by select/epoll that the file is readable/writeable, adjust the buffered status and reschedule the goroutine.

But ok, for reading you might often have a buffered state of readable while in reality it's not and you only get this information through EAGAIN on read, so you won't avoid the syscall there. For writing you most likely always would.

Re:Zero-cost futures in Rust by gpderetta on Thursday August 11, 2016 @06:11PM (#12271995)
For the example if you are using kernel bypass of the network stack, which is increasingly common in high throughput/low latency applications.
Re:Zero-cost futures in Rust by haimez on Thursday August 11, 2016 @10:03PM (#12273003)
If you're going to bypass the kernel network stack for performance, you're definitely not going to write the rest of your code in go. The garbage collector, goroutine scheduler, and conservative compile time optimizations will totally undermine the end goal.

If you're in that world, you're using C, C++, or That if you're feeling dangerous.

Re:Zero-cost futures in Rust by pjmlp on Friday August 12, 2016 @03:02AM (#12273793)
The JVM is used a lot in Fintech.

Of course it isn't the OpenJDK, rather specialized JVMs like Azul or they code using C style coding with the GC out of the out paths, having profiled the applications with tools like Java Mission Control.

These are the customers driving Oracles effort for value types, AOT compilation and JNI replacement.

Why not just use C and C++, one might ask?

In spite of all these tricks and required knowledge, salaries are lower than for C and C++ developers and overall project costs are anyway lower due to shorter development times for the other tools not available in C and C++.

Hence why Fintech nowadays is looking into languages like Pony, because they don't want to wait for Java 10, if possible.

So an area where Rust might eventually earn some friends.

Re:Zero-cost futures in Rust by kasey_junk on Thursday August 11, 2016 @11:01PM (#12273159)
I've done kernel bypass work on the JVM.
Re:Zero-cost futures in Rust by gpderetta on Friday August 12, 2016 @09:37AM (#12275486)
FWIW, I wasn't thinking bout go.
Re:Zero-cost futures in Rust by saosebastiao on Thursday August 11, 2016 @07:23PM (#12272419)
Is anybody doing kernel bypass networking in go?
Re:Zero-cost futures in Rust by tomp on Thursday August 11, 2016 @07:45PM (#12272532)
Couldn't you switch on timeout using some kind of CPU counter, e.g. RDTSC?
Re:Zero-cost futures in Rust by pcwalton on Thursday August 11, 2016 @08:37PM (#12272774)
Then you're polling the system clock all the time, which is a large unnecessary throughput loss.
Re:Zero-cost futures in Rust by tomp on Friday August 12, 2016 @06:13AM (#12274402)
But that's just one instruction, highly paralelisable (I assume), as opposed to going through the kernel... Anyways, I haven't measured it, it's just an idea I had!
Re:Zero-cost futures in Rust by lightcatcher on Thursday August 11, 2016 @05:31PM (#12271701)
> Why would you write networking code in C in 2016, when there are better alternatives available (like this one)?

Support for kernel bypass networking libraries like ibverbs, DPDK (has an old unmaintained Rust wrapper) and other IO kernel bypass libraries such as SPDK and IOAT.

If Rust supported these libraries, I'd much prefer the future based Rust code to a massive event loop in C.

Re:Zero-cost futures in Rust by benlwalker on Friday August 12, 2016 @03:05AM (#12273807)
I'm one of the authors of SPDK (which includes an NVMe driver and an IOAT driver). If the community wants to add rust bindings to those two components I'd be very supportive.
Re:Zero-cost futures in Rust by JoshTriplett on Thursday August 11, 2016 @05:46PM (#12271812)
The article mentioned that the authors would love to help make futures-based wrappers for C libraries. You might ask if they could help with wrappers for ibverbs, DPDK, SPDK, and IOAT.
Re:Zero-cost futures in Rust by bluejekyll on Thursday August 11, 2016 @06:21PM (#12272058)
It's all open-source, this is an area that if it matters this much to you, you could help support. The Rust community is hugely welcoming to people coming on and helping.