2 // Copyright Oliver Kowalke 2015.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // based on https://github.com/atemerev/skynet from Alexander Temerev
13 #include <condition_variable>
26 #include <boost/fiber/all.hpp>
27 #include <boost/fiber/numa/topology.hpp>
28 #include <boost/predef.h>
30 #include "../barrier.hpp"
32 using clock_type
= std::chrono::steady_clock
;
33 using duration_type
= clock_type::duration
;
34 using time_point_type
= clock_type::time_point
;
35 using channel_type
= boost::fibers::buffered_channel
< std::uint64_t >;
36 using allocator_type
= boost::fibers::fixedsize_stack
;
37 using lock_type
= std::unique_lock
< std::mutex
>;
39 static bool done
= false;
40 static std::mutex mtx
{};
41 static boost::fibers::condition_variable_any cnd
{};
43 std::uint32_t hardware_concurrency( std::vector
< boost::fibers::numa::node
> const& topo
) {
44 std::uint32_t cpus
= 0;
45 for ( auto & node
: topo
) {
46 cpus
+= node
.logical_cpus
.size();
52 void skynet( allocator_type
& salloc
, channel_type
& c
, std::size_t num
, std::size_t size
, std::size_t div
) {
56 channel_type rc
{ 16 };
57 for ( std::size_t i
= 0; i
< div
; ++i
) {
58 auto sub_num
= num
+ i
* size
/ div
;
59 boost::fibers::fiber
{ boost::fibers::launch::dispatch
,
60 std::allocator_arg
, salloc
,
62 std::ref( salloc
), std::ref( rc
), sub_num
, size
/ div
, div
}.detach();
64 std::uint64_t sum
{ 0 };
65 for ( std::size_t i
= 0; i
< div
; ++i
) {
66 sum
+= rc
.value_pop();
72 void thread( std::uint32_t cpu_id
, std::uint32_t node_id
, std::vector
< boost::fibers::numa::node
> const& topo
, barrier
* b
) {
73 boost::fibers::use_scheduling_algorithm
< boost::fibers::algo::numa::work_stealing
>( cpu_id
, node_id
, topo
);
76 cnd
.wait( lk
, [](){ return done
; });
82 std::vector
< boost::fibers::numa::node
> topo
= boost::fibers::numa::topology();
84 auto main_cpu_id
= * node
.logical_cpus
.begin();
85 boost::fibers::use_scheduling_algorithm
< boost::fibers::algo::numa::work_stealing
>( main_cpu_id
, node
.id
, topo
);
86 barrier b
{ hardware_concurrency( topo
) };
87 std::size_t size
{ 1000000 };
88 std::size_t div
{ 10 };
89 // Windows 10 and FreeBSD require a fiber stack of 8kb
90 // otherwise the stack gets exhausted
91 // stack requirements must be checked for other OS too
92 #if BOOST_OS_WINDOWS || BOOST_OS_BSD
93 allocator_type salloc
{ 2*allocator_type::traits_type::page_size() };
95 allocator_type salloc
{ allocator_type::traits_type::page_size() };
97 std::uint64_t result
{ 0 };
99 std::vector
< std::thread
> threads
;
100 for ( auto & node
: topo
) {
101 for ( std::uint32_t cpu_id
: node
.logical_cpus
) {
102 // exclude main-thread
103 if ( main_cpu_id
!= cpu_id
) {
104 threads
.emplace_back( thread
, cpu_id
, node
.id
, std::cref( topo
), & b
);
109 time_point_type start
{ clock_type::now() };
110 skynet( salloc
, rc
, 0, size
, div
);
111 result
= rc
.value_pop();
112 if ( 499999500000 != result
) {
113 throw std::runtime_error("invalid result");
115 auto duration
= clock_type::now() - start
;
120 for ( std::thread
& t
: threads
) {
123 std::cout
<< "duration: " << duration
.count() / 1000000 << " ms" << std::endl
;
125 } catch ( std::exception
const& e
) {
126 std::cerr
<< "exception: " << e
.what() << std::endl
;
128 std::cerr
<< "unhandled exception" << std::endl
;