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
5bdca02f
authored
6 years ago
by
FritzFlorian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add API for starting top level tasks.
parent
88b25f22
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
115 additions
and
48 deletions
+115
-48
lib/pls/include/pls/internal/base/aligned_stack.h
+3
-1
lib/pls/include/pls/internal/scheduling/scheduler.h
+56
-45
lib/pls/include/pls/internal/scheduling/thread_state.h
+6
-2
lib/pls/src/internal/scheduling/scheduler.cpp
+50
-0
No files found.
lib/pls/include/pls/internal/base/aligned_stack.h
View file @
5bdca02f
...
@@ -19,7 +19,9 @@ namespace pls {
...
@@ -19,7 +19,9 @@ namespace pls {
static
std
::
uintptr_t
next_alignment
(
std
::
uintptr_t
size
);
static
std
::
uintptr_t
next_alignment
(
std
::
uintptr_t
size
);
static
char
*
next_alignment
(
char
*
pointer
);
static
char
*
next_alignment
(
char
*
pointer
);
public
:
public
:
explicit
aligned_stack
(
char
*
memory_region
,
const
std
::
size_t
size
)
:
aligned_stack
()
:
memory_start_
{
nullptr
},
memory_end_
{
nullptr
},
head_
{
nullptr
}
{};
aligned_stack
(
char
*
memory_region
,
const
std
::
size_t
size
)
:
memory_start_
{
memory_region
},
memory_start_
{
memory_region
},
memory_end_
{
memory_region
+
size
},
memory_end_
{
memory_region
+
size
},
head_
{
next_alignment
(
memory_start_
)}
{}
head_
{
next_alignment
(
memory_start_
)}
{}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/include/pls/internal/scheduling/scheduler.h
View file @
5bdca02f
...
@@ -5,9 +5,11 @@
...
@@ -5,9 +5,11 @@
#include <array>
#include <array>
#include <iostream>
#include <iostream>
#include "pls/internal/base/aligned_stack.h"
#include "pls/internal/base/thread.h"
#include "pls/internal/base/thread.h"
#include "pls/internal/base/barrier.h"
#include "pls/internal/base/barrier.h"
#include "pls/internal/scheduling/thread_state.h"
#include "pls/internal/scheduling/thread_state.h"
#include "root_master_task.h"
#include "root_master_task.h"
#include "root_worker_task.h"
#include "root_worker_task.h"
...
@@ -18,78 +20,87 @@ namespace pls {
...
@@ -18,78 +20,87 @@ namespace pls {
// Could be moved to templating if needed.
// Could be moved to templating if needed.
static
constexpr
int
MAX_THREADS
=
32
;
static
constexpr
int
MAX_THREADS
=
32
;
class
scheduler
{
void
worker_routine
();
static
void
worker_routine
()
{
using
scheduler_thread
=
base
::
thread
<
decltype
(
&
worker_routine
),
thread_state
>
;
auto
my_state
=
base
::
this_thread
::
state
<
thread_state
>
();
while
(
true
)
{
class
scheduler_memory
{
my_state
->
scheduler_
->
sync_barrier_
.
wait
();
public
:
if
(
my_state
->
scheduler_
->
terminated_
)
{
virtual
size_t
max_threads
()
=
0
;
return
;
virtual
thread_state
*
thread_state_for
(
size_t
id
)
=
0
;
}
virtual
scheduler_thread
*
thread_for
(
size_t
id
)
=
0
;
virtual
base
::
aligned_stack
*
task_stack_for
(
size_t
id
)
=
0
;
};
// The root task must only return when all work is done,
template
<
size_t
MAX_THREADS
,
size_t
TASK_STACK_SIZE
>
// because of this a simple call is enough to ensure the
class
static_scheduler_memory
:
public
scheduler_memory
{
// fork-join-section is done (logically joined back into our main thread).
std
::
array
<
scheduler_thread
,
MAX_THREADS
>
threads_
;
my_state
->
root_task_
->
execute
();
std
::
array
<
thread_state
,
MAX_THREADS
>
thread_states_
;
std
::
array
<
std
::
array
<
char
,
TASK_STACK_SIZE
>
,
MAX_THREADS
>
task_stacks_memory_
;
std
::
array
<
base
::
aligned_stack
,
MAX_THREADS
>
task_stacks_
;
my_state
->
scheduler_
->
sync_barrier_
.
wait
();
public
:
static_scheduler_memory
()
{
for
(
size_t
i
=
0
;
i
<
MAX_THREADS
;
i
++
)
{
task_stacks_
[
i
]
=
base
::
aligned_stack
(
reinterpret_cast
<
char
*>
(
&
task_stacks_memory_
[
i
]),
TASK_STACK_SIZE
);
}
}
}
}
size_t
max_threads
()
override
{
return
MAX_THREADS
;
}
thread_state
*
thread_state_for
(
size_t
id
)
override
{
return
&
thread_states_
[
id
];
}
scheduler_thread
*
thread_for
(
size_t
id
)
override
{
return
&
threads_
[
id
];
}
base
::
aligned_stack
*
task_stack_for
(
size_t
id
)
override
{
return
&
task_stacks_
[
id
];
}
};
class
scheduler
{
friend
void
worker_routine
();
unsigned
int
num_threads_
;
unsigned
int
num_threads_
;
std
::
array
<
thread_state
,
MAX_THREADS
>
thread_states_
;
scheduler_memory
*
memory_
;
std
::
array
<
base
::
thread
<
decltype
(
&
worker_routine
),
thread_state
>
,
MAX_THREADS
>
threads_
;
base
::
barrier
sync_barrier_
;
base
::
barrier
sync_barrier_
;
bool
terminated_
;
bool
terminated_
;
public
:
public
:
explicit
scheduler
(
const
unsigned
int
num_threads
)
:
explicit
scheduler
(
scheduler_memory
*
memory
,
unsigned
int
num_threads
);
num_threads_
{
num_threads
},
~
scheduler
();
sync_barrier_
{
num_threads
+
1
},
terminated_
{
false
}
{
if
(
num_threads
>
MAX_THREADS
)
{
exit
(
1
);
// TODO: Exception Handling
}
for
(
unsigned
int
i
=
0
;
i
<
num_threads
;
i
++
)
{
thread_states_
[
i
]
=
thread_state
{
this
};
threads_
[
i
]
=
base
::
start_thread
(
&
worker_routine
,
&
thread_states_
[
i
]);
}
}
~
scheduler
()
{
terminate
();
}
template
<
typename
Function
>
template
<
typename
Function
>
void
perform_work
(
Function
work_section
)
{
void
perform_work
(
Function
work_section
)
{
root_master_task
<
Function
>
master
{
work_section
};
root_master_task
<
Function
>
master
{
work_section
};
root_worker_task
<
Function
>
worker
{
&
master
};
root_worker_task
<
Function
>
worker
{
&
master
};
thread_states_
[
0
].
root_task_
=
&
master
;
// Push root task on stacks
memory_
->
thread_state_for
(
0
)
->
root_task_
=
memory_
->
task_stack_for
(
0
)
->
push
(
master
);
for
(
unsigned
int
i
=
1
;
i
<
num_threads_
;
i
++
)
{
for
(
unsigned
int
i
=
1
;
i
<
num_threads_
;
i
++
)
{
thread_states_
[
i
].
root_task_
=
&
worker
;
memory_
->
thread_state_for
(
i
)
->
root_task_
=
memory_
->
task_stack_for
(
i
)
->
push
(
worker
)
;
}
}
// Perform and wait for work
sync_barrier_
.
wait
();
// Trigger threads to wake up
sync_barrier_
.
wait
();
// Trigger threads to wake up
sync_barrier_
.
wait
();
// Wait for threads to finish
sync_barrier_
.
wait
();
// Wait for threads to finish
}
void
terminate
(
bool
wait_for_workers
=
true
)
{
// Remove root task from stacks
if
(
terminated_
)
{
memory_
->
task_stack_for
(
0
)
->
pop
<
typeof
(
master
)
>
();
return
;
for
(
unsigned
int
i
=
1
;
i
<
num_threads_
;
i
++
)
{
memory_
->
task_stack_for
(
i
)
->
pop
<
typeof
(
worker
)
>
();
}
}
}
terminated_
=
true
;
// TODO: See if we should place this differently (only for performance reasons)
sync_barrier_
.
wait
();
template
<
typename
Task
>
void
execute_task
(
Task
&
task
)
{
static_assert
(
std
::
is_base_of
<
abstract_task
,
Task
>::
value
,
"Only pass abstract_task subclasses!"
);
if
(
wait_for_workers
)
{
auto
my_state
=
base
::
this_thread
::
state
<
thread_state
>
();
for
(
unsigned
int
i
=
0
;
i
<
num_threads_
;
i
++
)
{
auto
task_stack
=
my_state
->
task_stack_
;
threads_
[
i
].
join
();
}
// TODO: Assert if 'top level' task even have to go somewhere or if
}
// we can simply keep the on the call stack.
auto
my_task
=
task_stack
->
push
(
task
);
my_task
.
execute
();
task_stack
->
pop
<
Task
>
();
}
}
void
terminate
(
bool
wait_for_workers
=
true
);
};
};
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/include/pls/internal/scheduling/thread_state.h
View file @
5bdca02f
...
@@ -11,11 +11,15 @@ namespace pls {
...
@@ -11,11 +11,15 @@ namespace pls {
class
scheduler
;
class
scheduler
;
struct
thread_state
{
struct
thread_state
{
thread_state
()
:
scheduler_
{
nullptr
},
root_task_
{
nullptr
}
{};
thread_state
()
:
scheduler_
{
nullptr
},
root_task_
{
nullptr
},
task_stack_
{
nullptr
}
{};
explicit
thread_state
(
scheduler
*
scheduler
)
:
scheduler_
{
scheduler
},
root_task_
{
nullptr
}
{}
explicit
thread_state
(
scheduler
*
scheduler
,
base
::
aligned_stack
*
task_stack
)
:
scheduler_
{
scheduler
},
root_task_
{
nullptr
},
task_stack_
{
task_stack
}
{}
scheduler
*
scheduler_
;
scheduler
*
scheduler_
;
abstract_task
*
root_task_
;
abstract_task
*
root_task_
;
base
::
aligned_stack
*
task_stack_
;
};
};
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
lib/pls/src/internal/scheduling/scheduler.cpp
View file @
5bdca02f
...
@@ -3,7 +3,57 @@
...
@@ -3,7 +3,57 @@
namespace
pls
{
namespace
pls
{
namespace
internal
{
namespace
internal
{
namespace
scheduling
{
namespace
scheduling
{
scheduler
::
scheduler
(
scheduler_memory
*
memory
,
const
unsigned
int
num_threads
)
:
num_threads_
{
num_threads
},
memory_
{
memory
},
sync_barrier_
{
num_threads
+
1
},
terminated_
{
false
}
{
if
(
num_threads
>
MAX_THREADS
)
{
exit
(
1
);
// TODO: Exception Handling
}
for
(
unsigned
int
i
=
0
;
i
<
num_threads
;
i
++
)
{
*
memory_
->
thread_state_for
(
i
)
=
thread_state
{
this
,
memory_
->
task_stack_for
(
i
)};
*
memory_
->
thread_for
(
i
)
=
base
::
start_thread
(
&
worker_routine
,
memory_
->
thread_state_for
(
i
));
}
}
scheduler
::~
scheduler
()
{
terminate
();
}
void
worker_routine
()
{
auto
my_state
=
base
::
this_thread
::
state
<
thread_state
>
();
while
(
true
)
{
my_state
->
scheduler_
->
sync_barrier_
.
wait
();
if
(
my_state
->
scheduler_
->
terminated_
)
{
return
;
}
// The root task must only return when all work is done,
// because of this a simple call is enough to ensure the
// fork-join-section is done (logically joined back into our main thread).
my_state
->
root_task_
->
execute
();
my_state
->
scheduler_
->
sync_barrier_
.
wait
();
}
}
void
scheduler
::
terminate
(
bool
wait_for_workers
)
{
if
(
terminated_
)
{
return
;
}
terminated_
=
true
;
sync_barrier_
.
wait
();
if
(
wait_for_workers
)
{
for
(
unsigned
int
i
=
0
;
i
<
num_threads_
;
i
++
)
{
memory_
->
thread_for
(
i
)
->
join
();
}
}
}
}
}
}
}
}
}
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