Ben Clayton | e9ba0a9 | 2019-09-05 12:35:50 +0100 | [diff] [blame] | 1 | # Marl |
| 2 | |
| 3 | Marl is a hybrid thread / fiber task scheduler written in C++ 11. |
| 4 | |
| 5 | ## About |
| 6 | |
| 7 | Marl is a C++ 11 library that provides a fluent interface for running tasks across a number of threads. |
| 8 | |
| 9 | Marl 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 | |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 11 | Marl supports Windows, macOS, Linux, Fuchsia and Android (arm, aarch64, mips64, ppc64 (ELFv2), x86 and x64). |
Ben Clayton | e9ba0a9 | 2019-09-05 12:35:50 +0100 | [diff] [blame] | 12 | |
Ben Clayton | 7121ee6 | 2019-11-15 16:46:36 +0000 | [diff] [blame] | 13 | Marl has no dependencies on other libraries (with an exception on googletest for building the optional unit tests). |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 14 | |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 15 | Example: |
| 16 | |
| 17 | ```cpp |
| 18 | #include "marl/defer.h" |
| 19 | #include "marl/event.h" |
| 20 | #include "marl/scheduler.h" |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 21 | #include "marl/waitgroup.h" |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 22 | |
| 23 | #include <cstdio> |
| 24 | |
| 25 | int main() { |
| 26 | // Create a marl scheduler using the 4 hardware threads. |
| 27 | // Bind this scheduler to the main thread so we can call marl::schedule() |
| 28 | marl::Scheduler scheduler; |
| 29 | scheduler.bind(); |
| 30 | scheduler.setWorkerThreadCount(4); |
| 31 | defer(scheduler.unbind()); // Automatically unbind before returning. |
| 32 | |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 33 | constexpr int numTasks = 10; |
| 34 | |
| 35 | // Create an event that is manually reset. |
| 36 | marl::Event sayHellow(marl::Event::Mode::Manual); |
| 37 | |
| 38 | // Create a WaitGroup with an initial count of numTasks. |
| 39 | marl::WaitGroup saidHellow(numTasks); |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 40 | |
| 41 | // Schedule some tasks to run asynchronously. |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 42 | for (int i = 0; i < numTasks; i++) { |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 43 | // Each task will run on one of the 4 worker threads. |
| 44 | marl::schedule([=] { // All marl primitives are capture-by-value. |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 45 | // Decrement the WaitGroup counter when the task has finished. |
| 46 | defer(saidHellow.done()); |
| 47 | |
| 48 | printf("Task %d waiting to say hello...\n", i); |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 49 | |
| 50 | // Blocking in a task? |
| 51 | // The scheduler will find something else for this thread to do. |
| 52 | sayHellow.wait(); |
| 53 | |
| 54 | printf("Hello from task %d!\n", i); |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 55 | }); |
| 56 | } |
| 57 | |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 58 | sayHellow.signal(); // Unblock all the tasks. |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 59 | |
Ben Clayton | e90568e | 2020-02-13 17:34:05 +0000 | [diff] [blame] | 60 | saidHellow.wait(); // Wait for all tasks to complete. |
| 61 | |
| 62 | printf("All tasks said hello.\n"); |
| 63 | |
| 64 | // All tasks are guaranteed to complete before the scheduler is destructed. |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 65 | } |
| 66 | ``` |
| 67 | |
Ben Clayton | 569a9a4 | 2020-03-11 22:27:09 +0000 | [diff] [blame] | 68 | |
| 69 | ## Benchmarks |
| 70 | |
| 71 | Graphs of several microbenchmarks can be found [here](https://google.github.io/marl/benchmarks). |
| 72 | |
| 73 | |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 74 | ## Building |
| 75 | |
Ben Clayton | 7121ee6 | 2019-11-15 16:46:36 +0000 | [diff] [blame] | 76 | Marl contains many unit tests and examples that can be built using CMake. |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 77 | |
| 78 | Unit tests require fetching the `googletest` external project, which can be done by typing the following in your terminal: |
| 79 | |
| 80 | ```bash |
| 81 | cd <path-to-marl> |
| 82 | git submodule update --init |
| 83 | ``` |
| 84 | |
| 85 | ### Linux and macOS |
| 86 | |
| 87 | To build the unit tests and examples, type the following in your terminal: |
| 88 | |
| 89 | ```bash |
| 90 | cd <path-to-marl> |
| 91 | mkdir build |
| 92 | cd build |
| 93 | cmake .. -DMARL_BUILD_EXAMPLES=1 -DMARL_BUILD_TESTS=1 |
| 94 | make |
| 95 | ``` |
| 96 | |
| 97 | The resulting binaries will be found in `<path-to-marl>/build` |
| 98 | |
| 99 | ### Windows |
| 100 | |
Nicolas Capens | e901e9b | 2019-09-13 22:32:59 -0400 | [diff] [blame] | 101 | Marl 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). |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 102 | |
| 103 | ### Using Marl in your CMake project |
| 104 | |
| 105 | You can build and link Marl using `add_subdirectory()` in your project's `CMakeLists.txt` file: |
| 106 | ```cmake |
| 107 | set(MARL_DIR <path-to-marl>) # example <path-to-marl>: "${CMAKE_CURRENT_SOURCE_DIR}/third_party/marl" |
| 108 | add_subdirectory(${MARL_DIR}) |
| 109 | ``` |
| 110 | |
| 111 | This will define the `marl` library target, which you can pass to `target_link_libraries()`: |
| 112 | |
| 113 | ```cmake |
| 114 | target_link_libraries(<target> marl) # replace <target> with the name of your project's target |
| 115 | ``` |
| 116 | |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 117 | You may also wish to specify your own paths to the third party libraries used by `marl`. |
| 118 | You can do this by setting any of the following variables before the call to `add_subdirectory()`: |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 119 | |
| 120 | ```cmake |
Ben Clayton | b2bfc0d | 2020-02-09 19:20:01 +0000 | [diff] [blame] | 121 | set(MARL_THIRD_PARTY_DIR <third-party-root-directory>) # defaults to ${MARL_DIR}/third_party |
| 122 | set(MARL_GOOGLETEST_DIR <path-to-googletest>) # defaults to ${MARL_THIRD_PARTY_DIR}/googletest |
| 123 | add_subdirectory(${MARL_DIR}) |
Ben Clayton | fccbd75 | 2019-09-09 20:55:09 +0100 | [diff] [blame] | 124 | ``` |
Ben Clayton | 7121ee6 | 2019-11-15 16:46:36 +0000 | [diff] [blame] | 125 | |
| 126 | --- |
| 127 | |
| 128 | Note: This is not an officially supported Google product |