Live data from Hacker News

Zig's New Async I/O

kristoff.it

191–200 of 293 posts

Re: Zig's New Async I/O

#191
post #160
post #120

Earlier quoted context omitted.

> If you are using a library in rust, it has to be async await, tokio, send+sync and all the other crap Send and sync is only required if you want to access something from multiple threads, which isn't required by async await (parallelism vs concurrency) 1) You can use async await without parallelism and 2) send and sync aren't a product of async/await in Rust, but generally memory safety, i.e. you need Send generall…

Yes, but async Rust is basically built on tokio's runtime, which is what most the big async libraries depend on, like hyper/axum/tokio etc. And tokio is a thread-per-core work-stealing architecture, which requires Send + Sync bounds everywhere. You can avoid them if you depend on tokio-proper, but it's more icky when building on something like axum, where your application handlers also require these bounds. A good ar…

Iirc I had a situation a while back, in which I used async await with tokio with a non Send or Sync type and it compiled when I didn't use spawn[1] (implying multithreading) but a simple loop with sequential processing.

Only when I wanted to enable parallelism using spawn, I got a compilation error.

[1]: https://docs.rs/tokio/latest/tokio/task/fn.spawn.html

Re: Zig's New Async I/O

#192

I think this design is a regression from the previous design, in which you could use compile time introspection to check whether things are actually async (calling convention) or not. Additionally, I don't necessarily want to delegate the management of the memory backing the futures to an Io, or pass around a blob of syscalls and an associated runtime, which accesses everything via a vtable. I would prefer to have th…

Your preference to have them be compile time generic shouldn’t come at the cost of those that would want runtime virtualisation.

As the article concludes, you get the best of both worlds here, where the result is effectively compile time generic if you only use one io implementation in your program. In theory it’d also partially compile time generic if you exclusively use one io for one set of libraries/functions and a different io for another set of libraries/functions.

I see this as the objectively correct design based on the existing design decisions in Zig. It follows from the allocator interface decision.

Re: Zig's New Async I/O

#193
post #3

I feel that I have to point this out once again, because the article goes so far as to state that: > With this last improvement Zig has completely defeated function coloring. I disagree with this. Let's look at the 5 rules referenced in the famous "What color is your function?" article referenced here. > 1. Every function has a color Well, you don't have async/sync/red/blue anymore, but you now have IO and non-IO fun…

You are skipping the massive point here. If you are using a library in rust, it has to be async await, tokio, send+sync and all the other crap. Or if it is sync api then it is useless for async application. This approach of passing IO removes this problem and this is THE main problem. This way you don’t have to use procedural macros or other bs to implement multi versioning for the functions in your library, which do…

This comment is justly flatly incorrect. You don't need Tokio at all to write an async library. Nor do you need send sync. Not sure what other crap you are speaking of, either.

Sync APIs can be spawned in worker threads as futures, too. Generally executors have helper methods for that.

Re: Zig's New Async I/O

#194
post #75

Earlier quoted context omitted.

I’m confused about the assertion that green threads perform badly. 3 of the top platforms for high concurrency servers use or plan to use green threads (Go, Erlang, Java). My understanding was that green threads have limitations with C FFI which is why lower level languages don’t use them (Rust). Rust may also have performance concerns since it has other constraints to deal with.

Green threads have issues with C FFI mostly due to not being able to preempt execution, when the C thing is doing something that blocks. This is a problem when you have one global pool of threads that execute everything. To get around it you essentially need to set up a dedicated thread pool to handle those c calls. Which may be fine - go doesn't let the user directly create thread pools directly but do create one un…

The problem is when C calls expect to be on a particular thread. Either the main event thread, in a GUI context, or the same thread as some related previous call.

Re: Zig's New Async I/O

#195
post #14
post #3

I feel that I have to point this out once again, because the article goes so far as to state that: > With this last improvement Zig has completely defeated function coloring. I disagree with this. Let's look at the 5 rules referenced in the famous "What color is your function?" article referenced here. > 1. Every function has a color Well, you don't have async/sync/red/blue anymore, but you now have IO and non-IO fun…

Go also suffers from this form of “subtle coloring”. If you’re working with goroutines, you would always pass in a context parameter to handle cancellation. Many library functions also require context, which poisons the rest of your functions. Technically, you don’t have to use context for a goroutine and could stub every dependency with context.Background, but that’s very discouraged.

I think the main point is in something like Go, the approach is non-viral. If you are 99 levels deep in synchronous code and need to call something with context, well, you can just create one. With C#, if you need to refactor all 99 levels above (or use bad practices which is of course what everyone does).

Also, in general cancellation is something that you want to optionally have with any asynchronous function so I don't think there really exists an ideal approach that doesn't include it. In my opinion the approach taken by Zig looks pretty good.

Re: Zig's New Async I/O

#196
post #118

Earlier quoted context omitted.

Because it allows multiple topologies of producers and consumers.

No idea what that means.. Do you have a concrete example of what CPS allows and async/await doesn't?

I believe async/await means you have a single consumer (caller) and a single producer (callee) and only a single value will be produced (resolved).

With CPS you may send and receive many times over to whomever and from whomever you like.

In JavaScript you may write..

    const fetchData = async () => {
        // snip
        return data;
    }

    const data = await fetchData();
And in Go you might express the same like..

    channel := make(chan int);
    
    go func() {
        // snip
        channel 
But you could also, for example, keep sending data and send it to as many consumers as you like..

    go func() {
        for { // Infinite loop:
            // snip
            channel1 

Re: Zig's New Async I/O

#198

As the author of a semi-famous post about how Zig has function colors [1], I decided to read up on this. I see that blocking I/O is an option: > The most basic implementation of `Io` is one that maps to blocking I/O operations. So far, so good, but blocking I/O is not async. There is a thread pool that uses blocking I/O. Still good so far, but blocking I/O is still not async. Then there's green threads: > This implem…

It’s hard to judge before stackless coroutines are reintroduced. But I think you’re entirely wrong that the next version of it will have colored functions, even according to your definition.

It has been mentioned that it’s possible that the default for debug builds is that every single function is compiled as an async function. I.e. there is canonically only one function color. Changing function color could become an optimisation for release builds. This is really not much different from inlining functions or other tricks the compiler can do with the calling convention if it has perfect knowledge of all callers.

> it appears to me that the Zig team decided to have every concurrency technique in the hope that it would appear innovative.

That’s a really bad take. It’s not much different from what they did to make allocators explicit. It’s an excellent approach for what Zig is supposed to be. Different concurrency models have different performance tradeoffs, just like with allocators. If the can support different IO models without making the language complicated, that’s a huge win, and they seem to be achieving that.

I find this approach the opposite of “appear innovative”. They’ve moved away from designing in a bunch of fancy syntax that locks users into one particular concurrency model, and gone for a more explicit and boring design which puts power in the hands of the user. It may not be right for everyone, but for what Zig is setting out to do it’s perfect. A disciplined decision in my opinion.

Getting stackless coroutines right for a low level language like Zig would be somewhat innovative. But not in a way that’s flashy or super interesting.

Re: Zig's New Async I/O

#199
post #150

Earlier quoted context omitted.

Right, the proposal doesn't discuss the implementation details -- I do apologise if that made it seem a little hand-wavey. I opted not to discuss them there, because they're similar-ish to the way we lowered stackless async in its stage1 implementation, and hence not massively interesting to discuss. The idea is that, yes, the compiler will infer whether or not a function is async (in the stackless async sense) based…

> a change in a leaf function in a little file in a random helper module could introduce asynchronocity which propagates all the way up to your `pub fn main` If this doesn't make the argument that Zig has certainly not defeated function coloring, I don't know what would. The fact that this change to how my program runs is hidden away in the compiler instead of somewhere visible is not an improvement.

> If this doesn't make the argument that Zig has certainly not defeated function coloring, I don't know what would.

if you're opting into stackless coroutines then yeah you're opting into their viral nature, but the point is that you don't have to. as the application author your dependencies won't opt you forcefully in using stackless coroutines (or any singular execution model), which is currently the case with other languages.

this is what it means to defeat function coloring.

Re: Zig's New Async I/O

#200

Earlier quoted context omitted.

Sure it is a function coloring. Just in a different form. `async` in other languages is something like an implicit parameter. In zig they made this implicit parameter explicit. Is that more better/more ergonomic? I don't know yet. The sugar is different, but the end result the same. Unless you can show me concrete example of things that the approach zig has taken can do that is not possible in say, rust. Than I don't…

> Unless you can show me concrete example add io to a struct and let the struct keep track of its own io.

You can also carry around a runtime and dispatch async function in non-async functions.
Post reply on HN