release-2-2-0 Release 2.2.0 (2019/06/15) Download release-2-2-0_1release-2-2-0_download New Features release-2-2-0_1release-2-2-0_new_features Breaks and Deprecated Features release-2-2-0_1release-2-2-0_breaks_and_deprecated_features Cpp-Taskflow 2.2.0 is the 3rd release in the 2.x line! This release includes several new changes such as tf::ExecutorObserverInterface, tf::Executor, isolation of taskflow graph and executor, benchmarks, and so forth. In particular, this release improve the performance of the work stealing scheduler. Download Cpp-Taskflow 2.2.0 can be downloaded from here. New Features A new executor class to isolate the execution module from a taskflow A new observer interface to inspect the activities of an executor A decomposable taskflow construction interface A new work-stealing algorithm to improve the performance Breaks and Deprecated Features In this release, we isolated the executor interface from tf::Taskflow, and merge tf::Framework with tf::Taskflow. This change largely improved the modularity and composability of Cpp-Taskflow in creating clean task dependency graphs and execution flows. Performance is also better. While this introduced some breaks in tf::Taskflow, we have managed to make it as less painful as possible for users to adapt to the new change. Previously, tf::Taskflow is a hero class that manages both a task dependency graph and the execution of all graphs including frameworks. For example: //beforev2.2.0,tf::Taskflowmanagesbothgraphandexecution tf::Taskflowtaskflow(4);//createataskflowobjectwith4threads taskflow.emplace([](){std::cout<<"taskA\n";}); taskflow.wait_for_all();//dispatchthepresentgraph tf::Frameworkframework;//createaframeworkobject framework.emplace([](){std::cout<<"taskB\n";}); taskflow.run(framework);//runtheframeworkonce taskflow.wait_for_all();//waituntiltheframeworkfinishes However, this design is awkward in many aspects. For instance, calling wait_for_all dispatches the present graph and the graph vanishes when the execution completes. To reuse a graph, users have to create another special graph called framework and mix its execution with the one in a taskflow object. Given the user feedback and lessons we have learned so far, we decided to isolate the executor interface out of tf::Taskflow and merge tf::Framework with tf::Taskflow. All execution methods such as dispatch and wait_for_all have been moved from tf::Taskflow to tf::Executor. //startingfromv2.2.0,tf::Executormanagestheexecutionofgraphs tf::Taskflowtaskflow;//createataskflowtobuilddependenttasks tf::TaskA=taskflow.emplace([](){std::cout<<"taskA\n";}); tf::TaskB=taskflow.emplace([](){std::cout<<"taskB\n";}); A.precede(B); tf::Executorexecutor(4);//createanexecutorof4threads executor.run(taskflow);//runthetaskflowonce executor.run(taskflow,2);//runthetaskflowtwice executor.wait_for_all();//waitforthethreerunstofinish The new design has a clean separation between a task dependency graph builder tf::Taskflow and the execution of graphs tf::Executor. Users are fully responsible for the lifetime of a taskflow, and need to ensure a taskflow is alive during its execution. Besides, all task constructs remain unchanged in tf::Taskflow. In most situations, you will just need to add an executor to your program to run your taskflow graphs. Again, we apologize this breaking change! I hope you can understand what we did is to make Cpp-Taskflow provide good performance scaling and user experience.