// 2018/09/19 - created by Tsung-Wei Huang // // This program is used to benchmark the taskflow under different types // of workloads. #include #include #include #include // Procedure: benchmark #define BENCHMARK(TITLE, F) \ std::cout << "========== " << TITLE << " ==========\n"; \ \ std::cout << "Taskflow elapsed time: " \ << F() << " ms\n"; \ // ============================================================================ // Dynamic Stem // ============================================================================ // Function: dynamic_stem template auto dynamic_stem() { auto beg = std::chrono::high_resolution_clock::now(); { const int L = 1024; std::atomic sum {0}; T tf; std::optional prev; for(int l=0; l p; for(int k=0; kprecede(c); } p = c; } if(l & 1) { subflow.detach(); } }); if(prev) { prev->precede(curr); } prev = curr; } tf.wait_for_all(); assert(sum == L*(L+1)); } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - beg).count(); } // ============================================================================ // Map-Reduce // ============================================================================ // Function: map_reduce template auto map_reduce() { auto beg = std::chrono::high_resolution_clock::now(); { const int num_batches = 65536; std::vector C(1024, 10); std::atomic sum {0}; T tf; std::optional prev; for(int i=0; iprecede(s); } prev = t; } tf.wait_for_all(); assert(sum == num_batches * C.size() * 10); } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - beg).count(); } // ============================================================================ // Level Graph // ============================================================================ // Function: level_graph template auto level_graph() { const int num_levels = 2048; const int num_nodes_per_level = 1024; auto beg = std::chrono::high_resolution_clock::now(); { std::atomic sum {0}; T tf; std::vector< std::vector > tasks; tasks.resize(num_levels); for(int l=0; l(end - beg).count(); } // ============================================================================ // Linear Graph // ============================================================================ // Function: linear_graph template auto linear_graph() { const int num_nodes = 1000000; auto beg = std::chrono::high_resolution_clock::now(); { size_t sum {0}; T tf; std::vector tasks; for(int i=0; i(end - beg).count(); } // ============================================================================ // Binary Tree // ============================================================================ // Function: binary_tree template auto binary_tree() { const int num_levels = 21; auto beg = std::chrono::high_resolution_clock::now(); { T tf; std::atomic sum {0}; std::function insert; insert = [&] (int l, tf::Task parent) { if(l < num_levels) { auto lc = tf.silent_emplace([&] () { sum.fetch_add(1, std::memory_order_relaxed); }); auto rc = tf.silent_emplace([&] () { sum.fetch_add(1, std::memory_order_relaxed); }); parent.precede(lc); parent.precede(rc); insert(l+1, lc); insert(l+1, rc); } }; auto root = tf.silent_emplace([&] () { sum.fetch_add(1, std::memory_order_relaxed); }); insert(1, root); // synchronize until all tasks finish tf.wait_for_all(); assert(sum == (1 << (num_levels)) - 1); } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - beg).count(); } // ============================================================================ // Empty Jobs // ============================================================================ // Function: empty_jobs template auto empty_jobs() { const int num_tasks = 1000000; auto beg = std::chrono::high_resolution_clock::now(); { T tf; for(size_t i=0; i(end - beg).count(); } // ============================================================================ // Atomic add // ============================================================================ // Function: atomic_add template auto atomic_add() { const int num_tasks = 1000000; auto beg = std::chrono::high_resolution_clock::now(); { std::atomic counter(0); T tf; for(size_t i=0; i(end - beg).count(); } // ============================================================================ // Multiple Dispatches // ============================================================================ // Function: multiple_dispatches template auto multiple_dispatches() { auto create_graph = [] (T& tf, size_t N, std::atomic& c) { for(size_t i=0; i counter(0); create_graph(tf, p, counter); tf.wait_for_all(); assert(counter == p * 4); } } auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(end - beg).count(); } // ---------------------------------------------------------------------------- // Function: main int main(int argc, char* argv[]) { BENCHMARK("Multiple Dispatches", multiple_dispatches); BENCHMARK("Empty Jobs", empty_jobs); BENCHMARK("Atomic Add", atomic_add); BENCHMARK("Binary Tree", binary_tree); BENCHMARK("Linear Graph", linear_graph); BENCHMARK("Level Graph", level_graph); BENCHMARK("Map Reduce", map_reduce); BENCHMARK("Dynamic Stem", dynamic_stem); return 0; }