diff --git a/doc/attributes.qbk b/doc/attributes.qbk deleted file mode 100644 index 194d910..0000000 --- a/doc/attributes.qbk +++ /dev/null @@ -1,107 +0,0 @@ -[/ - Copyright Oliver Kowalke 2014. - Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt -] - -[section:attributes Attributes] - -Class `attributes` is used to specify parameters required to setup a -coroutine's context. - - enum flag_unwind_t - { - stack_unwind, - no_stack_unwind - }; - - enum flag_fpu_t - { - fpu_preserved, - fpu_not_preserved - }; - - struct attributes - { - std::size_t size; - flag_unwind_t do_unwind; - flag_fpu_t preserve_fpu; - - attributes() noexcept; - - explicit attributes( std::size_t size_) noexcept; - - explicit attributes( flag_unwind_t do_unwind_) noexcept; - - explicit attributes( flag_fpu_t preserve_fpu_) noexcept; - - explicit attributes( std::size_t size_, flag_unwind_t do_unwind_) noexcept; - - explicit attributes( std::size_t size_, flag_fpu_t preserve_fpu_) noexcept; - - explicit attributes( flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept; - - explicit attributes( std::size_t size_, flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept; - }; - -[heading `attributes()`] -[variablelist -[[Effects:] [Default constructor using `boost::context::default_stacksize()`, does unwind -the stack after coroutine/generator is complete and preserves FPU registers.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( std::size_t size)`] -[variablelist -[[Effects:] [Argument `size` defines stack size of the new coroutine. -Stack unwinding after termination and preserving FPU registers is set by -default.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( flag_unwind_t do_unwind)`] -[variablelist -[[Effects:] [Argument `do_unwind` determines if stack will be unwound after -termination or not. The default stacksize is used for the new coroutine -and FPU registers are preserved.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( flag_fpu_t preserve_fpu)`] -[variablelist -[[Effects:] [Argument `preserve_fpu` determines if FPU register have to be -preserved across context switches. The default stacksize is used for the -new coroutine and its stack will be unwound after termination.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( std::size_t size, flag_unwind_t do_unwind)`] -[variablelist -[[Effects:] [Arguments `size` and `do_unwind` are given by the user. -FPU registers are preserved across each context switch.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( std::size_t size, flag_fpu_t preserve_fpu)`] -[variablelist -[[Effects:] [Arguments `size` and `preserve_fpu` are given by the user. -The stack is automatically unwound after coroutine/generator terminates.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`] -[variablelist -[[Effects:] [Arguments `do_unwind` and `preserve_fpu` are given by the user. -The stack gets a default value of `boost::context::default_stacksize()`.]] -[[Throws:] [Nothing.]] -] - -[heading `attributes( std::size_t size, flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`] -[variablelist -[[Effects:] [Arguments `size`, `do_unwind` and `preserve_fpu` are given by the -user.]] -[[Throws:] [Nothing.]] -] - -[endsect] diff --git a/doc/coro.qbk b/doc/coro.qbk index 9192ac7..4821850 100644 --- a/doc/coro.qbk +++ b/doc/coro.qbk @@ -46,7 +46,7 @@ [def __coro_ns__ ['boost::coroutines2]] [def __econtext__ ['execution_context]] [def __end__ ['std::end()]] -[def __fcontext__ ['boost::contexts::fcontext_t]] +[def __fcontext__ [@boost:/libs/context/doc/html/context/cc.html#implementation fcontext_t]] [def __fetch__ ['inbuf::fetch()]] [def __fixedsize__ ['fixedsize_stack]] [def __forced_unwind__ ['detail::forced_unwind]] @@ -73,6 +73,9 @@ [def __standard_allocator__ ['fixedsize]] [def __start__ ['session::start()]] [def __terminate__ ['std::terminate()]] +[def __ucontext__ ['ucontext_t]] +[def __winfib__ ['WinFiber]] +[def __cc__ ['call/cc]] [def __underflow__ ['stream_buf::underflow()]] [def __yield_context__ ['boost::asio::yield_context]] diff --git a/doc/coroutine.qbk b/doc/coroutine.qbk index 4ecf944..0ab8153 100644 --- a/doc/coroutine.qbk +++ b/doc/coroutine.qbk @@ -57,7 +57,7 @@ stack (macro SIGSTKSZ on POSIX). At this time of writing only GCC (4.7) [footnote [@http://gcc.gnu.org/wiki/SplitStacks Ian Lance Taylor, Split Stacks in GCC]] is known to support segmented stacks. With version 1.54 __boost_coroutine__ -provides support for segmented stacks. +provides support for [link segmented ['segmented stacks]]. The destructor releases the associated stack. The implementer is free to deallocate the stack or to cache it for later usage. @@ -67,15 +67,6 @@ deallocate the stack or to cache it for later usage. A coroutine saves and restores registers according to the underlying ABI on each context switch (using __boost_context__). -Some applications do not use floating-point registers and can disable preserving -FPU registers for performance reasons. - -[note According to the calling convention the FPU registers are preserved by -default.] - -On POSIX systems, the coroutine context switch does not preserve signal masks -for performance reasons. - A context switch is done via __push_coro_op__ and __pull_coro_op__. [warning Calling __push_coro_op__ and __pull_coro_op__ from inside the [_same] @@ -85,11 +76,48 @@ As an example, the code below will result in undefined behaviour: boost::coroutines2::coroutine::push_type coro( [&](boost::coroutines2::coroutine::pull_type& yield){ - coro(); + yield(); }); coro(); [include asymmetric.qbk] + +[section Implementations: fcontext_t, ucontext_t and WinFiber] + +[heading fcontext_t] + +The implementation uses __fcontext__ per default. fcontext_t is based on +assembler and not available for all platforms. It provides a much better +performance than __ucontext__ +(the context switch takes two magnitudes of order less CPU cycles) and __winfib__. + + +[heading ucontext_t] + +As an alternative, [@https://en.wikipedia.org/wiki/Setcontext __ucontext__] +can be used by compiling with `BOOST_USE_UCONTEXT` and b2 property `context-impl=ucontext`. +__ucontext__ might be available on a broader range of POSIX-platforms but has +some (for instance deprecated since POSIX.1-2003, not C99 conform). + +[note __cc__ supports [link segmented ['Segmented stacks]] only with +__ucontext__ as its implementation.] + + +[heading WinFiber] + +With `BOOST_USE_WINFIB` and b2 property `context-impl=winfib` Win32-Fibers are used +as implementation for __cc__. + +Because the TIB (thread information block) is not fully described in the MSDN, +it might be possible that not all required TIB-parts are swapped. + +[note The first call of __cc__ converts the thread into a Windows fiber by +invoking `ConvertThreadToFiber()`. If desired, `ConvertFiberToThread()` has +to be called by the user explicitly in order to release resources allocated +by `ConvertThreadToFiber()` (e.g. after using boost.context). ] + +[endsect] + [endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk index 52d3a3e..34b4e84 100644 --- a/doc/intro.qbk +++ b/doc/intro.qbk @@ -15,7 +15,7 @@ always before the parent. Coroutines (the term was introduced by Melvin Conway [footnote Conway, Melvin E.. "Design of a Separable Transition-Diagram Compiler". Commun. ACM, Volume 6 Issue 7, July 1963, Article No. 7]), are a generalization of routines (Donald Knuth [footnote Knuth, Donald Ervin (1997). -"Fundamental Algorithms. The Art of Computer Programming 1", (3rd ed.)]. +"Fundamental Algorithms. The Art of Computer Programming 1", (3rd ed.)]). The principal difference between coroutines and routines is that a coroutine enables explicit suspend and resume of its progress via additional operations by preserving execution state and thus provides an diff --git a/doc/performance.qbk b/doc/performance.qbk index 55a3547..9d9d6ae 100644 --- a/doc/performance.qbk +++ b/doc/performance.qbk @@ -7,24 +7,18 @@ [section:performance Performance] -Performance of __boost_coroutine__ was measured on the platforms shown in the -following table. Performance measurements were taken using `rdtsc` and -`boost::chrono::high_resolution_clock`, with overhead corrections, on x86 -platforms. In each case, cache warm-up was accounted for, and the one -running thread was pinned to a single CPU. +Performance measurements were taken using `std::chrono::highresolution_clock`, +with overhead corrections. +The code was compiled with gcc-6.3.1, using build options: +variant = release, optimization = speed. +Tests were executed on dual Intel XEON E5 2620 2.2GHz, 16C/32T, 64GB RAM, +running Linux (x86_64). -[table Performance of asymmetric coroutines (using execution_context) +[table Performance of context switch + [[using __fcontext__] [using [@boost:/libs/context/doc/html/context/cc.html#implementation ucontext_t]]] [ - [Platform] - [switch] - [construction (protected stack-allocator)] - [construction (standard stack-allocator)] - ] - [ - [x86_64 [footnote Intel Core i7-4770S 3.10GHz]] - [16 ns / 50 cycles] - [7 \u00b5s / 22133 cycles] - [1.7 \u00b5s / 5383 cycles] + [26 ns / 56 CPU cycles] + [542 ns / 1146 CPU cycles] ] ] diff --git a/doc/stack.qbk b/doc/stack.qbk index 03f2a0a..a41dcb2 100644 --- a/doc/stack.qbk +++ b/doc/stack.qbk @@ -183,6 +183,7 @@ address of the stack.]] [endsect] +[#segmented] [section:segmented Class ['segmented_stack]] __boost_coroutine__ supports usage of a __segmented__, e. g. the size of @@ -193,11 +194,13 @@ In contrast to __protected_fixedsize__ and __fixedsize__ it creates a stack which grows on demand. [note Segmented stacks are currently only supported by [*gcc] from version -[*4.7] and [*clang] from version [*3.4] onwards. In order to use a -__segmented_stack__ __boost_coroutine__ must be built with -property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] at b2/bjam +[*4.7] [*clang] from version [*3.4] onwards. In order to use a +__segmented_stack__ __boost_context__ must be built with +property `segmented-stacks`, e.g. [*toolset=gcc segmented-stacks=on] and +applying `BOOST_USE_SEGMENTED_STACKS` and `BOOST_USE_UCONTEXT` at b2/bjam command line.] + #include struct segmented_stack { diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 75fd688..67bd232 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -40,6 +40,6 @@ exe parser : parser.cpp ; -#exe segmented -# : segmented.cpp -# ; +exe segmented + : segmented.cpp + ; diff --git a/example/segmented.cpp b/example/segmented.cpp index a6ba3f8..0728055 100644 --- a/example/segmented.cpp +++ b/example/segmented.cpp @@ -6,8 +6,7 @@ #include #include - -#include +#include #include @@ -16,17 +15,13 @@ __declspec(noinline) void access( char *buf); #else // GCC void access( char *buf) __attribute__ ((noinline)); #endif -void access( char *buf) -{ +void access( char *buf) { buf[0] = '\0'; } -void bar( int i) -{ +void bar( int i) { char buf[4 * 1024]; - - if ( i > 0) - { + if ( i > 0) { access( buf); std::cout << i << ". iteration" << std::endl; bar( i - 1); @@ -35,20 +30,19 @@ void bar( int i) int main() { int count = 384; - +#if defined(BOOST_USE_SEGMENTED_STACKS) std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl; std::cout << "application should not fail" << std::endl; - - boost::coroutines2::coroutine< void >::push_type sink( - [&]( boost::coroutines2::coroutine< void >::pull_type & source) { +#else + std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::context::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application might fail" << std::endl; +#endif + boost::coroutines2::coroutine< void >::pull_type coro{ + [count](boost::coroutines2::coroutine< void >::push_type & coro){ bar( count); - source(); - }); - - sink(); - - std::cout << "main: Done" << std::endl; - - return 0; + }}; + std::cout << "main: done" << std::endl; + return EXIT_SUCCESS; } diff --git a/include/boost/coroutine2/detail/coroutine.hpp b/include/boost/coroutine2/detail/coroutine.hpp index 1170590..c52608b 100644 --- a/include/boost/coroutine2/detail/coroutine.hpp +++ b/include/boost/coroutine2/detail/coroutine.hpp @@ -29,26 +29,14 @@ class push_coroutine; #include #include -#if defined(BOOST_EXECUTION_CONTEXT) -# if (BOOST_EXECUTION_CONTEXT==1) -# include -# include -# else -# include -# include -# endif +#include +#include -# include -# include +#include +#include -# if (BOOST_EXECUTION_CONTEXT==1) -# include -# include -# else -# include -# include -# endif -#endif +#include +#include #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX diff --git a/include/boost/coroutine2/detail/pull_control_block_ecv1.hpp b/include/boost/coroutine2/detail/pull_control_block_ecv1.hpp deleted file mode 100644 index 16d65a3..0000000 --- a/include/boost/coroutine2/detail/pull_control_block_ecv1.hpp +++ /dev/null @@ -1,119 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP -#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP - -#include -#include -#include - -#include -#include - -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace coroutines2 { -namespace detail { - -template< typename T > -struct pull_coroutine< T >::control_block { - boost::context::execution_context ctx; - typename push_coroutine< T >::control_block * other; - state_t state; - std::exception_ptr except; - bool bvalid; - typename std::aligned_storage< sizeof( T), alignof( T) >::type storage; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( typename push_coroutine< T >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume(); - - void set( T *); - - T & get() noexcept; - - bool valid() const noexcept; -}; - -template< typename T > -struct pull_coroutine< T & >::control_block { - boost::context::execution_context ctx; - typename push_coroutine< T & >::control_block * other; - state_t state; - std::exception_ptr except; - T * t; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( typename push_coroutine< T & >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume(); - - T & get() noexcept; - - bool valid() const noexcept; -}; - -struct pull_coroutine< void >::control_block { - boost::context::execution_context ctx; - push_coroutine< void >::control_block * other; - state_t state; - std::exception_ptr except; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( push_coroutine< void >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume(); - - bool valid() const noexcept; -}; - -}}} - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP diff --git a/include/boost/coroutine2/detail/pull_control_block_ecv1.ipp b/include/boost/coroutine2/detail/pull_control_block_ecv1.ipp deleted file mode 100644 index 7786604..0000000 --- a/include/boost/coroutine2/detail/pull_control_block_ecv1.ipp +++ /dev/null @@ -1,431 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP -#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP - -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace coroutines2 { -namespace detail { - -// pull_coroutine< T > - -template< typename T > -void -pull_coroutine< T >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename T > -template< typename StackAllocator, typename Fn > -pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; - push_coroutine< T > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< T >::control_block synthesized_cb{ this, ctx }; - push_coroutine< T > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{}, - bvalid{ false }, - storage{} { - // enter coroutine-fn in order to have first value available after ctor (of `*this`) returns - set( static_cast< T * >( ctx() ) ); -} - -template< typename T > -pull_coroutine< T >::control_block::control_block( typename push_coroutine< T >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{}, - bvalid{ false }, - storage{} { -} - -template< typename T > -pull_coroutine< T >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } - // destroy data if it set - if ( bvalid) { - reinterpret_cast< T * >( std::addressof( storage) )->~T(); - } -} - -template< typename T > -void -pull_coroutine< T >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -template< typename T > -void -pull_coroutine< T >::control_block::resume() { - other->ctx = boost::context::execution_context::current(); - set( static_cast< T * >( ctx() ) ); - if ( except) { - std::rethrow_exception( except); - } -} - -template< typename T > -void -pull_coroutine< T >::control_block::set( T * t) { - // destroy data if it set - if ( bvalid) { - reinterpret_cast< T * >( std::addressof( storage) )->~T(); - } - if ( nullptr != t) { - ::new ( static_cast< void * >( std::addressof( storage) ) ) T( std::move( * t) ); - bvalid = true; - } else { - bvalid = false; - } -} - -template< typename T > -T & -pull_coroutine< T >::control_block::get() noexcept { - return * reinterpret_cast< T * >( std::addressof( storage) ); -} - -template< typename T > -bool -pull_coroutine< T >::control_block::valid() const noexcept { - return nullptr != other && state_t::none == ( state & state_t::complete) && bvalid; -} - - -// pull_coroutine< T & > - -template< typename T > -void -pull_coroutine< T & >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename T > -template< typename StackAllocator, typename Fn > -pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; - push_coroutine< T & > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< T & >::control_block synthesized_cb{ this, ctx }; - push_coroutine< T & > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{}, - t{ nullptr } { - // enter coroutine-fn in order to have first value available after ctor (of `*this`) returns - t = static_cast< T * >( ctx() ); -} - -template< typename T > -pull_coroutine< T & >::control_block::control_block( typename push_coroutine< T & >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{}, - t( nullptr) { -} - -template< typename T > -pull_coroutine< T & >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } -} - -template< typename T > -void -pull_coroutine< T & >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -template< typename T > -void -pull_coroutine< T & >::control_block::resume() { - other->ctx = boost::context::execution_context::current(); - t = static_cast< T * >( ctx() ); - if ( except) { - std::rethrow_exception( except); - } -} - -template< typename T > -T & -pull_coroutine< T & >::control_block::get() noexcept { - return * static_cast< T * >( t); -} - -template< typename T > -bool -pull_coroutine< T & >::control_block::valid() const noexcept { - return nullptr != other && state_t::none == ( state & state_t::complete) && nullptr != t; -} - - -// pull_coroutine< void > - -inline -void -pull_coroutine< void >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename StackAllocator, typename Fn > -pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; - push_coroutine< void > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized push_coroutine< T > - typename push_coroutine< void >::control_block synthesized_cb{ this, ctx }; - push_coroutine< void > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - auto fn = std::move( fn_); - // call coroutine-fn with synthesized push_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "pull_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{} { - // enter coroutine-fn in order to have first value available after ctor returns - ctx(); -} - -inline -pull_coroutine< void >::control_block::control_block( push_coroutine< void >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{} { -} - -inline -pull_coroutine< void >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } -} - -inline -void -pull_coroutine< void >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -inline -void -pull_coroutine< void >::control_block::resume() { - other->ctx = boost::context::execution_context::current(); - ctx(); - if ( except) { - std::rethrow_exception( except); - } -} - -inline -bool -pull_coroutine< void >::control_block::valid() const noexcept { - return nullptr != other && state_t::none == ( state & state_t::complete); -} - -}}} - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP diff --git a/include/boost/coroutine2/detail/push_control_block_ecv1.hpp b/include/boost/coroutine2/detail/push_control_block_ecv1.hpp deleted file mode 100644 index 29d4971..0000000 --- a/include/boost/coroutine2/detail/push_control_block_ecv1.hpp +++ /dev/null @@ -1,111 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP -#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP - -#include -#include - -#include -#include - -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace coroutines2 { -namespace detail { - -template< typename T > -struct push_coroutine< T >::control_block { - boost::context::execution_context ctx; - typename pull_coroutine< T >::control_block * other; - state_t state; - std::exception_ptr except; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( typename pull_coroutine< T >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume( T const&); - - void resume( T &&); - - bool valid() const noexcept; -}; - -template< typename T > -struct push_coroutine< T & >::control_block { - boost::context::execution_context ctx; - typename pull_coroutine< T & >::control_block * other; - state_t state; - std::exception_ptr except; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( typename pull_coroutine< T & >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume( T &); - - bool valid() const noexcept; -}; - -struct push_coroutine< void >::control_block { - boost::context::execution_context ctx; - pull_coroutine< void >::control_block * other; - state_t state; - std::exception_ptr except; - - static void destroy( control_block * cb) noexcept; - - template< typename StackAllocator, typename Fn > - control_block( context::preallocated, StackAllocator, Fn &&); - - control_block( pull_coroutine< void >::control_block *, boost::context::execution_context const&) noexcept; - - ~control_block() noexcept; - - control_block( control_block &) = delete; - control_block & operator=( control_block &) = delete; - - void deallocate() noexcept; - - void resume(); - - bool valid() const noexcept; -}; - -}}} - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP diff --git a/include/boost/coroutine2/detail/push_control_block_ecv1.ipp b/include/boost/coroutine2/detail/push_control_block_ecv1.ipp deleted file mode 100644 index 8b4ae3f..0000000 --- a/include/boost/coroutine2/detail/push_control_block_ecv1.ipp +++ /dev/null @@ -1,428 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP -#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_PREFIX -#endif - -namespace boost { -namespace coroutines2 { -namespace detail { - -// push_coroutine< T > - -template< typename T > -void -push_coroutine< T >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename T > -template< typename StackAllocator, typename Fn > -push_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void * vp) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< T > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - T * t = static_cast< T * >( ctx() ); - // set transferred value - synthesized_cb.set( t); - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< T >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< T > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - T * t = static_cast< T * >( ctx() ); - // set transferred value - synthesized_cb.set( t); - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{} { - // enter coroutine-fn in order to get other set - ctx(); -} - -template< typename T > -push_coroutine< T >::control_block::control_block( typename pull_coroutine< T >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{} { -} - -template< typename T > -push_coroutine< T >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } -} - -template< typename T > -void -push_coroutine< T >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -template< typename T > -void -push_coroutine< T >::control_block::resume( T const& t) { - other->ctx = boost::context::execution_context::current(); - // pass an pointer to other context - ctx( const_cast< T * >( & t) ); - if ( except) { - std::rethrow_exception( except); - } -} - -template< typename T > -void -push_coroutine< T >::control_block::resume( T && t) { - other->ctx = boost::context::execution_context::current(); - // pass an pointer to other context - ctx( std::addressof( t) ); - if ( except) { - std::rethrow_exception( except); - } -} - -template< typename T > -bool -push_coroutine< T >::control_block::valid() const noexcept { - return state_t::none == ( state & state_t::complete ); -} - - -// push_coroutine< T & > - -template< typename T > -void -push_coroutine< T & >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename T > -template< typename StackAllocator, typename Fn > -push_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, - Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, void * vp) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< T & > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - T * t = static_cast< T * >( ctx() ); - // set transferred value - synthesized_cb.t = t; - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< T & >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< T & > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - T * t = static_cast< T * >( ctx() ); - // set transferred value - synthesized_cb.t = t; - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{} { - // enter coroutine-fn in order to get other set - ctx(); -} - -template< typename T > -push_coroutine< T & >::control_block::control_block( typename pull_coroutine< T & >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{} { -} - -template< typename T > -push_coroutine< T & >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } -} - -template< typename T > -void -push_coroutine< T & >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -template< typename T > -void -push_coroutine< T & >::control_block::resume( T & t) { - other->ctx = boost::context::execution_context::current(); - // pass an pointer to other context - ctx( const_cast< typename std::remove_const< T >::type * >( std::addressof( t) ) ); - if ( except) { - std::rethrow_exception( except); - } -} - -template< typename T > -bool -push_coroutine< T & >::control_block::valid() const noexcept { - return state_t::none == ( state & state_t::complete ); -} - - -// push_coroutine< void > - -inline -void -push_coroutine< void >::control_block::destroy( control_block * cb) noexcept { - boost::context::execution_context ctx = cb->ctx; - // destroy control structure - cb->state |= state_t::destroy; - cb->~control_block(); -} - -template< typename StackAllocator, typename Fn > -push_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, Fn && fn) : -#if defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) - ctx{ std::allocator_arg, palloc, salloc, - wrap( [this]( typename std::decay< Fn >::type & fn_, boost::context::execution_context & ctx, - void * vp) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< void >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< void > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - ctx(); - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }, - std::forward< Fn >( fn), - boost::context::execution_context::current() ) }, -#else - ctx{ std::allocator_arg, palloc, salloc, - [this,fn_=decay_copy( std::forward< Fn >( fn) ),ctx=boost::context::execution_context::current()] (void *) mutable noexcept { - // create synthesized pull_coroutine< T > - typename pull_coroutine< void >::control_block synthesized_cb{ this, ctx }; - pull_coroutine< void > synthesized{ & synthesized_cb }; - other = & synthesized_cb; - if ( state_t::none == ( state & state_t::destroy) ) { - try { - // jump back to ctor - ctx(); - auto fn = std::move( fn_); - // call coroutine-fn with synthesized pull_coroutine as argument - fn( synthesized); - } catch ( forced_unwind const&) { - // do nothing for unwinding exception - } catch (...) { - // store other exceptions in exception-pointer - except = std::current_exception(); - } - } - // set termination flags - state |= state_t::complete; - // jump back to ctx - other->ctx(); - BOOST_ASSERT_MSG( false, "push_coroutine is complete"); - }}, -#endif - other{ nullptr }, - state{ state_t::unwind }, - except{} { - // enter coroutine-fn in order to get other set - ctx(); -} - -inline -push_coroutine< void >::control_block::control_block( pull_coroutine< void >::control_block * cb, - boost::context::execution_context const& ctx_) noexcept : - ctx{ ctx_ }, - other{ cb }, - state{ state_t::none }, - except{} { -} - -inline -push_coroutine< void >::control_block::~control_block() { - if ( state_t::none == ( state & state_t::complete) && - state_t::none != ( state & state_t::unwind) ) { - // unwind coroutine stack - other->ctx = boost::context::execution_context::current(); - ctx( context::exec_ontop_arg, unwind_coroutine); - } -} - -inline -void -push_coroutine< void >::control_block::deallocate() noexcept { - if ( state_t::none != ( state & state_t::unwind) ) { - destroy( this); - } -} - -inline -void -push_coroutine< void >::control_block::resume() { - other->ctx = boost::context::execution_context::current(); - ctx(); - if ( except) { - std::rethrow_exception( except); - } -} - -inline -bool -push_coroutine< void >::control_block::valid() const noexcept { - return state_t::none == ( state & state_t::complete ); -} - -}}} - -#ifdef BOOST_HAS_ABI_HEADERS -# include BOOST_ABI_SUFFIX -#endif - -#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP diff --git a/include/boost/coroutine2/detail/wrap.hpp b/include/boost/coroutine2/detail/wrap.hpp index 25abce3..c628aa6 100644 --- a/include/boost/coroutine2/detail/wrap.hpp +++ b/include/boost/coroutine2/detail/wrap.hpp @@ -11,11 +11,7 @@ #include #include -#if (BOOST_EXECUTION_CONTEXT==1) -# include -#else -# include -#endif +#include #include #include @@ -28,45 +24,6 @@ namespace boost { namespace coroutines2 { namespace detail { -#if (BOOST_EXECUTION_CONTEXT==1) -template< typename Fn1, typename Fn2 > -class wrapper { -private: - typename std::decay< Fn1 >::type fn1_; - typename std::decay< Fn2 >::type fn2_; - boost::context::execution_context ctx_; - -public: - wrapper( Fn1 && fn1, Fn2 && fn2, - boost::context::execution_context const& ctx) : - fn1_( std::move( fn1) ), - fn2_( std::move( fn2) ), - ctx_{ ctx } { - } - - wrapper( wrapper const&) = delete; - wrapper & operator=( wrapper const&) = delete; - - wrapper( wrapper && other) = default; - wrapper & operator=( wrapper && other) = default; - - void operator()( void * vp) { - boost::context::detail::invoke( - std::move( fn1_), - fn2_, ctx_, vp); - } -}; - -template< typename Fn1, typename Fn2 > -wrapper< Fn1, Fn2 > -wrap( Fn1 && fn1, Fn2 && fn2, - boost::context::execution_context const& ctx) { - return wrapper< Fn1, Fn2 >( - std::forward< Fn1 >( fn1), - std::forward< Fn2 >( fn2), - ctx); -} -#else template< typename Fn1, typename Fn2 > class wrapper { private: @@ -101,7 +58,6 @@ wrap( Fn1 && fn1, Fn2 && fn2) { std::forward< Fn1 >( fn1), std::forward< Fn2 >( fn2) ); } -#endif }}} diff --git a/performance/Jamfile.v2 b/performance/Jamfile.v2 index fa38f05..b086090 100644 --- a/performance/Jamfile.v2 +++ b/performance/Jamfile.v2 @@ -61,17 +61,7 @@ alias sources explicit sources ; -exe performance_create_protected +exe performance : sources - performance_create_protected.cpp - ; - -exe performance_create_standard - : sources - performance_create_standard.cpp - ; - -exe performance_switch - : sources - performance_switch.cpp + performance.cpp ; diff --git a/performance/performance_create_standard.cpp b/performance/performance.cpp similarity index 71% rename from performance/performance_create_standard.cpp rename to performance/performance.cpp index 83f03e7..dcc916d 100644 --- a/performance/performance_create_standard.cpp +++ b/performance/performance.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -17,57 +18,52 @@ #include "clock.hpp" #include "cycle.hpp" -typedef boost::coroutines2::fixedsize_stack stack_allocator; -typedef boost::coroutines2::coroutine< void > coro_type; - boost::uint64_t jobs = 1000; -void fn( coro_type::pull_type & c) -{ while ( true) c(); } - -duration_type measure_time( duration_type overhead) -{ - stack_allocator stack_alloc; +void fn( boost::coroutines2::coroutine< void >::push_type & c) { + while ( true) { + c(); + } +} +duration_type measure_time_void( duration_type overhead) { + boost::coroutines2::coroutine< void >::pull_type c{ fn }; time_point_type start( clock_type::now() ); for ( std::size_t i = 0; i < jobs; ++i) { - coro_type::push_type c( stack_alloc, fn); + c(); } duration_type total = clock_type::now() - start; total -= overhead_clock(); // overhead of measurement total /= jobs; // loops + total /= 2; // 2x jump_fcontext return total; } # ifdef BOOST_CONTEXT_CYCLE -cycle_type measure_cycles( cycle_type overhead) -{ - stack_allocator stack_alloc; - +cycle_type measure_cycles_void( cycle_type overhead) { + boost::coroutines2::coroutine< void >::pull_type c{ fn }; cycle_type start( cycles() ); for ( std::size_t i = 0; i < jobs; ++i) { - coro_type::push_type c( stack_alloc, fn); + c(); } cycle_type total = cycles() - start; total -= overhead; // overhead of measurement total /= jobs; // loops + total /= 2; // 2x jump_fcontext return total; } # endif -int main( int argc, char * argv[]) -{ - try - { +int main( int argc, char * argv[]) { + try { bool bind = false; boost::program_options::options_description desc("allowed options"); desc.add_options() ("help", "help message") ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); - boost::program_options::variables_map vm; boost::program_options::store( boost::program_options::parse_command_line( @@ -76,28 +72,27 @@ int main( int argc, char * argv[]) desc), vm); boost::program_options::notify( vm); - if ( vm.count("help") ) { std::cout << desc << std::endl; return EXIT_SUCCESS; } - - if ( bind) bind_to_processor( 0); - + if ( bind) { + bind_to_processor( 0); + } duration_type overhead_c = overhead_clock(); - boost::uint64_t res = measure_time( overhead_c).count(); + boost::uint64_t res = measure_time_void( overhead_c).count(); std::cout << "average of " << res << " nano seconds" << std::endl; #ifdef BOOST_CONTEXT_CYCLE cycle_type overhead_y = overhead_cycle(); - res = measure_cycles( overhead_y); + res = measure_cycles_void( overhead_y); std::cout << "average of " << res << " cpu cycles" << std::endl; #endif - return EXIT_SUCCESS; } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } + catch ( std::exception const& e) { + std::cerr << "exception: " << e.what() << std::endl; + } catch (...) { + std::cerr << "unhandled exception" << std::endl; + } return EXIT_FAILURE; } diff --git a/performance/performance_create_protected.cpp b/performance/performance_create_protected.cpp deleted file mode 100644 index 050555e..0000000 --- a/performance/performance_create_protected.cpp +++ /dev/null @@ -1,103 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include - -#include -#include -#include -#include - -#include "bind_processor.hpp" -#include "clock.hpp" -#include "cycle.hpp" - -typedef boost::coroutines2::protected_fixedsize_stack stack_allocator; -typedef boost::coroutines2::coroutine< void > coro_type; - -boost::uint64_t jobs = 1000; - -void fn( coro_type::pull_type & c) -{ while ( true) c(); } - -duration_type measure_time( duration_type overhead) -{ - stack_allocator stack_alloc; - - time_point_type start( clock_type::now() ); - for ( std::size_t i = 0; i < jobs; ++i) { - coro_type::push_type c( stack_alloc, fn); - } - duration_type total = clock_type::now() - start; - total -= overhead_clock(); // overhead of measurement - total /= jobs; // loops - - return total; -} - -# ifdef BOOST_CONTEXT_CYCLE -cycle_type measure_cycles( cycle_type overhead) -{ - stack_allocator stack_alloc; - - cycle_type start( cycles() ); - for ( std::size_t i = 0; i < jobs; ++i) { - coro_type::push_type c( stack_alloc, fn); - } - cycle_type total = cycles() - start; - total -= overhead; // overhead of measurement - total /= jobs; // loops - - return total; -} -# endif - -int main( int argc, char * argv[]) -{ - try - { - bool bind = false; - boost::program_options::options_description desc("allowed options"); - desc.add_options() - ("help", "help message") - ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") - ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); - - boost::program_options::variables_map vm; - boost::program_options::store( - boost::program_options::parse_command_line( - argc, - argv, - desc), - vm); - boost::program_options::notify( vm); - - if ( vm.count("help") ) { - std::cout << desc << std::endl; - return EXIT_SUCCESS; - } - - if ( bind) bind_to_processor( 0); - - duration_type overhead_c = overhead_clock(); - boost::uint64_t res = measure_time( overhead_c).count(); - std::cout << "average of " << res << " nano seconds" << std::endl; -#ifdef BOOST_CONTEXT_CYCLE - cycle_type overhead_y = overhead_cycle(); - res = measure_cycles( overhead_y); - std::cout << "average of " << res << " cpu cycles" << std::endl; -#endif - - return EXIT_SUCCESS; - } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } - return EXIT_FAILURE; -} diff --git a/performance/performance_switch.cpp b/performance/performance_switch.cpp deleted file mode 100644 index 4de11cf..0000000 --- a/performance/performance_switch.cpp +++ /dev/null @@ -1,194 +0,0 @@ - -// Copyright Oliver Kowalke 2014. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "bind_processor.hpp" -#include "clock.hpp" -#include "cycle.hpp" - -boost::uint64_t jobs = 1000; - -struct X -{ - std::string str; - - X( std::string const& str_) : - str( str_) - {} -}; - -const X x("abc"); - -void fn_void( boost::coroutines2::coroutine< void >::push_type & c) -{ while ( true) c(); } - -void fn_int( boost::coroutines2::coroutine< int >::push_type & c) -{ while ( true) c( 7); } - -void fn_x( boost::coroutines2::coroutine< X >::push_type & c) -{ - while ( true) c( x); -} - -duration_type measure_time_void( duration_type overhead) -{ - boost::coroutines2::coroutine< void >::pull_type c( fn_void); - - time_point_type start( clock_type::now() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - duration_type total = clock_type::now() - start; - total -= overhead_clock(); // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} - -duration_type measure_time_int( duration_type overhead) -{ - boost::coroutines2::coroutine< int >::pull_type c( fn_int); - - time_point_type start( clock_type::now() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - duration_type total = clock_type::now() - start; - total -= overhead_clock(); // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} - -duration_type measure_time_x( duration_type overhead) -{ - boost::coroutines2::coroutine< X >::pull_type c( fn_x); - - time_point_type start( clock_type::now() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - duration_type total = clock_type::now() - start; - total -= overhead_clock(); // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} - -# ifdef BOOST_CONTEXT_CYCLE -cycle_type measure_cycles_void( cycle_type overhead) -{ - boost::coroutines2::coroutine< void >::pull_type c( fn_void); - - cycle_type start( cycles() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - cycle_type total = cycles() - start; - total -= overhead; // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} - -cycle_type measure_cycles_int( cycle_type overhead) -{ - boost::coroutines2::coroutine< int >::pull_type c( fn_int); - - cycle_type start( cycles() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - cycle_type total = cycles() - start; - total -= overhead; // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} - -cycle_type measure_cycles_x( cycle_type overhead) -{ - boost::coroutines2::coroutine< X >::pull_type c( fn_x); - - cycle_type start( cycles() ); - for ( std::size_t i = 0; i < jobs; ++i) { - c(); - } - cycle_type total = cycles() - start; - total -= overhead; // overhead of measurement - total /= jobs; // loops - total /= 2; // 2x jump_fcontext - - return total; -} -# endif - -int main( int argc, char * argv[]) -{ - try - { - bool bind = false; - boost::program_options::options_description desc("allowed options"); - desc.add_options() - ("help", "help message") - ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") - ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); - - boost::program_options::variables_map vm; - boost::program_options::store( - boost::program_options::parse_command_line( - argc, - argv, - desc), - vm); - boost::program_options::notify( vm); - - if ( vm.count("help") ) { - std::cout << desc << std::endl; - return EXIT_SUCCESS; - } - - if ( bind) bind_to_processor( 0); - - duration_type overhead_c = overhead_clock(); - boost::uint64_t res = measure_time_void( overhead_c).count(); - std::cout << "void: average of " << res << " nano seconds" << std::endl; - res = measure_time_int( overhead_c).count(); - std::cout << "int: average of " << res << " nano seconds" << std::endl; - res = measure_time_x( overhead_c).count(); - std::cout << "X: average of " << res << " nano seconds" << std::endl; -#ifdef BOOST_CONTEXT_CYCLE - cycle_type overhead_y = overhead_cycle(); - res = measure_cycles_void( overhead_y); - std::cout << "void: average of " << res << " cpu cycles" << std::endl; - res = measure_cycles_int( overhead_y); - std::cout << "int: average of " << res << " cpu cycles" << std::endl; - res = measure_cycles_x( overhead_y); - std::cout << "X: average of " << res << " cpu cycles" << std::endl; -#endif - - return EXIT_SUCCESS; - } - catch ( std::exception const& e) - { std::cerr << "exception: " << e.what() << std::endl; } - catch (...) - { std::cerr << "unhandled exception" << std::endl; } - return EXIT_FAILURE; -}