6 ms·
What advantages does this have over say, just using pthreads and the Linux kernel to handle the scheduling? I guess you can hit higher performance by not making
by aninteger 7y ago
What advantages does this have over say, just using pthreads and the Linux kernel to handle the scheduling? I guess you can hit higher performance by not making extra system calls, right? What other benefits would there be?
- api 7y agoMostly that and a lack of context switches. Async programming is really a fine grained hand written way to do fibers. Goroutines are basically fibers too and are incredibly cheap.
- headlessclayton 7y agoAuthor here. Marl was originally written for SwiftShader¹ a pure-software implementation of the Vulkan graphics API, which needs to run on desktop and embedded devices, with CPUs ranging from 2 to many cores. SwiftShader executes a number of parallel rasterization tasks which have complex blocking dependencies on one another. Marl attempts to simplify the problem of running and synchronizing tasks. It shamelessly borrows quite a few concepts from golang, simplifying fan-out, fan-in style problems. The main reason of why not just use pthreads / std::thread really comes down to blocking tasks. Using regular threads, you either need to know your dependency graph ahead of time to ensure tasks are executed in dependency order (blockless), or you likely end up spinning up many more threads than you actually need to allow things to block and wait on others to complete. SwiftShader has tight requirements on the number of threads we're allowed to create, so Marl was our solution. Marl is even capable of running single-threaded with no code changes to the tasks. It's worth mentioning we also evaluated other scheduler solutions using completion callbacks, but "callback hell" was something we were keen to avoid. I'm still looking into Marl optimizations, but it is already pretty fast. Fiber switching is notably faster than OS context switching for many of the benchmarks we've done. ¹ https://github.com/google/swiftshader https://github.com/google/swiftshader
- vkaku 7y agoIt's a lot of things (like tracing, defer etc), but for all practical purposes, the code is simple and straightforward to use. Thank you author. I wish you could create a version based setJmp/longJmp if instrinsics weren't available (say on a different processor, like AVR). That could really help!
- headlessclayton 7y ago> I wish you could create a version based setJmp/longJmp if instrinsics weren't available (say on a different processor, like AVR). That could really help! There is an implementation that uses ucontext, which you can enable with defining MARL_FIBERS_USE_UCONTEXT. That said, ucontext is a bit broken on macOS, and I haven't attempted to maintain this codepath, so may be removed in the near future. Assuming you know a bit of assembly for your platform and the ABI (specifically caller vs callee saved registers), adding new platforms is not too difficult. For example: ppc64 and MIPS64 support were both added by external contributors. We're always open to contributions, so if you write a nice generic implementation using setJmp/longJmp, I'm sure it would be accepted! Cheers!
- spc476 7y agoWith cooperative multitasking (coroutines) you can get by with saving less CPU state. I do this for my C coroutines package [1] for x86 and x86-64 by only saving the registers required by the calling convention. [1] https://github.com/spc476/C-Coroutines https://github.com/spc476/C-Coroutines
- headlessclayton 7y agoMarl does exactly this. The scheduler guarantees that once a task is started on a particular thread, it will only resume on that same thread. This allows us to save a bare minimum of registers and state. Some more info here: https://github.com/google/marl/blob/cbef55d588bc28661bedb8220531758f7a44fc0e/src/osfiber_asm.h#L15-L23 https://github.com/google/marl/blob/cbef55d588bc28661bedb822...