4 ms·
“Async” / event loop systems will usually be more efficient because 1) they don’t have to store unnecessary processor state to memory between handling events an
by CyberRabbi 5y ago
“Async” / event loop systems will usually be more efficient because 1) they don’t have to store unnecessary processor state to memory between handling events and 2) they are associated with cooperative event handlers and the assumption of a cooperative system provides more opportunities for optimization.
- jlokier 5y agoIronically, it is not true that "async" (stackless) event loop systems don't store unnecessary processor state to memory. Those systems store the entire processing state in future object memory. It is the same state that threaded systems store. Stackful context switching systems (which includes some kinds of efficient threads - I would count Linux kernel internal threads among these) store that same state in the stack and context objects when context switching, so in principle store about the same amount of state. But some async, stackless systems do a bunch of extra work unwinding and restoring entire call stacks whenever an async task pauses in await, and therefore take more CPU time to context switch than stackful systems do.
- CyberRabbi 5y ago> It is the same state that threaded systems store. Nope. Synchronous task switching systems additionally have to store CPU register state. In the cooperative case you need to store the caller-saved registers, in the pre-emptive case you need to store all the registers. Async systems simply don’t have to do that extra work. > But some async, stackless systems do a bunch of extra work unwinding and restoring entire call stacks whenever an async task pauses in await Just the really bad ones. In any competent system a callback is stored ready to handle the event. No “unwinding” necessary.
- gpderetta 5y agoA cooperative synchronous task switching (i.e. fiber based) need only save the exact same information as an async based one (i.e. stackless coroutines): at a minimum a context pointer and an instruction pointer. Plus any live registers (which might be none). You only need to save caller saved registers fs your context switching routine uses the conventional ABI, but that's not a requirement.
- CyberRabbi 5y ago> You only need to save caller saved registers fs your context switching routine uses the conventional ABI, but that's not a requirement. If you request a task switch from C or any high level language that has the concept of caller-saved registers and the compiler has no knowledge of your task switching system (vast majority of cases) you will be forced to pay an extra cost. Is there a practical system in common use that is able to elide register-saves that you’re referring to? Or is your point essentially that you don’t have save caller-saved/live registers in the theoretical case that you have no caller-saved/live registers?
- gpderetta 5y agoThe assumption is that you control the compiler (we are comparing with async that outside of duff device hacks does require compiler help). But even without explicit compiler help, you can go a long way, say, with gcc extended inline asm.
- CyberRabbi 5y agoYou don’t need explicit compiler help. At least with C this can be done entirely with a library. A task_switch() call can conform to the standard C-ABI, requiring no compiler support, and do the switching (in assembly). Without duff’s device. This is for example how kernels written in C do their task switching. Likely the same can be said for Rust and nearly any language, since they all have ABIs to which can be conformed such that task_switch() looks like a normal function call.
- gpderetta 5y agoOh, I have written my own share of userspace C context switching libraries, I know all the gory the details :). For example see my minimalist [1] stackful coroutine library: the full context switching logic is three inline asm instructions (99% of the complexity in that code is to transparently support throwing exceptions across coroutine boundaries with no overhead in the happy path). You need compiler help for the custom calling convention support and possibly to optimize away the context switching overhead for stackful coroutines, which is something that compilers can already do for stackless coroutines. The duff device is just a way to simulate stackless coroutines (i.e. async/await or whateverer) in plain C, in a way that the compiler can still optimize quite well. [1] https://github.com/gpderetta/delimited/blob/master/delimited.hpp https://github.com/gpderetta/delimited/blob/master/delimited...
- otabdeveloper4 5y ago> ...and the assumption of a cooperative system provides more opportunities for optimization Also much more opportunities for blocking and starvation. Especially when people try to reinvent schedulers without understanding how they work in the first place.