Live data from Hacker News

Async-std: an async port of the Rust standard library

async.rs

221–230 of 238 posts

Re: Async-std: an async port of the Rust standard library

#221

Earlier quoted context omitted.

We have some pretty vanilla file upload code that needs async. S3 latency is fairly high. If you're uploading a few tiny files per user per second, thread usage gets out of hand real fast. With a simulated load of ~20 users we were running over 1000 threads. Several posts in the chain say that 20k+ threads is "fine". Not unless you have a ton of cores. The memory and context switching overhead is gigantic. Eventually…

In what language?

Java

Re: Async-std: an async port of the Rust standard library

#222
post #208

Earlier quoted context omitted.

In Go you need to manually tell the runtime to spawn a goroutine with the `go` keyword, which also «litter» the code…

Except in practice, go keyword is used much more coarsly and sparingly because you can group all the block of function calls under one big go call. With async, every single function has to be flagged as beeing asynchronous and be called differently ( although maybe some modern languages have a way to group all the await calls ?)

That's funny how gophers can at the same time defend the «explicitness» of the if-based error handling, and be annoyed to have syntactic annotations for the yield points for coroutines (because that's exactly what `await` is, versus the yield points silently added by the go compiler everywhere so the runtime can perform its scheduling).

Re: Async-std: an async port of the Rust standard library

#223
post #189

Earlier quoted context omitted.

Many systems have been developed in systems enabled GC languages. C++11 introduced a GC API in the standard library, and one of the biggest C++ game engine does use GC in their engine objects, Unreal. C++ on Windows makes heavy use of reference counting (which is a GC algorithm from CS point of view), via COM/UWP. The biggest problem to overcome is religious, not technical.

>C++ on Windows makes heavy use of reference counting (which is a GC algorithm from CS point of view), via COM/UWP. Not sure if Ref counting is a good example here, as there is no runtime monitoring the object graph hierarchy and of course Rust it’s self uses ref counting in many situations.

Chapter 5 of "The Garbage Collection Handbook", one of the GC gospel books.

Reference Counting is a garbage collection implementation algorithm from CS point of view.

RC has plenty of runtime costs as well, cache invalidation, lock contention on reference counters, stop the world in complex data structures, possible stack overflows if destructors are incorrectly written, memory fragmentation.

Re: Async-std: an async port of the Rust standard library

#224
post #217

Earlier quoted context omitted.

In cooperative multitasking you can program when to give up control, not when it is regained. The regaining part is unpredictable. Which introduces a lot of non-determinism to deal with and overhead.

This is no different than async/await. At some point you await a scheduled primitive, it could be a timer, io readiness, an io completion... and yield to a scheduler. You don’t specify explicitly when you return. These are not tightly coupled coroutines. This is precisely what is going on in cooperative multitasking. I don’t see how this increases overhead to deal with either. Basically, coop multitasking and async/a…

Await is just syntactic sugar. You do not really await anything. What actually happens is an event handler gets called on an event, where it sets up more event handlers for more events and so on. This is the essence of asynchronous programming. There are no tasks, no yielding, practically no overhead and everything is deterministic (in relation to external events obviously) [1]. The only cooperative multitasking implementations that have the same amount of determinism are those implemented strictly on top of event loops and that lack yielding function, so they cannot really be called cooperative multitasking implementations, as they can't "cooperate". All actual implementations have yielding, do not get control deterministically (dealing with that non-determinism requires stuff like semaphores) and have relatively significant overhead.

[1] If implemented with care, not doing syscalls in the middle of async primitives and using fast nearly-O(1) algorithms for timers, etc. it can be incredibly fast. And of course Rust also gives enough room to mess up all that nice determinism.

Re: Async-std: an async port of the Rust standard library

#225

I must be dumb, because every time I dive into async/await, I feel like I reach an epiphany about how it works, and how to use it. Then a week later I read about it again and totally lost all understanding. What do I gain if I have code like this [0], which has a bunch of `.await?` in sequence? I know .await != join_thread(), but doesn't execution of the current scope of code halt while it waits for the future we are…

User space threads and corouting is pretty much the right abstraction for application code. Async can be useful when more control over the details of execution is needed.

Async/await and futures/promises (and before that, stuff like Java's Executor abstraction) are being added to a lot of languages because it is very difficult for even experienced developers to manage threads in a bug-free way.

I've seen a lot of people try to manage complex programs by working with threads directly. Whatever they come up with is very unlikely to be as correct and reliable as the abstractions provided by the language. Even when they get it right, programs written with those techniques are difficult to modify without introducing new bugs.

Manual management of threads is becoming like manual management of memory -- it is discouraged by newer language features and you should only do if you really need to.

Re: Async-std: an async port of the Rust standard library

#226
post #208

Earlier quoted context omitted.

Except in practice, go keyword is used much more coarsly and sparingly because you can group all the block of function calls under one big go call. With async, every single function has to be flagged as beeing asynchronous and be called differently ( although maybe some modern languages have a way to group all the await calls ?)

That's funny how gophers can at the same time defend the «explicitness» of the if-based error handling, and be annoyed to have syntactic annotations for the yield points for coroutines (because that's exactly what `await` is, versus the yield points silently added by the go compiler everywhere so the runtime can perform its scheduling).

not sure who you're refering to.. i certainly don't like many aspects of the go language. goroutines and the "go" keyword isn't one of them.

Re: Async-std: an async port of the Rust standard library

#227
post #9

Earlier quoted context omitted.

Are those really the only options? I'm trying to wrap my head around how using a fixed size thread pool for I/O automatically implies deadlocks but I just can't. Unless the threads block on completion until their results are consumed instead of just notifying and then taking the next task.. I can definitely imagine blocking happening while waiting for a worker to be available, though. Did you mean simply blocking ins…

N threads, with N readers waiting for a message that will only come if the N+1 reader (still in the queue) gets a message first.

Thank you for humoring me. I had to sleep on it, but I can see it now. Seems like it would require a really bad design or more likely bad actors (remotes leaving dead sockets open), but it would definitely be possible.

The same scenarios would lead to resource exhaustion if the thread pool wasn't bounded.

Re: Async-std: an async port of the Rust standard library

#228

Earlier quoted context omitted.

I'm sitting in a Rust talk at a conference right now, and the speaker has slides comparing the syntax to Ruby/TypeScript/Python. "You already basically know Rust" A lot of the fancier stuff is very different, but there's fairly close parallels to most of the basic syntax.

Basically everything Algol-descended has close parallels in the syntax. That falls apart as soon as you hit a turbofish. I'm not saying the language or even the syntax is bad, but I think it does earn its reputation for being a little hard to read.

Even the turbo fish is consistent with other languages’ syntax: :: for scopes, for type parameters. Regardless, you almost never need to write the turbofish, so I think it also falls under niche syntax.

Re: Async-std: an async port of the Rust standard library

#229

Earlier quoted context omitted.

Nobody is denying that async code is faster. But it’s not as dramatic as presented in the grand parent post. And IMHO the added code complexity is not worth the trouble.

> And IMHO the added code complexity is not worth the trouble. The thing is, this is just that - your opinion, generalized as The Truth. But engineering is about making the right trade-offs. Often threading will be fine, you'll win simplicity, and all is good. But sometimes you really need the performance, or your field is crowded and its a competitive advantage. Think large-scale infrastructure at AWS, central load-…

> The thing is, this is just that - your opinion, generalized as The Truth.

Heh? Where?

Re: Async-std: an async port of the Rust standard library

#230

Earlier quoted context omitted.

As an aside, Zig[0] recently merged a change to try and tackle the use case of [a]sync-agnostic functions: https://github.com/ziglang/zig/issues/1778 . The "What Color is Your Function" blog post appears to have been one of the inspirations behind the change. Some of Andy's (the language creator) recent videos go over it in detail: https://www.youtube.com/channel/UCUICU6mgcyGy61pojwuWyHA [0]: https://ziglang.org

That looks quite interesting! The idea is that you can set the global "io_mode" mode to blocking, mixed or evented and I/O functions will switch their implementation accordingly. The type of the function will then, if I got that right, propagate up the call stack and turn functions that touch it transparently into either normal or async/awaitable funtions. Nice way to avoid a bifurcation of the ecosystem into red/gre…

Please excuse my cynicism, but how can a global variable for switching between blocking and async be considered interesting?

I mean, don’t you know at compile-time whether you want something to be async or not? If so, it should be handled by the type system, not by mutating a variable at runtime.

Post reply on HN