| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ce32fbb commit 1a1a346
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -610,7 +610,7 @@ std::cout << "all topologies complete" << '\n'; | |||
| 610 | 610 | ||
| 611 | 611 | ## Task API | |
| 612 | 612 | ||
| 613 | - Each `tf::Taskflow::Task` object is a lightweight handle for you to create dependencies in its associated graph. | ||
| 613 | + Each task object is a lightweight handle for you to create dependencies in its associated graph. | ||
| 614 | 614 | The table below summarizes its methods. | |
| 615 | 615 | ||
| 616 | 616 | | Method | Argument | Return | Description | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,18 +3,24 @@ | |||
| 3 | 3 | This page summarizes a list of frequently asked questions about Cpp-Taskflow. | |
| 4 | 4 | If you cannot find a solution here, please post an issue [here][Github issues]. | |
| 5 | 5 | ||
| 6 | + + [General Questions](#General-Questions) | ||
| 7 | + + [Compilation Issues](#Compilation-Issues) | ||
| 8 | + + [Programming Questions](#Programming-Questions) | ||
| 9 | + | ||
| 10 | + --- | ||
| 11 | + | ||
| 6 | 12 | ## General Questions | |
| 7 | 13 | ||
| 8 | - #### Q: How do I use Cpp-Taskflow in my projects? | ||
| 14 | + ### Q: How do I use Cpp-Taskflow in my projects? | ||
| 9 | 15 | ||
| 10 | - **A:** Cpp-Taskflow is a header-only library with zero dependencies. | ||
| 16 | + Cpp-Taskflow is a header-only library with zero dependencies. | ||
| 11 | 17 | The only thing you need is a [C++17][C++17] compiler. | |
| 12 | 18 | To use Cpp-Taskflow, simply drop the folder | |
| 13 | 19 | [taskflow](../taskflow) to your project and include [taskflow.hpp](../taskflow/taskflow.hpp). | |
| 14 | 20 | ||
| 15 | - #### Q: What is the difference between static tasking and dynamic tasking? | ||
| 21 | + ### Q: What is the difference between static tasking and dynamic tasking? | ||
| 16 | 22 | ||
| 17 | - **A:** Static tasking refers to those tasks created before execution, | ||
| 23 | + Static tasking refers to those tasks created before execution, | ||
| 18 | 24 | while dynamic tasking refers to those tasks created during the execution of static tasks | |
| 19 | 25 | or dynamic tasks (nested). | |
| 20 | 26 | Dynamic tasks created by the same task node are grouped together to a subflow. | |
@@ -24,37 +30,39 @@ Dynamic tasks created by the same task node are grouped together to a subflow. | |||
| 24 | 30 | |  |  | | |
| 25 | 31 | ||
| 26 | 32 | ||
| 27 | - #### Q: How many tasks can Cpp-Taskflow handle? | ||
| 33 | + ### Q: How many tasks can Cpp-Taskflow handle? | ||
| 28 | 34 | ||
| 29 | - **A:** Cpp-Taskflow is a very lightweight and efficient tasking library. | ||
| 35 | + Cpp-Taskflow is a very lightweight and efficient tasking library. | ||
| 30 | 36 | It has been applied in many academic and industry projects to scale up their existing workload. | |
| 31 | 37 | A research project [OpenTimer][OpenTimer] has used Cpp-Taskflow to deal with hundreds of millions of tasks. | |
| 32 | 38 | ||
| 33 | - #### Q: What are the differences between Cpp-Taskflow and other tasking libraries? | ||
| 39 | + ### Q: What are the differences between Cpp-Taskflow and other tasking libraries? | ||
| 34 | 40 | ||
| 35 | - **A:** From our humble opinion, Cpp-Taskflow is superior in its tasking API, interface, and performance. | ||
| 41 | + From our humble opinion, Cpp-Taskflow is superior in its tasking API, interface, and performance. | ||
| 36 | 42 | In most cases, users can quickly master Cpp-Taskflow to create large and complex dependency graphs | |
| 37 | 43 | in just a few minutes. | |
| 38 | 44 | The performance scales very well and is comparable to hard-coded multi-threading. | |
| 39 | 45 | Of course, the judge is always left for users -:) | |
| 40 | 46 | ||
| 41 | - #### Q: What is the weird hex value, like 0x7fc39d402ab0, in the dumped graph? | ||
| 47 | + ### Q: What is the weird hex value, like 0x7fc39d402ab0, in the dumped graph? | ||
| 42 | 48 | ||
| 43 | - **A:** Each task has a method `name(const std::string&)` for user to assign a human readable string | ||
| 49 | + Each task has a method `name(const std::string&)` for user to assign a human readable string | ||
| 44 | 50 | to ease the debugging process. | |
| 45 | 51 | If a task is not assigned a name or is an internal node, | |
| 46 | 52 | its address value in the memory is used instead. | |
| 47 | 53 | ||
| 48 | - ## Compile Issues | ||
| 54 | + --- | ||
| 49 | 55 | ||
| 50 | - #### Q: I can't get Cpp-Taskflow compiled in my project! | ||
| 56 | + ## Compilation Issues | ||
| 51 | 57 | ||
| 52 | - **A:** Please make sure your compile supports the latest version of [C++17][C++17]. | ||
| 58 | + ### Q: I can't get Cpp-Taskflow compiled in my project! | ||
| 59 | + | ||
| 60 | + Please make sure your compile supports the latest version of [C++17][C++17]. | ||
| 53 | 61 | Make sure your project meets the System Requirements described at [README][README]. | |
| 54 | 62 | ||
| 55 | - #### Q: Clang can't compile due to the use of std::variant. | ||
| 63 | + ### Q: Clang can't compile due to the use of std::variant. | ||
| 56 | 64 | ||
| 57 | - **A:** Cpp-Taskflow uses `std::variant` to enable uniform interface between static and dynamic tasking. | ||
| 65 | + Cpp-Taskflow uses `std::variant` to enable uniform interface between static and dynamic tasking. | ||
| 58 | 66 | However it has been reported in | |
| 59 | 67 | [Clang Bug 31852](https://bugs.llvm.org/show_bug.cgi?id=31852) that | |
| 60 | 68 | clang-6.0 (and before) does not correctly pick up the friend declaration with auto-deduced return type. | |
@@ -71,11 +79,13 @@ For clang users, you will need to use this patch in `taskflow.hpp` as follows: | |||
| 71 | 79 | #endif | |
| 72 | 80 | ``` | |
| 73 | 81 | ||
| 82 | + --- | ||
| 83 | + | ||
| 74 | 84 | ## Programming Questions | |
| 75 | 85 | ||
| 76 | - #### Q: What is the difference between Cpp-Taskflow threads and workers? | ||
| 86 | + ### Q: What is the difference between Cpp-Taskflow threads and workers? | ||
| 77 | 87 | ||
| 78 | - **A:** The master thread owns the thread pool and can spawn workers to run tasks | ||
| 88 | + The master thread owns the thread pool and can spawn workers to run tasks | ||
| 79 | 89 | or shutdown the pool. | |
| 80 | 90 | Giving taskflow `N` threads means using `N` threads to do the works, | |
| 81 | 91 | and there is a total of `N+1` threads (including the master threads) in the program. | |
@@ -86,39 +96,40 @@ tf::Taskflow(N); // N workers, N+1 threads in the program. | |||
| 86 | 96 | ||
| 87 | 97 | If there is no worker threads in the pool, the master thread will do all the works by itself. | |
| 88 | 98 | ||
| 89 | - #### Q: Is taskflow thread-safe? | ||
| 90 | - **A:** No, the taskflow object is not thread-safe. You can't create tasks from multiple threads | ||
| 99 | + ### Q: Is taskflow thread-safe? | ||
| 100 | + No, the taskflow object is not thread-safe. You can't create tasks from multiple threads | ||
| 91 | 101 | at the same time. | |
| 92 | 102 | ||
| 93 | - #### Q: My program hangs and never returns after dispatching a taskflow graph. What's wrong? | ||
| 103 | + ### Q: My program hangs and never returns after dispatching a taskflow graph. What's wrong? | ||
| 94 | 104 | ||
| 95 | - **A:** When the program hangs forever it is very likely your taskflow graph has a cycle. | ||
| 105 | + When the program hangs forever it is very likely your taskflow graph has a cycle. | ||
| 96 | 106 | Try the `dump` method to debug the graph before dispatching your taskflow graph. | |
| 97 | 107 | If there is no cycle, make sure you are using `future.get()` in the right way, | |
| 98 | 108 | i.e., not blocking your control flow. | |
| 99 | 109 | ||
| 100 | - #### Q: In the following example where B spawns a joined subflow of two tasks B1 and B2, do they run concurrently with task A? | ||
| 110 | + ### Q: In the following example where B spawns a joined subflow of two tasks B1 and B2, do they run concurrently with task A? | ||
| 101 | 111 | ||
| 102 | 112 | <p> | |
| 103 | 113 | <img src="../image/dynamic_graph.png" width="60%"> | |
| 104 | 114 | </p> | |
| 105 | 115 | ||
| 106 | - **A:** No. The subflow is spawned during the execution of B, and at this point A must finish | ||
| 116 | + No. The subflow is spawned during the execution of B, and at this point A must finish | ||
| 107 | 117 | because A precedes B. This gives rise to the fact B1 and B2 must run after A. | |
| 108 | 118 | This graph may looks strange because B seems to run twice! | |
| 109 | 119 | However, Cpp-Taskflow will schedule B only once to create its subflow. | |
| 110 | 120 | Whether this subflow joins or detaches from B only affects the future object returned from B. | |
| 111 | 121 | ||
| 112 | - #### Q: How can I parallelize multiple runs on the same function with different arguments? | ||
| 122 | + ### Q: How can I parallelize multiple runs on the same function with different arguments? | ||
| 123 | + | ||
| 124 | + Many people have been asking how to apply Taskflow's `parallel_for` method | ||
| 125 | + to parallelize a sequential loop over an index sequence. | ||
| 113 | 126 | ||
| 114 | 127 | ```cpp | |
| 115 | 128 | for(int i=0; i<N; ++i) { | |
| 116 | 129 | func(i); // each call to func is independent of each other | |
| 117 | 130 | } | |
| 118 | 131 | ``` | |
| 119 | 132 | ||
| 120 | - **A:** Many people have been asking how to apply Taskflow's `parallel_for` method | ||
| 121 | - to parallelize a sequential loop over an index sequence. | ||
| 122 | 133 | This can be done by using the capture property of a C++ lambda. | |
| 123 | 134 | ||
| 124 | 135 | ```cpp | |
@@ -131,6 +142,12 @@ for(int i=0; i<N; ++i) { | |||
| 131 | 142 | tf.wait_for_all(); | |
| 132 | 143 | ``` | |
| 133 | 144 | ||
| 145 | + ### Q: What is a Taskflow::Task? | ||
| 146 | + | ||
| 147 | + A `Taskflow::Task` is a lightweight handle associated with an internal graph node | ||
| 148 | + for users to build task dependencies. | ||
| 149 | + We do not allow users to explicitly access the internal node but using this wrapper | ||
| 150 | + to assign different attributes to a graph node. | ||
| 134 | 151 | ||
| 135 | 152 | * * * | |
| 136 | 153 | [Github issues]: https://github.com/cpp-taskflow/cpp-taskflow/issues | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - // 2018/09/18 - created by Tsung-Wei Huang | ||
| 1 | + // 2018/09/19 - created by Tsung-Wei Huang | ||
| 2 | 2 | // | |
| 3 | 3 | // This program is used to benchmark the taskflow under different types | |
| 4 | 4 | // of workloads. | |
@@ -29,6 +29,57 @@ using tf_privatized_t = tf::BasicTaskflow<std::function, tf::PrivatizedThreadpo | |||
| 29 | 29 | std::cout << "Taskflow [privatized + std::func] elapsed time: " \ | |
| 30 | 30 | << F<tf_privatized_t>() << " ms\n"; \ | |
| 31 | 31 | ||
| 32 | + // ============================================================================ | ||
| 33 | + // Dynamic Stem | ||
| 34 | + // ============================================================================ | ||
| 35 | + | ||
| 36 | + // Function: dynamic_stem | ||
| 37 | + template <typename T> | ||
| 38 | + auto dynamic_stem() { | ||
| 39 | + | ||
| 40 | + auto beg = std::chrono::high_resolution_clock::now(); | ||
| 41 | + | ||
| 42 | + const int L = 1024; | ||
| 43 | + | ||
| 44 | + std::atomic<size_t> sum {0}; | ||
| 45 | + | ||
| 46 | + T tf; | ||
| 47 | + | ||
| 48 | + std::optional<tf::Task> prev; | ||
| 49 | + | ||
| 50 | + for(int l=0; l<L; ++l) { | ||
| 51 | + auto curr = tf.silent_emplace([&, l] (auto& subflow) { | ||
| 52 | + sum.fetch_add(1, std::memory_order_relaxed); | ||
| 53 | + std::optional<tf::Task> p; | ||
| 54 | + for(int k=0; k<L; k++) { | ||
| 55 | + auto c = subflow.silent_emplace([&] () { | ||
| 56 | + sum.fetch_add(1, std::memory_order_relaxed); | ||
| 57 | + }); | ||
| 58 | + if(p) { | ||
| 59 | + p->precede(c); | ||
| 60 | + } | ||
| 61 | + p = c; | ||
| 62 | + } | ||
| 63 | + if(l & 1) { | ||
| 64 | + subflow.detach(); | ||
| 65 | + } | ||
| 66 | + }); | ||
| 67 | + | ||
| 68 | + if(prev) { | ||
| 69 | + prev->precede(curr); | ||
| 70 | + } | ||
| 71 | + prev = curr; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + tf.wait_for_all(); | ||
| 75 | + | ||
| 76 | + assert(sum == L*(L+1)); | ||
| 77 | + | ||
| 78 | + auto end = std::chrono::high_resolution_clock::now(); | ||
| 79 | + | ||
| 80 | + return std::chrono::duration_cast<std::chrono::milliseconds>(end - beg).count(); | ||
| 81 | + } | ||
| 82 | + | ||
| 32 | 83 | // ============================================================================ | |
| 33 | 84 | // Map-Reduce | |
| 34 | 85 | // ============================================================================ | |
@@ -46,7 +97,7 @@ auto map_reduce() { | |||
| 46 | 97 | ||
| 47 | 98 | T tf; | |
| 48 | 99 | ||
| 49 | - std::optional<typename T::TaskType> prev; | ||
| 100 | + std::optional<typename T::Task> prev; | ||
| 50 | 101 | ||
| 51 | 102 | for(int i=0; i<num_batches; ++i) { | |
| 52 | 103 | ||
@@ -87,7 +138,7 @@ auto level_graph() { | |||
| 87 | 138 | ||
| 88 | 139 | T tf; | |
| 89 | 140 | ||
| 90 | - std::vector< std::vector<typename T::TaskType> > tasks; | ||
| 141 | + std::vector< std::vector<typename T::Task> > tasks; | ||
| 91 | 142 | ||
| 92 | 143 | tasks.resize(num_levels); | |
| 93 | 144 | for(int l=0; l<num_levels; ++l) { | |
@@ -132,7 +183,7 @@ auto linear_graph() { | |||
| 132 | 183 | ||
| 133 | 184 | T tf; | |
| 134 | 185 | ||
| 135 | - std::vector<typename T::TaskType> tasks; | ||
| 186 | + std::vector<typename T::Task> tasks; | ||
| 136 | 187 | ||
| 137 | 188 | for(int i=0; i<num_nodes; ++i) { | |
| 138 | 189 | tasks.push_back(tf.silent_emplace([&] () { ++sum; })); | |
@@ -163,9 +214,9 @@ auto binary_tree() { | |||
| 163 | 214 | T tf; | |
| 164 | 215 | ||
| 165 | 216 | std::atomic<size_t> sum {0}; | |
| 166 | - std::function<void(int, typename T::TaskType)> insert; | ||
| 217 | + std::function<void(int, typename T::Task)> insert; | ||
| 167 | 218 | ||
| 168 | - insert = [&] (int l, typename T::TaskType parent) { | ||
| 219 | + insert = [&] (int l, typename T::Task parent) { | ||
| 169 | 220 | ||
| 170 | 221 | if(l < num_levels) { | |
| 171 | 222 | ||
@@ -257,13 +308,14 @@ auto atomic_add() { | |||
| 257 | 308 | ||
| 258 | 309 | // Function: main | |
| 259 | 310 | int main(int argc, char* argv[]) { | |
| 260 | - | ||
| 311 | + | ||
| 261 | 312 | BENCHMARK("Empty Jobs", empty_jobs); | |
| 262 | 313 | BENCHMARK("Atomic Add", atomic_add); | |
| 263 | 314 | BENCHMARK("Binary Tree", binary_tree); | |
| 264 | 315 | BENCHMARK("Linear Graph", linear_graph); | |
| 265 | 316 | BENCHMARK("Level Graph", level_graph); | |
| 266 | 317 | BENCHMARK("Map Reduce", map_reduce); | |
| 318 | + BENCHMARK("Dynamic Stem", dynamic_stem); | ||
| 267 | 319 | ||
| 268 | 320 | return 0; | |
| 269 | 321 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments