
A hybrid thread / fiber task scheduler written in C++ 11 - headlessclayton
https://github.com/google/marl
======
m0zg
Every time someone writes something like this, I just wish Google open sourced
its Fibers - that thing is sublime. Unfortunately that's not going to happen
as the niceties that implementation affords require a modified kernel. I think
I saw a presentation describing Fibers a few years back by one of its authors,
but it's darn near impossible to find because there's also "Google Fiber". If
someone has a link, please share. Fibers sort of opened up my mind to what's
possible in this space if you go deeper than people usually go. They're also a
source of dissatisfaction with how this turns out when people don't go as deep
as the authors of Fibers did.

[https://github.com/abseil/abseil-
cpp/issues/5](https://github.com/abseil/abseil-cpp/issues/5)

~~~
nwlieb
Could you describe what makes the Google Fibers so nice?

I'm also really curious why they require modifications to the Linux kernel. My
first guess would be stronger integration with the IO model at the syscall
boundary (similar to io_uring).

Edit: is this the talk your referring to?
[https://www.youtube.com/watch?v=KXuZi9aeGTw](https://www.youtube.com/watch?v=KXuZi9aeGTw)

~~~
Blackthorn
You know how the first time you learned about tcp sockets you made a server
that spawned a new thread to handle an incoming connection (or maybe not,
people learn differently nowadays).

With the fibers implementation you can just do that. It doesn't kill your
performance, and you don't need to go to a painful async model just for
performance reasons.

~~~
m0zg
Pretty much. You get to pretend inside your fibers that you're actually
running threads. IIRC (it's been a while) you also get proper stack trace when
something barfs, the importance of which cannot be overstated.

~~~
ninkendo
What _are_ they though? Is this a library for an existing language? A runtime
scheduler like the one that does goroutines in Go? If it were open sourced,
how would I use it?

~~~
thedance
It’s just a library that allows easier development of C++ servers in the
synchronous, thread-per-requests style, similar to working in Go but a
bajillion times better because it’s not in Go.

------
aninteger
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?

~~~
headlessclayton
Author 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
It'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
> 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!

------
aidos
I’d not heard the term fiber before react reworked their framework around it.
Is it a more general thing, and is there a good resource to learn about the
theory?

~~~
htfy96
Personally I recommend Distinguishing coroutines and fibers ([http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2014/n402...](http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2014/n4024.pdf))

In short:

\- The key difference between fibers and kernel threads is that fibers use
cooperative context switching

\- When a coroutine yields, it passes control directly to its caller (or, in
the case of symmetric coroutines, a designated other coroutine).

\- When a fiber blocks, it implicitly passes control to the fiber scheduler.
Coroutines have no scheduler because they need no scheduler

~~~
Waterluvian
Is that kind of like how gevent for Python works? I thought they were
coroutines but there's a scheduler, I think.

~~~
icebraining
Fibers can be built (and according to the paper, in C++ Boost they are) on top
of coroutines, and that's essentially what both gevent and asyncio do.

------
cheez
I don't particularly like the name defer. It seems to operate similarly to
Boost ScopeExit but the name does not imply that.

~~~
diath
The name is likely inspired by Go's defer statement which works in a similar
fashion: [https://blog.golang.org/defer-panic-and-
recover](https://blog.golang.org/defer-panic-and-recover)

~~~
cheez
It is, but we are in C++

------
banachtarski
C++11 is so 2011...

