Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
las3_pub
/
predictable_parallel_patterns
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
4da92c52
authored
6 years ago
by
FritzFlorian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add basic tests for TBB like tasks.
parent
1ebfd567
Pipeline
#1119
passed with stages
in 3 minutes 16 seconds
Changes
8
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
121 additions
and
26 deletions
+121
-26
lib/pls/include/pls/internal/base/spin_lock.h
+1
-3
lib/pls/include/pls/internal/scheduling/scheduler.h
+28
-12
lib/pls/include/pls/internal/scheduling/tbb_task.h
+5
-3
lib/pls/src/internal/base/spin_lock.cpp
+2
-4
lib/pls/src/internal/scheduling/abstract_task.cpp
+2
-1
lib/pls/src/internal/scheduling/tbb_task.cpp
+3
-2
test/CMakeLists.txt
+1
-1
test/scheduling_tests.cpp
+79
-0
No files found.
lib/pls/include/pls/internal/base/spin_lock.h
View file @
4da92c52
...
...
@@ -17,9 +17,7 @@ namespace pls {
public
:
spin_lock
()
:
flag_
{
ATOMIC_FLAG_INIT
},
yield_at_tries_
{
1024
}
{};
spin_lock
(
const
spin_lock
&
other
)
:
flag_
{
ATOMIC_FLAG_INIT
},
yield_at_tries_
{
other
.
yield_at_tries_
}
{
std
::
cout
<<
"Spinlock Moved!"
<<
std
::
endl
;
}
spin_lock
(
const
spin_lock
&
other
)
:
flag_
{
ATOMIC_FLAG_INIT
},
yield_at_tries_
{
other
.
yield_at_tries_
}
{}
void
lock
();
void
unlock
();
...
...
This diff is collapsed.
Click to expand it.
lib/pls/include/pls/internal/scheduling/scheduler.h
View file @
4da92c52
...
...
@@ -65,19 +65,28 @@ namespace pls {
template
<
typename
Function
>
void
perform_work
(
Function
work_section
)
{
root_task
<
Function
>
master
{
work_section
};
root_worker_task
<
Function
>
worker
{
&
master
};
// Push root task on stacks
memory_
->
thread_state_for
(
0
)
->
root_task_
=
&
master
;
memory_
->
thread_state_for
(
0
)
->
current_task_
=
&
master
;
auto
new_master
=
memory_
->
task_stack_for
(
0
)
->
push
(
master
);
memory_
->
thread_state_for
(
0
)
->
root_task_
=
new_master
;
memory_
->
thread_state_for
(
0
)
->
current_task_
=
new_master
;
for
(
unsigned
int
i
=
1
;
i
<
num_threads_
;
i
++
)
{
memory_
->
thread_state_for
(
i
)
->
root_task_
=
&
worker
;
memory_
->
thread_state_for
(
i
)
->
current_task_
=
&
worker
;
root_worker_task
<
Function
>
worker
{
new_master
};
auto
new_worker
=
memory_
->
task_stack_for
(
0
)
->
push
(
worker
);
memory_
->
thread_state_for
(
i
)
->
root_task_
=
new_worker
;
memory_
->
thread_state_for
(
i
)
->
current_task_
=
new_worker
;
}
// Perform and wait for work
sync_barrier_
.
wait
();
// Trigger threads to wake up
sync_barrier_
.
wait
();
// Wait for threads to finish
// Clean up stack
memory_
->
task_stack_for
(
0
)
->
pop
<
typeof
(
master
)
>
();
for
(
unsigned
int
i
=
1
;
i
<
num_threads_
;
i
++
)
{
root_worker_task
<
Function
>
worker
{
new_master
};
memory_
->
task_stack_for
(
0
)
->
pop
<
typeof
(
worker
)
>
();
}
}
// TODO: See if we should place this differently (only for performance reasons)
...
...
@@ -86,24 +95,31 @@ namespace pls {
static_assert
(
std
::
is_base_of
<
abstract_task
,
Task
>::
value
,
"Only pass abstract_task subclasses!"
);
auto
my_state
=
base
::
this_thread
::
state
<
thread_state
>
();
auto
current_task
=
my_state
->
current_task_
;
abstract_task
*
old_task
;
abstract_task
*
new_task
;
// Init Task
{
std
::
lock_guard
<
base
::
spin_lock
>
lock
{
my_state
->
lock_
};
task
.
set_depth
(
depth
>=
0
?
depth
:
current_task
->
depth
()
+
1
);
my_state
->
current_task_
=
&
task
;
current_task
->
set_child
(
&
task
);
old_task
=
my_state
->
current_task_
;
new_task
=
my_state
->
task_stack_
->
push
(
task
);
new_task
->
set_depth
(
depth
>=
0
?
depth
:
old_task
->
depth
()
+
1
);
my_state
->
current_task_
=
new_task
;
old_task
->
set_child
(
new_task
);
}
// Run Task
task
.
execute
();
new_task
->
execute
();
// Teardown state back to before the task was executed
{
std
::
lock_guard
<
base
::
spin_lock
>
lock
{
my_state
->
lock_
};
current_task
->
set_child
(
nullptr
);
my_state
->
current_task_
=
current_task
;
old_task
->
set_child
(
nullptr
);
my_state
->
current_task_
=
old_task
;
my_state
->
task_stack_
->
pop
<
Task
>
();
}
}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/include/pls/internal/scheduling/tbb_task.h
View file @
4da92c52
...
...
@@ -74,13 +74,15 @@ namespace pls {
abstract_task
{
0
,
0
},
root_task_
{
root_task
},
deque_
{},
last_stolen_
{
nullptr
}
{
last_stolen_
{
nullptr
}
{};
void
execute
()
override
{
// Bind this instance to our OS thread
my_stack_
=
base
::
this_thread
::
state
<
thread_state
>
()
->
task_stack_
;
root_task_
->
tbb_task_
=
this
;
root_task_
->
stack_state_
=
my_stack_
->
save_state
();
};
void
execute
()
override
{
// Execute it on our OS thread until its finished
root_task_
->
execute
();
}
};
...
...
This diff is collapsed.
Click to expand it.
lib/pls/src/internal/base/spin_lock.cpp
View file @
4da92c52
...
...
@@ -8,18 +8,16 @@ namespace pls {
// also act as a strict memory fence.
void
spin_lock
::
lock
()
{
int
tries
=
0
;
while
(
flag_
.
test_and_set
(
std
::
memory_order_
acquire
))
{
while
(
flag_
.
test_and_set
(
std
::
memory_order_
seq_cst
))
{
tries
++
;
if
(
tries
%
yield_at_tries_
==
0
)
{
this_thread
::
yield
();
}
}
std
::
atomic_thread_fence
(
std
::
memory_order_seq_cst
);
}
void
spin_lock
::
unlock
()
{
flag_
.
clear
(
std
::
memory_order_release
);
std
::
atomic_thread_fence
(
std
::
memory_order_seq_cst
);
flag_
.
clear
(
std
::
memory_order_seq_cst
);
}
}
}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/src/internal/scheduling/abstract_task.cpp
View file @
4da92c52
...
...
@@ -42,7 +42,8 @@ namespace pls {
// Execute 'top level task steal' if possible
// (only try deeper tasks to keep depth restricted stealing)
while
(
current_task
!=
nullptr
)
{
if
(
current_task
->
split_task
(
&
target_state
->
lock_
))
{
auto
lock
=
&
target_state
->
lock_
;
if
(
current_task
->
split_task
(
lock
))
{
// internal steal was no success (we did a top level task steal)
return
false
;
}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/src/internal/scheduling/tbb_task.cpp
View file @
4da92c52
#include
<pls/internal/scheduling/scheduler.h>
#include
"pls/internal/scheduling/scheduler.h"
#include "pls/internal/scheduling/tbb_task.h"
namespace
pls
{
...
...
@@ -82,11 +82,12 @@ namespace pls {
if
(
stolen_sub_task
==
nullptr
)
{
return
false
;
}
tbb_task
task
{
stolen_sub_task
};
// In success case, unlock.
// TODO: this locking is complicated and error prone.
lock
->
unlock
();
tbb_task
task
{
stolen_sub_task
};
scheduler
::
execute_task
(
task
,
depth
());
return
true
;
}
...
...
This diff is collapsed.
Click to expand it.
test/CMakeLists.txt
View file @
4da92c52
add_executable
(
tests
main.cpp
base_tests.cpp
)
base_tests.cpp
scheduling_tests.cpp
)
target_link_libraries
(
tests catch2 pls
)
This diff is collapsed.
Click to expand it.
test/scheduling_tests.cpp
0 → 100644
View file @
4da92c52
#include <catch.hpp>
#include <pls/pls.h>
using
namespace
pls
;
class
once_sub_task
:
public
tbb_sub_task
{
std
::
atomic
<
int
>*
counter_
;
int
children_
;
protected
:
void
execute_internal
()
override
{
(
*
counter_
)
++
;
for
(
int
i
=
0
;
i
<
children_
;
i
++
)
{
spawn_child
(
once_sub_task
(
counter_
,
children_
-
1
));
}
}
public
:
explicit
once_sub_task
(
std
::
atomic
<
int
>*
counter
,
int
children
)
:
tbb_sub_task
(),
counter_
{
counter
},
children_
{
children
}
{}
};
class
force_steal_sub_task
:
public
tbb_sub_task
{
std
::
atomic
<
int
>*
parent_counter_
;
std
::
atomic
<
int
>*
overall_counter_
;
protected
:
void
execute_internal
()
override
{
(
*
overall_counter_
)
--
;
if
(
overall_counter_
->
load
()
>
0
)
{
std
::
atomic
<
int
>
counter
{
1
};
spawn_child
(
force_steal_sub_task
(
&
counter
,
overall_counter_
));
while
(
counter
.
load
()
>
0
)
;
// Spin...
}
(
*
parent_counter_
)
--
;
}
public
:
explicit
force_steal_sub_task
(
std
::
atomic
<
int
>*
parent_counter
,
std
::
atomic
<
int
>*
overall_counter
)
:
tbb_sub_task
(),
parent_counter_
{
parent_counter
},
overall_counter_
{
overall_counter
}
{}
};
TEST_CASE
(
"tbb task are scheduled correctly"
,
"[internal/scheduling/tbb_task.h]"
)
{
static
static_scheduler_memory
<
8
,
2
<<
12
>
my_scheduler_memory
;
SECTION
(
"tasks are executed exactly once"
)
{
scheduler
my_scheduler
{
&
my_scheduler_memory
,
2
};
int
start_counter
=
4
;
int
total_tasks
=
1
+
4
+
4
*
3
+
4
*
3
*
2
+
4
*
3
*
2
*
1
;
std
::
atomic
<
int
>
counter
{
0
};
my_scheduler
.
perform_work
([
&
]
(){
once_sub_task
sub_task
{
&
counter
,
start_counter
};
tbb_task
task
{
&
sub_task
};
scheduler
::
execute_task
(
task
);
});
REQUIRE
(
counter
.
load
()
==
total_tasks
);
my_scheduler
.
terminate
(
true
);
}
SECTION
(
"tasks can be stolen"
)
{
scheduler
my_scheduler
{
&
my_scheduler_memory
,
8
};
my_scheduler
.
perform_work
([
&
]
(){
std
::
atomic
<
int
>
dummy_parent
{
1
},
overall_counter
{
8
};
force_steal_sub_task
sub_task
{
&
dummy_parent
,
&
overall_counter
};
tbb_task
task
{
&
sub_task
};
scheduler
::
execute_task
(
task
);
});
my_scheduler
.
terminate
(
true
);
}
}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment