xref: /aosp_15_r20/external/swiftshader/third_party/marl/README.md (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker# Marl
2*03ce13f7SAndroid Build Coastguard Worker
3*03ce13f7SAndroid Build Coastguard WorkerMarl is a hybrid thread / fiber task scheduler written in C++ 11.
4*03ce13f7SAndroid Build Coastguard Worker
5*03ce13f7SAndroid Build Coastguard Worker## About
6*03ce13f7SAndroid Build Coastguard Worker
7*03ce13f7SAndroid Build Coastguard WorkerMarl is a C++ 11 library that provides a fluent interface for running tasks across a number of threads.
8*03ce13f7SAndroid Build Coastguard Worker
9*03ce13f7SAndroid Build Coastguard WorkerMarl uses a combination of fibers and threads to allow efficient execution of tasks that can block, while keeping a fixed number of hardware threads.
10*03ce13f7SAndroid Build Coastguard Worker
11*03ce13f7SAndroid Build Coastguard WorkerMarl supports Windows, macOS, Linux, FreeBSD, Fuchsia, Emscripten, Android and iOS (arm, aarch64, loongarch64, mips64, ppc64, rv64, x86 and x64).
12*03ce13f7SAndroid Build Coastguard Worker
13*03ce13f7SAndroid Build Coastguard WorkerMarl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests).
14*03ce13f7SAndroid Build Coastguard Worker
15*03ce13f7SAndroid Build Coastguard WorkerExample:
16*03ce13f7SAndroid Build Coastguard Worker
17*03ce13f7SAndroid Build Coastguard Worker```cpp
18*03ce13f7SAndroid Build Coastguard Worker#include "marl/defer.h"
19*03ce13f7SAndroid Build Coastguard Worker#include "marl/event.h"
20*03ce13f7SAndroid Build Coastguard Worker#include "marl/scheduler.h"
21*03ce13f7SAndroid Build Coastguard Worker#include "marl/waitgroup.h"
22*03ce13f7SAndroid Build Coastguard Worker
23*03ce13f7SAndroid Build Coastguard Worker#include <cstdio>
24*03ce13f7SAndroid Build Coastguard Worker
25*03ce13f7SAndroid Build Coastguard Workerint main() {
26*03ce13f7SAndroid Build Coastguard Worker  // Create a marl scheduler using all the logical processors available to the process.
27*03ce13f7SAndroid Build Coastguard Worker  // Bind this scheduler to the main thread so we can call marl::schedule()
28*03ce13f7SAndroid Build Coastguard Worker  marl::Scheduler scheduler(marl::Scheduler::Config::allCores());
29*03ce13f7SAndroid Build Coastguard Worker  scheduler.bind();
30*03ce13f7SAndroid Build Coastguard Worker  defer(scheduler.unbind());  // Automatically unbind before returning.
31*03ce13f7SAndroid Build Coastguard Worker
32*03ce13f7SAndroid Build Coastguard Worker  constexpr int numTasks = 10;
33*03ce13f7SAndroid Build Coastguard Worker
34*03ce13f7SAndroid Build Coastguard Worker  // Create an event that is manually reset.
35*03ce13f7SAndroid Build Coastguard Worker  marl::Event sayHello(marl::Event::Mode::Manual);
36*03ce13f7SAndroid Build Coastguard Worker
37*03ce13f7SAndroid Build Coastguard Worker  // Create a WaitGroup with an initial count of numTasks.
38*03ce13f7SAndroid Build Coastguard Worker  marl::WaitGroup saidHello(numTasks);
39*03ce13f7SAndroid Build Coastguard Worker
40*03ce13f7SAndroid Build Coastguard Worker  // Schedule some tasks to run asynchronously.
41*03ce13f7SAndroid Build Coastguard Worker  for (int i = 0; i < numTasks; i++) {
42*03ce13f7SAndroid Build Coastguard Worker    // Each task will run on one of the 4 worker threads.
43*03ce13f7SAndroid Build Coastguard Worker    marl::schedule([=] {  // All marl primitives are capture-by-value.
44*03ce13f7SAndroid Build Coastguard Worker      // Decrement the WaitGroup counter when the task has finished.
45*03ce13f7SAndroid Build Coastguard Worker      defer(saidHello.done());
46*03ce13f7SAndroid Build Coastguard Worker
47*03ce13f7SAndroid Build Coastguard Worker      printf("Task %d waiting to say hello...\n", i);
48*03ce13f7SAndroid Build Coastguard Worker
49*03ce13f7SAndroid Build Coastguard Worker      // Blocking in a task?
50*03ce13f7SAndroid Build Coastguard Worker      // The scheduler will find something else for this thread to do.
51*03ce13f7SAndroid Build Coastguard Worker      sayHello.wait();
52*03ce13f7SAndroid Build Coastguard Worker
53*03ce13f7SAndroid Build Coastguard Worker      printf("Hello from task %d!\n", i);
54*03ce13f7SAndroid Build Coastguard Worker    });
55*03ce13f7SAndroid Build Coastguard Worker  }
56*03ce13f7SAndroid Build Coastguard Worker
57*03ce13f7SAndroid Build Coastguard Worker  sayHello.signal();  // Unblock all the tasks.
58*03ce13f7SAndroid Build Coastguard Worker
59*03ce13f7SAndroid Build Coastguard Worker  saidHello.wait();  // Wait for all tasks to complete.
60*03ce13f7SAndroid Build Coastguard Worker
61*03ce13f7SAndroid Build Coastguard Worker  printf("All tasks said hello.\n");
62*03ce13f7SAndroid Build Coastguard Worker
63*03ce13f7SAndroid Build Coastguard Worker  // All tasks are guaranteed to complete before the scheduler is destructed.
64*03ce13f7SAndroid Build Coastguard Worker}
65*03ce13f7SAndroid Build Coastguard Worker```
66*03ce13f7SAndroid Build Coastguard Worker
67*03ce13f7SAndroid Build Coastguard Worker## Benchmarks
68*03ce13f7SAndroid Build Coastguard Worker
69*03ce13f7SAndroid Build Coastguard WorkerGraphs of several microbenchmarks can be found [here](https://google.github.io/marl/benchmarks).
70*03ce13f7SAndroid Build Coastguard Worker
71*03ce13f7SAndroid Build Coastguard Worker## Building
72*03ce13f7SAndroid Build Coastguard Worker
73*03ce13f7SAndroid Build Coastguard WorkerMarl contains many unit tests and examples that can be built using CMake.
74*03ce13f7SAndroid Build Coastguard Worker
75*03ce13f7SAndroid Build Coastguard WorkerUnit tests require fetching the `googletest` external project, which can be done by typing the following in your terminal:
76*03ce13f7SAndroid Build Coastguard Worker
77*03ce13f7SAndroid Build Coastguard Worker```bash
78*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl>
79*03ce13f7SAndroid Build Coastguard Workergit submodule update --init
80*03ce13f7SAndroid Build Coastguard Worker```
81*03ce13f7SAndroid Build Coastguard Worker
82*03ce13f7SAndroid Build Coastguard Worker### Linux and macOS
83*03ce13f7SAndroid Build Coastguard Worker
84*03ce13f7SAndroid Build Coastguard WorkerTo build the unit tests and examples, type the following in your terminal:
85*03ce13f7SAndroid Build Coastguard Worker
86*03ce13f7SAndroid Build Coastguard Worker```bash
87*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl>
88*03ce13f7SAndroid Build Coastguard Workermkdir build
89*03ce13f7SAndroid Build Coastguard Workercd build
90*03ce13f7SAndroid Build Coastguard Workercmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1
91*03ce13f7SAndroid Build Coastguard Workermake
92*03ce13f7SAndroid Build Coastguard Worker```
93*03ce13f7SAndroid Build Coastguard Worker
94*03ce13f7SAndroid Build Coastguard WorkerThe resulting binaries will be found in `<path-to-marl>/build`
95*03ce13f7SAndroid Build Coastguard Worker
96*03ce13f7SAndroid Build Coastguard Worker### Emscripten
97*03ce13f7SAndroid Build Coastguard Worker
98*03ce13f7SAndroid Build Coastguard Worker1. install and activate the emscripten sdk following [standard instructions for your platform](https://emscripten.org/docs/getting_started/downloads.html).
99*03ce13f7SAndroid Build Coastguard Worker2. build an example from the examples folder using emscripten, say `hello_task`.
100*03ce13f7SAndroid Build Coastguard Worker```bash
101*03ce13f7SAndroid Build Coastguard Workercd <path-to-marl>
102*03ce13f7SAndroid Build Coastguard Workermkdir build
103*03ce13f7SAndroid Build Coastguard Workercd build
104*03ce13f7SAndroid Build Coastguard Workeremcmake cmake .. -DMARL_BUILD_EXAMPLES=1
105*03ce13f7SAndroid Build Coastguard Workermake hello_task -j 8
106*03ce13f7SAndroid Build Coastguard Worker```
107*03ce13f7SAndroid Build Coastguard WorkerNOTE: you want to change the value of the linker flag `sPTHREAD_POOL_SIZE` that must be at least as large as the number of threads used by your application.
108*03ce13f7SAndroid Build Coastguard Worker3. Test the emscripten output.
109*03ce13f7SAndroid Build Coastguard WorkerYou can use the provided python script to create a local web server:
110*03ce13f7SAndroid Build Coastguard Worker```bash
111*03ce13f7SAndroid Build Coastguard Worker../run_webserver
112*03ce13f7SAndroid Build Coastguard Worker```
113*03ce13f7SAndroid Build Coastguard WorkerIn your browser, navigate to the example URL: [http://127.0.0.1:8080/hello_task.html](http://127.0.0.1:8080/hello_task.html).
114*03ce13f7SAndroid Build Coastguard WorkerVoilà - you should see the log output appear on the web page.
115*03ce13f7SAndroid Build Coastguard Worker
116*03ce13f7SAndroid Build Coastguard Worker### Installing Marl (vcpkg)
117*03ce13f7SAndroid Build Coastguard Worker
118*03ce13f7SAndroid Build Coastguard WorkerAlternatively, you can build and install Marl using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
119*03ce13f7SAndroid Build Coastguard Worker
120*03ce13f7SAndroid Build Coastguard Worker```bash or powershell
121*03ce13f7SAndroid Build Coastguard Workergit clone https://github.com/Microsoft/vcpkg.git
122*03ce13f7SAndroid Build Coastguard Workercd vcpkg
123*03ce13f7SAndroid Build Coastguard Worker./bootstrap-vcpkg.sh
124*03ce13f7SAndroid Build Coastguard Worker./vcpkg integrate install
125*03ce13f7SAndroid Build Coastguard Worker./vcpkg install marl
126*03ce13f7SAndroid Build Coastguard Worker```
127*03ce13f7SAndroid Build Coastguard Worker
128*03ce13f7SAndroid Build Coastguard WorkerThe Marl port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
129*03ce13f7SAndroid Build Coastguard Worker
130*03ce13f7SAndroid Build Coastguard Worker### Windows
131*03ce13f7SAndroid Build Coastguard Worker
132*03ce13f7SAndroid Build Coastguard WorkerMarl can be built using [Visual Studio 2019's CMake integration](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019).
133*03ce13f7SAndroid Build Coastguard Worker
134*03ce13f7SAndroid Build Coastguard Worker### Using Marl in your CMake project
135*03ce13f7SAndroid Build Coastguard Worker
136*03ce13f7SAndroid Build Coastguard WorkerYou can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file:
137*03ce13f7SAndroid Build Coastguard Worker
138*03ce13f7SAndroid Build Coastguard Worker```cmake
139*03ce13f7SAndroid Build Coastguard Workerset(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl"
140*03ce13f7SAndroid Build Coastguard Workeradd_subdirectory(${MARL_DIR})
141*03ce13f7SAndroid Build Coastguard Worker```
142*03ce13f7SAndroid Build Coastguard Worker
143*03ce13f7SAndroid Build Coastguard WorkerThis will define the `marl` library target, which you can pass to `target_link_libraries()`:
144*03ce13f7SAndroid Build Coastguard Worker
145*03ce13f7SAndroid Build Coastguard Worker```cmake
146*03ce13f7SAndroid Build Coastguard Workertarget_link_libraries(<target> marl) # replace <target> with the name of your project's target
147*03ce13f7SAndroid Build Coastguard Worker```
148*03ce13f7SAndroid Build Coastguard Worker
149*03ce13f7SAndroid Build Coastguard WorkerYou may also wish to specify your own paths to the third party libraries used by `marl`.
150*03ce13f7SAndroid Build Coastguard WorkerYou can do this by setting any of the following variables before the call to `add_subdirectory()`:
151*03ce13f7SAndroid Build Coastguard Worker
152*03ce13f7SAndroid Build Coastguard Worker```cmake
153*03ce13f7SAndroid Build Coastguard Workerset(MARL_THIRD_PARTY_DIR <third-party-root-directory>) # defaults to ${MARL_DIR}/third_party
154*03ce13f7SAndroid Build Coastguard Workerset(MARL_GOOGLETEST_DIR  <path-to-googletest>)         # defaults to ${MARL_THIRD_PARTY_DIR}/googletest
155*03ce13f7SAndroid Build Coastguard Workeradd_subdirectory(${MARL_DIR})
156*03ce13f7SAndroid Build Coastguard Worker```
157*03ce13f7SAndroid Build Coastguard Worker
158*03ce13f7SAndroid Build Coastguard Worker### Usage Recommendations
159*03ce13f7SAndroid Build Coastguard Worker
160*03ce13f7SAndroid Build Coastguard Worker#### Capture marl synchronization primitives by value
161*03ce13f7SAndroid Build Coastguard Worker
162*03ce13f7SAndroid Build Coastguard WorkerAll marl synchronization primitives aside from `marl::ConditionVariable` should be lambda-captured by **value**:
163*03ce13f7SAndroid Build Coastguard Worker
164*03ce13f7SAndroid Build Coastguard Worker```c++
165*03ce13f7SAndroid Build Coastguard Workermarl::Event event;
166*03ce13f7SAndroid Build Coastguard Workermarl::schedule([=]{ // [=] Good, [&] Bad.
167*03ce13f7SAndroid Build Coastguard Worker  event.signal();
168*03ce13f7SAndroid Build Coastguard Worker})
169*03ce13f7SAndroid Build Coastguard Worker```
170*03ce13f7SAndroid Build Coastguard Worker
171*03ce13f7SAndroid Build Coastguard WorkerInternally, these primitives hold a shared pointer to the primitive state. By capturing by value we avoid common issues where the primitive may be destructed before the last reference is used.
172*03ce13f7SAndroid Build Coastguard Worker
173*03ce13f7SAndroid Build Coastguard Worker#### Create one instance of `marl::Scheduler`, use it for the lifetime of the process
174*03ce13f7SAndroid Build Coastguard Worker
175*03ce13f7SAndroid Build Coastguard WorkerThe `marl::Scheduler` constructor can be expensive as it may spawn a number of hardware threads. \
176*03ce13f7SAndroid Build Coastguard WorkerDestructing the `marl::Scheduler` requires waiting on all tasks to complete.
177*03ce13f7SAndroid Build Coastguard Worker
178*03ce13f7SAndroid Build Coastguard WorkerMultiple `marl::Scheduler`s may fight each other for hardware thread utilization.
179*03ce13f7SAndroid Build Coastguard Worker
180*03ce13f7SAndroid Build Coastguard WorkerFor these reasons, it is recommended to create a single `marl::Scheduler` for the lifetime of your process.
181*03ce13f7SAndroid Build Coastguard Worker
182*03ce13f7SAndroid Build Coastguard WorkerFor example:
183*03ce13f7SAndroid Build Coastguard Worker
184*03ce13f7SAndroid Build Coastguard Worker```c++
185*03ce13f7SAndroid Build Coastguard Workerint main() {
186*03ce13f7SAndroid Build Coastguard Worker  marl::Scheduler scheduler(marl::Scheduler::Config::allCores());
187*03ce13f7SAndroid Build Coastguard Worker  scheduler.bind();
188*03ce13f7SAndroid Build Coastguard Worker  defer(scheduler.unbind());
189*03ce13f7SAndroid Build Coastguard Worker
190*03ce13f7SAndroid Build Coastguard Worker  return do_program_stuff();
191*03ce13f7SAndroid Build Coastguard Worker}
192*03ce13f7SAndroid Build Coastguard Worker```
193*03ce13f7SAndroid Build Coastguard Worker
194*03ce13f7SAndroid Build Coastguard Worker#### Bind the scheduler to externally created threads
195*03ce13f7SAndroid Build Coastguard Worker
196*03ce13f7SAndroid Build Coastguard WorkerIn order to call `marl::schedule()` the scheduler must be bound to the calling thread. Failure to bind the scheduler to the thread before calling `marl::schedule()` will result in undefined behavior.
197*03ce13f7SAndroid Build Coastguard Worker
198*03ce13f7SAndroid Build Coastguard Worker`marl::Scheduler` may be simultaneously bound to any number of threads, and the scheduler can be retrieved from a bound thread with `marl::Scheduler::get()`.
199*03ce13f7SAndroid Build Coastguard Worker
200*03ce13f7SAndroid Build Coastguard WorkerA typical way to pass the scheduler from one thread to another would be:
201*03ce13f7SAndroid Build Coastguard Worker
202*03ce13f7SAndroid Build Coastguard Worker```c++
203*03ce13f7SAndroid Build Coastguard Workerstd::thread spawn_new_thread() {
204*03ce13f7SAndroid Build Coastguard Worker  // Grab the scheduler from the currently running thread.
205*03ce13f7SAndroid Build Coastguard Worker  marl::Scheduler* scheduler = marl::Scheduler::get();
206*03ce13f7SAndroid Build Coastguard Worker
207*03ce13f7SAndroid Build Coastguard Worker  // Spawn the new thread.
208*03ce13f7SAndroid Build Coastguard Worker  return std::thread([=] {
209*03ce13f7SAndroid Build Coastguard Worker    // Bind the scheduler to the new thread.
210*03ce13f7SAndroid Build Coastguard Worker    scheduler->bind();
211*03ce13f7SAndroid Build Coastguard Worker    defer(scheduler->unbind());
212*03ce13f7SAndroid Build Coastguard Worker
213*03ce13f7SAndroid Build Coastguard Worker    // You can now safely call `marl::schedule()`
214*03ce13f7SAndroid Build Coastguard Worker    run_thread_logic();
215*03ce13f7SAndroid Build Coastguard Worker  });
216*03ce13f7SAndroid Build Coastguard Worker}
217*03ce13f7SAndroid Build Coastguard Worker
218*03ce13f7SAndroid Build Coastguard Worker```
219*03ce13f7SAndroid Build Coastguard Worker
220*03ce13f7SAndroid Build Coastguard WorkerAlways remember to unbind the scheduler before terminating the thread. Forgetting to unbind will result in the `marl::Scheduler` destructor blocking indefinitely.
221*03ce13f7SAndroid Build Coastguard Worker
222*03ce13f7SAndroid Build Coastguard Worker#### Don't use externally blocking calls in marl tasks
223*03ce13f7SAndroid Build Coastguard Worker
224*03ce13f7SAndroid Build Coastguard WorkerThe `marl::Scheduler` internally holds a number of worker threads which will execute the scheduled tasks. If a marl task becomes blocked on a marl synchronization primitive, marl can yield from the blocked task and continue execution of other scheduled tasks.
225*03ce13f7SAndroid Build Coastguard Worker
226*03ce13f7SAndroid Build Coastguard WorkerCalling a non-marl blocking function on a marl worker thread will prevent that worker thread from being able to switch to execute other tasks until the blocking function has returned. Examples of these non-marl blocking functions include: [`std::mutex::lock()`](https://en.cppreference.com/w/cpp/thread/mutex/lock), [`std::condition_variable::wait()`](https://en.cppreference.com/w/cpp/thread/condition_variable/wait), [`accept()`](http://man7.org/linux/man-pages/man2/accept.2.html).
227*03ce13f7SAndroid Build Coastguard Worker
228*03ce13f7SAndroid Build Coastguard WorkerShort blocking calls are acceptable, such as a mutex lock to access a data structure. However be careful that you do not use a marl blocking call with a `std::mutex` lock held - the marl task may yield with the lock held, and block other tasks from re-locking the mutex. This sort of situation may end up with a deadlock.
229*03ce13f7SAndroid Build Coastguard Worker
230*03ce13f7SAndroid Build Coastguard WorkerIf you need to make a blocking call from a marl worker thread, you may wish to use [`marl::blocking_call()`](https://github.com/google/marl/blob/main/include/marl/blockingcall.h), which will spawn a new thread for performing the call, allowing the marl worker to continue processing other scheduled tasks.
231*03ce13f7SAndroid Build Coastguard Worker
232*03ce13f7SAndroid Build Coastguard Worker---
233*03ce13f7SAndroid Build Coastguard Worker
234*03ce13f7SAndroid Build Coastguard WorkerNote: This is not an officially supported Google product
235