Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 18f4e69

Browse files
author
twhuang
committed
updated priority
1 parent df12a03 commit 18f4e69

File tree

17 files changed

+573
-130
lines changed

17 files changed

+573
-130
lines changed

3rd-party/tbb/cmake/TBBConfig.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ get_filename_component(_tbb_root "${_tbb_root}" PATH)
4848
foreach (_tbb_component ${TBB_FIND_COMPONENTS})
4949
set(TBB_${_tbb_component}_FOUND 0)
5050

51-
set(_tbb_release_lib "/home/xiongzc/taskflow/build/benchmarks/tbb_cmake_build/tbb_cmake_build_subdir_release/lib${_tbb_component}.so.2")
51+
set(_tbb_release_lib "/home/twhuang/Code/taskflow/build/benchmarks/tbb_cmake_build/tbb_cmake_build_subdir_release/lib${_tbb_component}.so.2")
5252

5353
if (NOT TBB_FIND_RELEASE_ONLY)
54-
set(_tbb_debug_lib "/home/xiongzc/taskflow/build/benchmarks/tbb_cmake_build/tbb_cmake_build_subdir_debug/lib${_tbb_component}_debug.so.2")
54+
set(_tbb_debug_lib "/home/twhuang/Code/taskflow/build/benchmarks/tbb_cmake_build/tbb_cmake_build_subdir_debug/lib${_tbb_component}_debug.so.2")
5555
endif()
5656

5757
if (EXISTS "${_tbb_release_lib}" OR EXISTS "${_tbb_debug_lib}")

doxygen/releases/release-3.5.0.dox

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,18 @@ To use %Taskflow v3.5.0, you need a compiler that supports C++17:
3838

3939
+ Added tf::WorkerInterface to allow changing properties of workers upon their creations
4040
+ Added tf::Executor::loop_until to allow looping a worker with a custom stop predicate
41-
+ Extended tf::Executor to take tf::WorkerInterface
41+
+ Extended tf::TaskQueue to include priority (tf::TaskPriority)
42+
+ Extended tf::Executor to include tf::WorkerInterface
4243

4344
@subsection release-3-5-0_cudaflow cudaFlow
4445

4546
@subsection release-3-5-0_syclflow syclFlow
4647

4748
@subsection release-3-5-0_utilities Utilities
4849

50+
+ Added tf::unroll to unroll loops using template techniques
51+
+ Added tf::CachelineAligned to create a cacheline-aligned struct
52+
4953
@subsection release-3-5-0_profiler Taskflow Profiler (TFProf)
5054

5155
@section release-3-5-0_bug_fixes Bug Fixes

examples/CMakeLists.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ list(APPEND TF_EXAMPLES
1313
while_loop
1414
if_else
1515
nested_if_else
16+
priority
1617
visualization
1718
reduce
1819
parallel_for
1920
parallel_sort
20-
parallel_pipeline
21-
parallel_scalable_pipeline
22-
parallel_text_pipeline
21+
pipeline
22+
scalable_pipeline
23+
text_pipeline
24+
taskflow_pipeline
2325
parallel_graph_pipeline
24-
parallel_taskflow_pipeline
2526
data_parallel_pipeline
2627
run
2728
run_and_wait
File renamed without changes.

examples/priority.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// This program demonstrates how to set priority to a task.
2+
//
3+
// Currently, Taskflow supports only three priority levels:
4+
// + tf::TaskPriority::HIGH (numerical value = 0)
5+
// + tf::TaskPriority::NORMAL (numerical value = 1)
6+
// + tf::TaskPriority::LOW (numerical value = 2)
7+
//
8+
// Priority-based execution is non-preemptive. Once a task
9+
// has started to execute, it will execute to completion,
10+
// even if a higher priority task has been spawned or enqueued.
11+
12+
#include <taskflow/taskflow.hpp>
13+
14+
int main() {
15+
16+
// create an executor of only one worker to enable
17+
// deterministic behavior
18+
tf::Executor executor(1);
19+
20+
tf::Taskflow taskflow;
21+
22+
int counter {0};
23+
24+
// Here we create five tasks and print thier execution
25+
// orders which should align with assigned priorities
26+
auto [A, B, C, D, E] = taskflow.emplace(
27+
[] () { },
28+
[&] () {
29+
std::cout << "Task B: " << counter++ << '\n'; // 0
30+
},
31+
[&] () {
32+
std::cout << "Task C: " << counter++ << '\n'; // 2
33+
},
34+
[&] () {
35+
std::cout << "Task D: " << counter++ << '\n'; // 1
36+
},
37+
[] () { }
38+
);
39+
40+
A.precede(B, C, D);
41+
E.succeed(B, C, D);
42+
43+
// By default, all tasks are of tf::TaskPriority::HIGH
44+
B.priority(tf::TaskPriority::HIGH);
45+
C.priority(tf::TaskPriority::LOW);
46+
D.priority(tf::TaskPriority::NORMAL);
47+
48+
assert(B.priority() == tf::TaskPriority::HIGH);
49+
assert(C.priority() == tf::TaskPriority::LOW);
50+
assert(D.priority() == tf::TaskPriority::NORMAL);
51+
52+
// we should see B, D, and C in their priority order
53+
executor.run(taskflow).wait();
54+
}
55+
File renamed without changes.
File renamed without changes.
File renamed without changes.

taskflow/core/executor.hpp

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,18 @@ class Executor {
5858
/**
5959
@brief constructs the executor with @c N worker threads
6060
61+
62+
@param N number of workers (default std::thread::hardware_concurrency)
63+
@param wix worker interface class to alter worker (thread) behaviors
64+
6165
The constructor spawns @c N worker threads to run tasks in a
6266
work-stealing loop. The number of workers must be greater than zero
6367
or an exception will be thrown.
6468
By default, the number of worker threads is equal to the maximum
6569
hardware concurrency returned by std::thread::hardware_concurrency.
70+
71+
Users can alter the worker behavior, such as changing thread affinity,
72+
via deriving an instance from tf::WorkerInterface.
6673
*/
6774
explicit Executor(
6875
size_t N = std::thread::hardware_concurrency(),
@@ -1167,31 +1174,41 @@ inline size_t Executor::num_observers() const noexcept {
11671174

11681175
// Procedure: _schedule
11691176
inline void Executor::_schedule(Worker& worker, Node* node) {
1177+
1178+
// We need to fetch p before the release such that the read
1179+
// operation is synchronized properly with other thread to
1180+
// void data race.
1181+
auto p = static_cast<unsigned>(node->_priority);
11701182

11711183
node->_state.fetch_or(Node::READY, std::memory_order_release);
11721184

11731185
// caller is a worker to this pool
11741186
if(worker._executor == this) {
1175-
worker._wsq.push(node);
1187+
worker._wsq.push(node, p);
11761188
return;
11771189
}
11781190

11791191
{
11801192
std::lock_guard<std::mutex> lock(_wsq_mutex);
1181-
_wsq.push(node);
1193+
_wsq.push(node, p);
11821194
}
11831195

11841196
_notifier.notify(false);
11851197
}
11861198

11871199
// Procedure: _schedule
11881200
inline void Executor::_schedule(Node* node) {
1201+
1202+
// We need to fetch p before the release such that the read
1203+
// operation is synchronized properly with other thread to
1204+
// void data race.
1205+
auto p = static_cast<unsigned>(node->_priority);
11891206

11901207
node->_state.fetch_or(Node::READY, std::memory_order_release);
11911208

11921209
{
11931210
std::lock_guard<std::mutex> lock(_wsq_mutex);
1194-
_wsq.push(node);
1211+
_wsq.push(node, p);
11951212
}
11961213

11971214
_notifier.notify(false);
@@ -1208,22 +1225,24 @@ inline void Executor::_schedule(Worker& worker, const SmallVector<Node*>& nodes)
12081225
return;
12091226
}
12101227

1211-
// make the node ready
1212-
for(size_t i=0; i<num_nodes; ++i) {
1213-
nodes[i]->_state.fetch_or(Node::READY, std::memory_order_release);
1214-
}
1215-
1228+
// We need to fetch p before the release such that the read
1229+
// operation is synchronized properly with other thread to
1230+
// void data race.
12161231
if(worker._executor == this) {
12171232
for(size_t i=0; i<num_nodes; ++i) {
1218-
worker._wsq.push(nodes[i]);
1233+
auto p = static_cast<unsigned>(nodes[i]->_priority);
1234+
nodes[i]->_state.fetch_or(Node::READY, std::memory_order_release);
1235+
worker._wsq.push(nodes[i], p);
12191236
}
12201237
return;
12211238
}
12221239

12231240
{
12241241
std::lock_guard<std::mutex> lock(_wsq_mutex);
12251242
for(size_t k=0; k<num_nodes; ++k) {
1226-
_wsq.push(nodes[k]);
1243+
auto p = static_cast<unsigned>(nodes[k]->_priority);
1244+
nodes[k]->_state.fetch_or(Node::READY, std::memory_order_release);
1245+
_wsq.push(nodes[k], p);
12271246
}
12281247
}
12291248

@@ -1240,15 +1259,15 @@ inline void Executor::_schedule(const SmallVector<Node*>& nodes) {
12401259
return;
12411260
}
12421261

1243-
// make the node ready
1244-
for(size_t i=0; i<num_nodes; ++i) {
1245-
nodes[i]->_state.fetch_or(Node::READY, std::memory_order_release);
1246-
}
1247-
1262+
// We need to fetch p before the release such that the read
1263+
// operation is synchronized properly with other thread to
1264+
// void data race.
12481265
{
12491266
std::lock_guard<std::mutex> lock(_wsq_mutex);
12501267
for(size_t k=0; k<num_nodes; ++k) {
1251-
_wsq.push(nodes[k]);
1268+
auto p = static_cast<unsigned>(nodes[k]->_priority);
1269+
nodes[k]->_state.fetch_or(Node::READY, std::memory_order_release);
1270+
_wsq.push(nodes[k], p);
12521271
}
12531272
}
12541273

@@ -1376,7 +1395,9 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
13761395
auto& j = (node->_parent) ? node->_parent->_join_counter :
13771396
node->_topology->_join_counter;
13781397

1398+
// Here, we want to cache the latest successor with the highest priority
13791399
Node* cache {nullptr};
1400+
TaskPriority max_p {TaskPriority::MAX};
13801401

13811402
// At this point, the node storage might be destructed (to be verified)
13821403
// case 1: non-condition task
@@ -1391,10 +1412,16 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
13911412
// zeroing the join counter for invariant
13921413
s->_join_counter.store(0, std::memory_order_relaxed);
13931414
j.fetch_add(1);
1394-
if(cache) {
1395-
_schedule(worker, cache);
1415+
if(s->_priority <= max_p) {
1416+
if(cache) {
1417+
_schedule(worker, cache);
1418+
}
1419+
cache = s;
1420+
max_p = s->_priority;
1421+
}
1422+
else {
1423+
_schedule(worker, s);
13961424
}
1397-
cache = s;
13981425
}
13991426
}
14001427
}
@@ -1403,12 +1430,18 @@ inline void Executor::_invoke(Worker& worker, Node* node) {
14031430
// non-condition task
14041431
default: {
14051432
for(size_t i=0; i<node->_successors.size(); ++i) {
1406-
if(--(node->_successors[i]->_join_counter) == 0) {
1433+
if(auto s = node->_successors[i]; --(s->_join_counter) == 0) {
14071434
j.fetch_add(1);
1408-
if(cache) {
1409-
_schedule(worker, cache);
1435+
if(s->_priority <= max_p) {
1436+
if(cache) {
1437+
_schedule(worker, cache);
1438+
}
1439+
cache = s;
1440+
max_p = s->_priority;
1441+
}
1442+
else {
1443+
_schedule(worker, s);
14101444
}
1411-
cache = node->_successors[i];
14121445
}
14131446
}
14141447
}

taskflow/core/graph.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "semaphore.hpp"
1212
#include "environment.hpp"
1313
#include "topology.hpp"
14+
#include "tsq.hpp"
1415

1516
/**
1617
@file graph.hpp
@@ -428,6 +429,8 @@ class Node {
428429
private:
429430

430431
std::string _name;
432+
433+
TaskPriority _priority {TaskPriority::HIGH};
431434

432435
void* _data {nullptr};
433436

0 commit comments

Comments
 (0)