Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
59c6e837
Commit
59c6e837
authored
Apr 28, 2017
by
Sebastian Woelke
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement NUMA-aware work stealing scheduler
parent
ee702b88
Changes
14
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
491 additions
and
61 deletions
+491
-61
CMakeLists.txt
CMakeLists.txt
+11
-10
cmake/build_config.hpp.in
cmake/build_config.hpp.in
+1
-1
examples/CMakeLists.txt
examples/CMakeLists.txt
+1
-0
examples/hello_world.cpp
examples/hello_world.cpp
+3
-4
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/policy/numa_aware_work_stealing.hpp
libcaf_core/caf/policy/numa_aware_work_stealing.hpp
+323
-0
libcaf_core/caf/policy/scheduler_policy.hpp
libcaf_core/caf/policy/scheduler_policy.hpp
+13
-0
libcaf_core/caf/policy/work_sharing.hpp
libcaf_core/caf/policy/work_sharing.hpp
+21
-2
libcaf_core/caf/policy/work_stealing.hpp
libcaf_core/caf/policy/work_stealing.hpp
+44
-17
libcaf_core/caf/scheduler/coordinator.hpp
libcaf_core/caf/scheduler/coordinator.hpp
+11
-15
libcaf_core/caf/scheduler/worker.hpp
libcaf_core/caf/scheduler/worker.hpp
+2
-1
libcaf_core/src/actor_system.cpp
libcaf_core/src/actor_system.cpp
+19
-9
libcaf_core/src/actor_system_config.cpp
libcaf_core/src/actor_system_config.cpp
+2
-2
libcaf_core/src/numa_aware_work_stealing.cpp
libcaf_core/src/numa_aware_work_stealing.cpp
+39
-0
No files found.
CMakeLists.txt
View file @
59c6e837
...
...
@@ -277,6 +277,15 @@ endif(CAF_ENABLE_ADDRESS_SANITIZER)
if
(
NOT APPLE AND NOT WIN32
)
set
(
EXTRA_FLAGS
"
${
EXTRA_FLAGS
}
-pthread"
)
endif
()
# load hwloc library if not told otherwise
if
(
NOT CAF_NO_HWLOC
)
find_package
(
Hwloc
)
if
(
Hwloc_FOUND
)
set
(
LD_FLAGS
${
LD_FLAGS
}
${
Hwloc_LIBRARIES
}
)
else
()
set
(
CAF_NO_HWLOC yes
)
endif
()
endif
()
# -fPIC generates warnings on MinGW and Cygwin plus extra setup steps needed on MinGW
if
(
MINGW
)
add_definitions
(
-D_WIN32_WINNT=0x0600
)
...
...
@@ -345,6 +354,7 @@ macro(to_int_value name)
endif
()
endmacro
()
to_int_value
(
CAF_NO_HWLOC
)
to_int_value
(
CAF_LOG_LEVEL
)
to_int_value
(
CAF_NO_EXCEPTIONS
)
to_int_value
(
CAF_NO_MEM_MANAGEMENT
)
...
...
@@ -496,6 +506,7 @@ endmacro()
# build core and I/O library
add_caf_lib
(
core
)
set
(
CAF_CORE_LIBRARY libcaf_core_shared
)
add_optional_caf_lib
(
io
)
# build opencl library if not told otherwise and OpenCL package was found
...
...
@@ -509,16 +520,6 @@ if(NOT CAF_NO_OPENCL)
endif
()
endif
()
# load hwloc library if not told otherwise
if
(
NOT CAF_NO_HWLOC
)
find_package
(
Hwloc
)
if
(
Hwloc_FOUND
)
add_optional_caf_lib
(
hwloc
)
else
()
set
(
CAF_NO_HWLOC yes
)
endif
()
endif
()
# build Python binding if not being told otherwise
if
(
NOT CAF_NO_PYTHON AND EXISTS
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/libcaf_python/CMakeLists.txt"
)
add_subdirectory
(
libcaf_python
)
...
...
cmake/build_config.hpp.in
View file @
59c6e837
...
...
@@ -42,7 +42,7 @@
#define CAF_NO_EXCEPTIONS
#endif
#if @CAF_NO_HWLOC@ != -1
#if @CAF_NO_HWLOC
_INT
@ != -1
#define CAF_NO_HWLOC
#endif
...
...
examples/CMakeLists.txt
View file @
59c6e837
...
...
@@ -25,6 +25,7 @@ endmacro()
# introductionary applications
add
(
. aout
)
add
(
. hello_world
)
add
(
. fibonacci
)
# basic message passing primitives
add
(
message_passing cell
)
...
...
examples/hello_world.cpp
View file @
59c6e837
...
...
@@ -33,13 +33,12 @@ void hello_world(event_based_actor* self, const actor& buddy) {
);
}
int
main
()
{
// our CAF environment
actor_system_config
cfg
;
actor_system
system
{
cfg
};
void
caf_main
(
actor_system
&
system
)
{
// create a new actor that calls 'mirror()'
auto
mirror_actor
=
system
.
spawn
(
mirror
);
// create another actor that calls 'hello_world(mirror_actor)';
system
.
spawn
(
hello_world
,
mirror_actor
);
// system will wait until both actors are destroyed before leaving main
}
CAF_MAIN
()
libcaf_core/CMakeLists.txt
View file @
59c6e837
...
...
@@ -75,6 +75,7 @@ set (LIBCAF_CORE_SRCS
src/message_view.cpp
src/monitorable_actor.cpp
src/node_id.cpp
src/numa_aware_work_stealing.cpp
src/outgoing_stream_multiplexer.cpp
src/parse_ini.cpp
src/private_thread.cpp
...
...
libcaf_core/caf/policy/numa_aware_work_stealing.hpp
0 → 100644
View file @
59c6e837
This diff is collapsed.
Click to expand it.
libcaf_core/caf/policy/scheduler_policy.hpp
View file @
59c6e837
...
...
@@ -31,15 +31,28 @@ namespace policy {
class
scheduler_policy
{
public:
/// Policy-specific data fields for the coordinator.
template
<
class
Worker
>
struct
coordinator_data
{
explicit
coordinator_data
(
scheduler
::
abstract_coordinator
*
);
/// Stores the pointer of all workes handeld by the scheduler
std
::
vector
<
std
::
unique_ptr
<
Worker
>>
workers
;
};
/// Policy-specific data fields for the worker.
template
<
class
Worker
>
struct
worker_data
{
explicit
worker_data
(
scheduler
::
abstract_coordinator
*
);
};
/// Create x workers.
template
<
class
Coordinator
,
class
Worker
>
void
create_workers
(
Coordinator
*
self
,
size_t
num_workers
,
size_t
throughput
);
/// Initalize worker thread.
template
<
class
Worker
>
void
init_worker_thread
(
Worker
*
self
);
/// Enqueues a new job to coordinator.
template
<
class
Coordinator
>
void
central_enqueue
(
Coordinator
*
self
,
resumable
*
job
);
...
...
libcaf_core/caf/policy/work_sharing.hpp
View file @
59c6e837
...
...
@@ -40,22 +40,41 @@ public:
~
work_sharing
()
override
;
template
<
class
Worker
>
struct
coordinator_data
{
inline
explicit
coordinator_data
(
scheduler
::
abstract_coordinator
*
)
{
explicit
coordinator_data
(
scheduler
::
abstract_coordinator
*
)
{
// nop
}
queue_type
queue
;
std
::
mutex
lock
;
std
::
condition_variable
cv
;
std
::
vector
<
std
::
unique_ptr
<
Worker
>>
workers
;
};
template
<
class
Worker
>
struct
worker_data
{
inline
explicit
worker_data
(
scheduler
::
abstract_coordinator
*
)
{
explicit
worker_data
(
scheduler
::
abstract_coordinator
*
)
{
// nop
}
};
// Create x workers.
template
<
class
Coordinator
,
class
Worker
>
void
create_workers
(
Coordinator
*
self
,
size_t
num_workers
,
size_t
throughput
)
{
for
(
size_t
i
=
0
;
i
<
num_workers
;
++
i
)
{
std
::
unique_ptr
<
Worker
>
worker
(
new
Worker
(
i
,
self
,
throughput
));
d
(
self
).
workers
.
emplace_back
(
std
::
move
(
worker
));
}
}
/// Initalize worker thread.
template
<
class
Worker
>
void
init_worker_thread
(
Worker
*
)
{
// nop
}
template
<
class
Coordinator
>
void
enqueue
(
Coordinator
*
self
,
resumable
*
job
)
{
queue_type
l
;
...
...
libcaf_core/caf/policy/work_stealing.hpp
View file @
59c6e837
...
...
@@ -56,32 +56,42 @@ public:
};
// The coordinator has only a counter for round-robin enqueue to its workers.
template
<
class
Worker
>
struct
coordinator_data
{
inline
explicit
coordinator_data
(
scheduler
::
abstract_coordinator
*
)
explicit
coordinator_data
(
scheduler
::
abstract_coordinator
*
)
:
next_worker
(
0
)
{
// nop
}
std
::
atomic
<
size_t
>
next_worker
;
std
::
vector
<
std
::
unique_ptr
<
Worker
>>
workers
;
};
static
std
::
vector
<
poll_strategy
>
get_poll_strategies
(
scheduler
::
abstract_coordinator
*
p
)
{
auto
&
x
=
p
->
system
().
config
();
std
::
vector
<
poll_strategy
>
res
{
{
x
.
work_stealing_aggressive_poll_attempts
,
1
,
x
.
work_stealing_aggressive_steal_interval
,
usec
{
0
}},
{
x
.
work_stealing_moderate_poll_attempts
,
1
,
x
.
work_stealing_moderate_steal_interval
,
usec
{
x
.
work_stealing_moderate_sleep_duration_us
}},
{
1
,
0
,
x
.
work_stealing_relaxed_steal_interval
,
usec
{
p
->
system
().
config
().
work_stealing_relaxed_sleep_duration_us
}}
};
return
res
;
}
// Holds job job queue of a worker and a random number generator.
template
<
class
Worker
>
struct
worker_data
{
inline
explicit
worker_data
(
scheduler
::
abstract_coordinator
*
p
)
:
rengine
(
std
::
random_device
{}())
,
explicit
worker_data
(
scheduler
::
abstract_coordinator
*
p
)
:
rengine
(
std
::
random_device
{}())
// no need to worry about wrap-around; if `p->num_workers() < 2`,
// `uniform` will not be used anyway
uniform
(
0
,
p
->
num_workers
()
-
2
),
strategies
{
{
p
->
system
().
config
().
work_stealing_aggressive_poll_attempts
,
1
,
p
->
system
().
config
().
work_stealing_aggressive_steal_interval
,
usec
{
0
}},
{
p
->
system
().
config
().
work_stealing_moderate_poll_attempts
,
1
,
p
->
system
().
config
().
work_stealing_moderate_steal_interval
,
usec
{
p
->
system
().
config
().
work_stealing_moderate_sleep_duration_us
}},
{
1
,
0
,
p
->
system
().
config
().
work_stealing_relaxed_steal_interval
,
usec
{
p
->
system
().
config
().
work_stealing_relaxed_sleep_duration_us
}}
}
{
,
uniform
(
0
,
p
->
num_workers
()
-
2
)
,
strategies
(
get_poll_strategies
(
p
))
{
// nop
}
...
...
@@ -91,9 +101,25 @@ public:
// needed to generate pseudo random numbers
std
::
default_random_engine
rengine
;
std
::
uniform_int_distribution
<
size_t
>
uniform
;
poll_strategy
strategies
[
3
]
;
std
::
vector
<
poll_strategy
>
strategies
;
};
// Create x workers.
template
<
class
Coordinator
,
class
Worker
>
void
create_workers
(
Coordinator
*
self
,
size_t
num_workers
,
size_t
throughput
)
{
for
(
size_t
i
=
0
;
i
<
num_workers
;
++
i
)
{
std
::
unique_ptr
<
Worker
>
worker
(
new
Worker
(
i
,
self
,
throughput
));
d
(
self
).
workers
.
emplace_back
(
std
::
move
(
worker
));
}
}
/// Initalize worker thread.
template
<
class
Worker
>
void
init_worker_thread
(
Worker
*
)
{
// nop
}
// Goes on a raid in quest for a shiny new job.
template
<
class
Worker
>
resumable
*
try_steal
(
Worker
*
self
)
{
...
...
@@ -104,15 +130,16 @@ public:
}
// roll the dice to pick a victim other than ourselves
auto
victim
=
d
(
self
).
uniform
(
d
(
self
).
rengine
);
// in this case the worker id is similar to the worker index
if
(
victim
==
self
->
id
())
victim
=
p
->
num_workers
()
-
1
;
// steal oldest element from the victim's queue
return
d
(
p
->
worker_by_id
(
victim
)).
queue
.
take_tail
();
return
d
(
p
->
worker_by_id
x
(
victim
)).
queue
.
take_tail
();
}
template
<
class
Coordinator
>
void
central_enqueue
(
Coordinator
*
self
,
resumable
*
job
)
{
auto
w
=
self
->
worker_by_id
(
d
(
self
).
next_worker
++
%
self
->
num_workers
());
auto
w
=
self
->
worker_by_id
x
(
d
(
self
).
next_worker
++
%
self
->
num_workers
());
w
->
external_enqueue
(
job
);
}
...
...
libcaf_core/caf/scheduler/coordinator.hpp
View file @
59c6e837
...
...
@@ -39,16 +39,17 @@ class coordinator : public abstract_coordinator {
public:
using
super
=
abstract_coordinator
;
using
policy_data
=
typename
Policy
::
coordinator_data
;
using
worker_type
=
worker
<
Policy
>
;
using
policy_data
=
typename
Policy
::
template
coordinator_data
<
worker_type
>;
coordinator
(
actor_system
&
sys
)
:
super
(
sys
),
data_
(
this
)
{
// nop
}
using
worker_type
=
worker
<
Policy
>
;
worker_type
*
worker_by_id
(
size_t
x
)
{
return
workers_
[
x
].
get
();
/// return a worker by its vector index
/// (this is not necessarily the id of the worker)
worker_type
*
worker_by_id
x
(
size_t
x
)
{
return
data_
.
workers
[
x
].
get
();
}
policy_data
&
data
()
{
...
...
@@ -58,12 +59,9 @@ public:
protected:
void
start
()
override
{
// initialize workers vector
auto
num
=
num_workers
();
workers_
.
reserve
(
num
);
for
(
size_t
i
=
0
;
i
<
num
;
++
i
)
workers_
.
emplace_back
(
new
worker_type
(
i
,
this
,
max_throughput_
));
policy_
.
template
create_workers
<
coordinator
<
Policy
>,
worker_type
>
(
this
,
num_workers
(),
max_throughput_
);
// start all workers now that all workers have been initialized
for
(
auto
&
w
:
workers_
)
for
(
auto
&
w
:
data_
.
workers
)
w
->
start
();
// run remaining startup code
super
::
start
();
...
...
@@ -99,7 +97,7 @@ protected:
std
::
set
<
worker_type
*>
alive_workers
;
auto
num
=
num_workers
();
for
(
size_t
i
=
0
;
i
<
num
;
++
i
)
{
alive_workers
.
insert
(
worker_by_id
(
i
));
alive_workers
.
insert
(
worker_by_id
x
(
i
));
sh
.
ref
();
// make sure reference count is high enough
}
while
(
!
alive_workers
.
empty
())
{
...
...
@@ -116,12 +114,12 @@ protected:
// shutdown utility actors
stop_actors
();
// wait until all workers are done
for
(
auto
&
w
:
workers_
)
{
for
(
auto
&
w
:
data_
.
workers
)
{
w
->
get_thread
().
join
();
}
// run cleanup code for each resumable
auto
f
=
&
abstract_coordinator
::
cleanup_and_release
;
for
(
auto
&
w
:
workers_
)
for
(
auto
&
w
:
data_
.
workers
)
policy_
.
foreach_resumable
(
w
.
get
(),
f
);
policy_
.
foreach_central_resumable
(
this
,
f
);
}
...
...
@@ -131,8 +129,6 @@ protected:
}
private:
// usually of size std::thread::hardware_concurrency()
std
::
vector
<
std
::
unique_ptr
<
worker_type
>>
workers_
;
// policy-specific data
policy_data
data_
;
// instance of our policy object
...
...
libcaf_core/caf/scheduler/worker.hpp
View file @
59c6e837
...
...
@@ -40,7 +40,7 @@ class worker : public execution_unit {
public:
using
job_ptr
=
resumable
*
;
using
coordinator_ptr
=
coordinator
<
Policy
>*
;
using
policy_data
=
typename
Policy
::
worker_data
;
using
policy_data
=
typename
Policy
::
template
worker_data
<
worker
<
Policy
>
>
;
worker
(
size_t
worker_id
,
coordinator_ptr
worker_parent
,
size_t
throughput
)
:
execution_unit
(
&
worker_parent
->
system
()),
...
...
@@ -106,6 +106,7 @@ public:
private:
void
run
()
{
CAF_SET_LOGGER_SYS
(
&
system
());
policy_
.
init_worker_thread
(
this
);
// scheduling loop
for
(;;)
{
auto
job
=
policy_
.
dequeue
(
this
);
...
...
libcaf_core/src/actor_system.cpp
View file @
59c6e837
...
...
@@ -29,6 +29,7 @@
#include "caf/policy/work_sharing.hpp"
#include "caf/policy/work_stealing.hpp"
#include "caf/policy/numa_aware_work_stealing.hpp"
#include "caf/scheduler/coordinator.hpp"
#include "caf/scheduler/test_coordinator.hpp"
...
...
@@ -223,6 +224,7 @@ actor_system::actor_system(actor_system_config& cfg)
using
test
=
scheduler
::
test_coordinator
;
using
share
=
scheduler
::
coordinator
<
policy
::
work_sharing
>
;
using
steal
=
scheduler
::
coordinator
<
policy
::
work_stealing
>
;
using
numa_steal
=
scheduler
::
coordinator
<
policy
::
numa_aware_work_stealing
>
;
using
profiled_share
=
scheduler
::
profiled_coordinator
<
policy
::
work_sharing
>
;
using
profiled_steal
=
scheduler
::
profiled_coordinator
<
policy
::
work_stealing
>
;
// set scheduler only if not explicitly loaded by user
...
...
@@ -231,29 +233,37 @@ actor_system::actor_system(actor_system_config& cfg)
stealing
=
0x0001
,
sharing
=
0x0002
,
testing
=
0x0003
,
numa_stealing
=
0x0004
,
profiled
=
0x0100
,
profiled_stealing
=
0x0101
,
profiled_sharing
=
0x0102
};
sched_conf
sc
=
stealing
;
if
(
cfg
.
scheduler_policy
==
atom
(
"sharing"
))
sched_conf
sc
=
numa_stealing
;
if
(
cfg
.
scheduler_policy
==
atom
(
"stealing"
))
sc
=
stealing
;
else
if
(
cfg
.
scheduler_policy
==
atom
(
"sharing"
))
sc
=
sharing
;
else
if
(
cfg
.
scheduler_policy
==
atom
(
"testing"
))
sc
=
testing
;
else
if
(
cfg
.
scheduler_policy
!=
atom
(
"stealing"
))
std
::
cerr
<<
"[WARNING] "
<<
deep_to_string
(
cfg
.
scheduler_policy
)
<<
" is an unrecognized scheduler pollicy, "
"falling back to 'stealing' (i.e. work-stealing)"
<<
std
::
endl
;
else
if
(
cfg
.
scheduler_policy
!=
atom
(
"numa-steal"
))
std
::
cerr
<<
"[WARNING] "
<<
deep_to_string
(
cfg
.
scheduler_policy
)
<<
" is an unrecognized scheduler pollicy, "
"falling back to 'numa-steal' (i.e. numa aware work-stealing)"
<<
std
::
endl
;
if
(
cfg
.
scheduler_enable_profiling
)
sc
=
static_cast
<
sched_conf
>
(
sc
|
profiled
);
switch
(
sc
)
{
default:
// any invalid configuration falls back to work stealing
sched
.
reset
(
new
steal
(
*
this
));
default:
// any invalid configuration falls back to
numa
work stealing
sched
.
reset
(
new
numa_
steal
(
*
this
));
break
;
case
sharing
:
sched
.
reset
(
new
share
(
*
this
));
break
;
case
stealing
:
sched
.
reset
(
new
steal
(
*
this
));
break
;
case
profiled_stealing
:
sched
.
reset
(
new
profiled_steal
(
*
this
));
break
;
...
...
libcaf_core/src/actor_system_config.cpp
View file @
59c6e837
...
...
@@ -101,7 +101,7 @@ actor_system_config::actor_system_config()
slave_mode
(
false
),
slave_mode_fun
(
nullptr
)
{
// (1) hard-coded defaults
scheduler_policy
=
atom
(
"
stealing
"
);
scheduler_policy
=
atom
(
"
numa-steal
"
);
scheduler_max_threads
=
std
::
max
(
std
::
thread
::
hardware_concurrency
(),
unsigned
{
4
});
scheduler_max_throughput
=
std
::
numeric_limits
<
size_t
>::
max
();
...
...
@@ -328,7 +328,7 @@ actor_system_config& actor_system_config::parse(message& args,
atom
(
"asio"
)
# endif
},
middleman_network_backend
,
"middleman.network-backend"
);
verify_atom_opt
({
atom
(
"stealing"
),
atom
(
"sharing"
)},
verify_atom_opt
({
atom
(
"stealing"
),
atom
(
"sharing"
)
,
atom
(
"numa-steal"
)
},
scheduler_policy
,
"scheduler.policy "
);
if
(
res
.
opts
.
count
(
"caf#dump-config"
)
!=
0u
)
{
cli_helptext_printed
=
true
;
...
...
libcaf_core/src/numa_aware_work_stealing.cpp
0 → 100644
View file @
59c6e837
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/policy/numa_aware_work_stealing.hpp"
namespace
caf
{
namespace
policy
{
numa_aware_work_stealing
::~
numa_aware_work_stealing
()
{
// nop
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
s
,
const
numa_aware_work_stealing
::
hwloc_bitmap_wrapper
&
w
)
{
char
*
tmp
=
nullptr
;
hwloc_bitmap_asprintf
(
&
tmp
,
w
.
get
());
s
<<
std
::
string
(
tmp
);
free
(
tmp
);
return
s
;
}
}
// namespace policy
}
// namespace caf
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