Live data from Hacker News

Zero-cost futures in Rust

aturon.github.io

291–300 of 348 posts

Re: Zero-cost futures in Rust

#291

Earlier quoted context omitted.

Well, they're two different points: closures in rust do not inherently have to heap allocate. But that also doesn't mean that they can _not_ be heap allocated either. And in this example, it's not even really the closures that allocate: it's still one allocation, regardless of the number of closures.

No. they are not two different points. You are artificially restricting the argument, and saying that some parts are a different question by introducing this new idea of "inherent nature" of a closure in rust : Some humans have legs, some do not. Does it mean that having legs is not an "inherent" part of the human experience? So to go back to the argument, we are trying to compare using a coroutine base approach vs u…

May I ask, where do you assume a coroutine's stack lives?

i.e. In relation to program memory

Re: Zero-cost futures in Rust

#292
post #287

Little benchmark rs-futures vs lwan ( https://lwan.ws ) on my machine Core i5 futures-minihttp(singlethread): $ wrk -c 100 -t 2 -d 20 http://127.0.0.1:8080/plaintext Running 20s test @ http://127.0.0.1:8080/plaintext 2 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 823.09us 449.37us 20.98ms 98.69% Req/Sec 62.15k 10.51k 105.24k 48.63% 2479035 requests in 20.10s, 340.44MB read Requests/sec: 12…

But for me, this difference is nothing, because on Rust we can write safe and fast close to metal code. I think Rust is the greatest modern system programming language.

Re: Zero-cost futures in Rust

#293

Earlier quoted context omitted.

Except when the IO doesn't go though the kernel in the first place of course.

What I/O doesn't go through the kernel?

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

#294

Earlier quoted context omitted.

> So wondering if Rust provides any ability to add that kind of an N:M threading approach. Perhaps via an extension, macro or some other mechanism. I don't want M:N threading as Go implements it. It's a big loss of performance for marginal benefit over futures. In particular the libmill approach was tried in Rust and the results were far worse than 1:1. However, assuming this takes off I would like to see async/await…

Are libmill and Go's approach the same? I.e. Holding concurrent state in multiple stack frames? What causes that to have degraded performance? Is this new approach different because instead of stack frames you just have dynamically allocated callback state?

libmill only uses a single kernel thread so it's a little bit simpler here than Go, but otherwise they should be comparable.

Regarding stack vs. dynamically allocated state (in a future) I'm not convinced what's better. Yes, the allocated stack most likely has an overhead. But at least the data will be stored there in linear fashion and accessing the state will be super cheap once the stack is loaded. In case of futures that hold dynamically allocated data the state might be spread over much more memory locations, so it might be slower to access. I however haven't any more scientific data on this.

In my real-world applications I'm getting about the same performance from a Go based and a boost asio (uses no futures but dynamically allocated callback closures for storing state) networking application. However the programming style is completely different.

Re: Zero-cost futures in Rust

#295

Why you didn't compare it to C++ or C ? If you want to compete with C/C++ it would be natural to compare those in benchmarks. Java and Go have GC. It's like comparing super car with street cars when you should compare it to other super cars.

Because they're benchmarking the implementation of async IO, and showing the results against libraries that folks use. This is not a common pattern in C/C++, so the libraries there are fewer and less people have used them.

But, if you want C++, check the data from the original benchmark which was linked: https://www.techempower.com/benchmarks/#section=data-r12&hw=... . The Java one is slightly faster than the C++ one (more like "just as fast", since the difference is tiny). And there are 7 more java libs (half of them with recognizable names) before the next one (which I've never heard of), which underlines my point about this being more common in Java than C/++.

The benchmark wasn't shown to prove that Rust is the fastest language in the universe. It was shown to prove that the Rust futures implementation is competitive with the others in use today, ones which people would recognize.

Re: Zero-cost futures in Rust

#296
post #212

Earlier quoted context omitted.

Couldn't you switch on timeout using some kind of CPU counter, e.g. RDTSC?

Then you're polling the system clock all the time, which is a large unnecessary throughput loss.

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

#297
post #106

I love the direction this is going, and the performance it achieves. Debugging promises/deferreds in other languages has given me nightmares, compare with erlang/golang debugging where you get a simple stacktrace. Does this provide some nice way of debugging complex future chains? Are there plans towards making it super easy to debug? Cheers!

Native promises in JavaScript suffered from debugging issues - but there are hooks that can help you with those - like unhandledRejecton and the like.

Debugging promises in JavaScript is very easy at the moment.

Re: Zero-cost futures in Rust

#298

I'm confused by .map(|row| { json::encode(row) }) .map(|val| some_new_value(val)) Over .map(json::encode) .map(some_new_value) Is the explicit extra layer of lambda generally prefered in Rust over just passing the functions?

It makes the blog post more approachable for users. I have seen the exlicit style being used over the point-free style many times before since it seems like it's easier for newbies to grasp - I haven't seen any evidence to support it though.

Re: Zero-cost futures in Rust

#299

Earlier quoted context omitted.

Defaults matter. If some people use async I/O and others don't then you get a mess when they want to share reusable libraries. It's similar to the mess you get when there is more than one string type. I think the "what color is your function" problem could be mostly solved by making async/await the default function type - that is, most callback functions should be allowed to suspend execution by waiting on a Future.…

> (Unfortunately, the default has to be the opposite in browser-based languages due to performance concerns.) Also in Rust. Most apps that aren't servers don't want async I/O, and it causes a lot of problems when you need high-performance FFI. For example, in Servo async-everywhere would be a big problem for WebRender, which needs to be able to call OpenGL extremely quickly. > Defaults matter. If some people use asyn…

> Given that having both is necessary for Rust (maybe a necessary evil), I think the right approach is to make jumping from one mode to the other painless. For sync-to-async, it needs to be easy to block on the result; for async-to-sync, it needs to be easy and fast to offload to a thread pool. If we can make it really easy to switch from one mode to the other, then most of the really hairy problems go away.

That really sounds like the Task type from C# TPL, which can also be used in sync and async environments and was probably also designed to be a generic solution. While it basically works there's a bigger number of pitfalls associated with that model. E.g. you can synchronously block on .Result from some tasks (that will be fulfilled by other threads), but not from others (which would be fulfilled by the same thread, because that causes a deadlock). In the Task+Multithreading world there's also always the question where continuations (attached with .ContinueWith, .then, ...) run. E.g. synchronously in the thread that fulfills the promise, asynchronously in a new eventloop iteration (but for which EventLoop?), in an explicitly specified scheduler, etc. C# uses TaskScheduler and SynchronizationContext variables for that. But as they are only partially known and even behave somewhat different for await Task and Task.ContinueWith there's quite some room for confusion.

Re: Zero-cost futures in Rust

#300

Earlier quoted context omitted.

> I'm not aware of anyone offering an alternative superior to an informal CSP yet which seems to be the reason why Go and Clojure have picked it as well for their concurrency model. What about Quasar [0] on the JVM? 0 - http://docs.paralleluniverse.co/quasar/

But Quasar does seem to offer go-like channels? I'm unclear what kind of combinators are provided but those could be implemented.

It does offer channels, see the linked Docu.

For combinators I don't think those are really needed in the CSP environments because you can do most transformations with normal function calls and normal control flow contructs (loops, if, ...)

Post reply on HN