2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-02 08:52:07 +00:00

allow different stack-allocators

This commit is contained in:
Oliver Kowalke
2014-02-26 19:40:28 +01:00
parent 12d97c57cd
commit 7cf2cabc99
7 changed files with 255 additions and 149 deletions

View File

@@ -17,7 +17,7 @@ average computed over 1000 iterations.
[table Perfomance of coroutine switch
[[thread] [qthread] [tbb] [fiber]]
[[37114 ns] [577 ns] [309 ns] [1124 ns]]
[[37114 ns] [577 ns] [309 ns] [1063 ns]]
]

View File

@@ -13,9 +13,7 @@
#include <boost/assert.hpp>
#include <boost/atomic.hpp>
#include <boost/bind.hpp>
#include <boost/config.hpp>
#include <boost/coroutine/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/exception_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
@@ -42,8 +40,6 @@ namespace boost {
namespace fibers {
namespace detail {
namespace coro = boost::coroutines;
class BOOST_FIBERS_DECL fiber_base : public notify
{
public:
@@ -81,50 +77,18 @@ private:
fss_data_t fss_data_;
// set terminate is only set inside fiber_base::trampoline_()
void set_terminated_() BOOST_NOEXCEPT;
void trampoline_( coro::symmetric_coroutine< void >::yield_type &);
protected:
atomic< state_t > state_;
atomic< int > flags_;
atomic< int > priority_;
coro::symmetric_coroutine< void >::yield_type * callee_;
coro::symmetric_coroutine< void >::call_type caller_;
exception_ptr except_;
spinlock splk_;
std::vector< ptr_t > waiting_;
atomic< state_t > state_;
atomic< int > flags_;
atomic< int > priority_;
exception_ptr except_;
spinlock splk_;
std::vector< ptr_t > waiting_;
void release();
virtual void run() = 0;
public:
template< typename StackAllocator, typename Allocator >
fiber_base( attributes const& attrs, StackAllocator const& stack_alloc, Allocator const&) :
fss_data_(),
state_( READY),
flags_( 0),
priority_( 0),
callee_( 0),
caller_(
boost::bind( & fiber_base::trampoline_, this, _1),
attrs,
stack_alloc),
except_(),
waiting_()
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( 0 == callee_);
caller_(); // jump to trampoline
BOOST_ASSERT( 0 != callee_);
BOOST_ASSERT( * callee_);
set_ready(); // fiber is setup and now ready to run
}
fiber_base();
virtual ~fiber_base();
@@ -137,10 +101,6 @@ public:
void priority( int prio) BOOST_NOEXCEPT
{ priority_ = prio; }
void resume();
void suspend();
bool join( ptr_t const&);
bool interruption_blocked() const BOOST_NOEXCEPT
@@ -170,6 +130,8 @@ public:
bool is_waiting() const BOOST_NOEXCEPT
{ return WAITING == state_; }
void set_terminated() BOOST_NOEXCEPT;
void set_ready() BOOST_NOEXCEPT;
void set_running() BOOST_NOEXCEPT;
@@ -188,6 +150,10 @@ public:
{ return except_; }
void rethrow() const;
virtual void resume() = 0;
virtual void suspend() = 0;
};
}}}

View File

@@ -10,13 +10,17 @@
#include <cstddef>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <boost/config.hpp>
#include <boost/coroutine/all.hpp>
#include <boost/exception_ptr.hpp>
#include <boost/move/move.hpp>
#include <boost/ref.hpp>
#include <boost/fiber/attributes.hpp>
#include <boost/fiber/detail/config.hpp>
#include <boost/fiber/detail/fiber_base.hpp>
#include <boost/fiber/exceptions.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
@@ -31,19 +35,26 @@ namespace boost {
namespace fibers {
namespace detail {
template< typename Fn, typename Allocator >
namespace coro = boost::coroutines;
template< typename Fn, typename StackAllocator, typename Allocator >
class fiber_object : public fiber_base
{
public:
typedef typename Allocator::template rebind<
fiber_object< Fn, Allocator >
>::other allocator_t;
fiber_object< Fn, StackAllocator, Allocator >
>::other allocator_t;
private:
typedef fiber_base base_type;
typedef fiber_base base_t;
typedef coro::symmetric_coroutine<
void, StackAllocator
> coro_t;
Fn fn_;
allocator_t alloc_;
Fn fn_;
typename coro_t::yield_type * callee_;
typename coro_t::call_type caller_;
allocator_t alloc_;
static void destroy_( allocator_t & alloc, fiber_object * p)
{
@@ -54,41 +65,131 @@ private:
fiber_object( fiber_object &);
fiber_object & operator=( fiber_object const&);
void trampoline_( typename coro_t::yield_type & yield)
{
BOOST_ASSERT( yield);
BOOST_ASSERT( ! is_terminated() );
callee_ = & yield;
set_running();
suspend();
try
{
BOOST_ASSERT( is_running() );
fn_();
BOOST_ASSERT( is_running() );
}
catch ( coro::detail::forced_unwind const&)
{
set_terminated();
release();
throw;
}
catch ( fiber_interrupted const&)
{ except_ = current_exception(); }
catch (...)
{ std::terminate(); }
set_terminated();
release();
suspend();
BOOST_ASSERT_MSG( false, "fiber already terminated");
}
public:
#ifndef BOOST_NO_RVALUE_REFERENCES
template< typename StackAllocator >
fiber_object( Fn && fn, attributes const& attr,
fiber_object( Fn && fn, attributes const& attrs,
StackAllocator const& stack_alloc,
allocator_t const& alloc) :
base_type( attr, stack_alloc, alloc),
base_t(),
fn_( forward< Fn >( fn) ),
callee_( 0),
caller_(
boost::bind( & fiber_object::trampoline_, this, _1),
attrs,
stack_alloc),
alloc_( alloc)
{}
#else
template< typename StackAllocator >
fiber_object( Fn fn, attributes const& attr,
StackAllocator const& stack_alloc,
allocator_t const& alloc) :
base_type( attr, stack_alloc, alloc),
fn_( fn),
alloc_( alloc)
{}
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( 0 == callee_);
template< typename StackAllocator >
fiber_object( BOOST_RV_REF( Fn) fn, attributes const& attr,
caller_(); // jump to trampoline
BOOST_ASSERT( 0 != callee_);
BOOST_ASSERT( * callee_);
set_ready(); // fiber is setup and now ready to run
}
#else
fiber_object( Fn fn, attributes const& attrs,
StackAllocator const& stack_alloc,
allocator_t const& alloc) :
base_type( attr, stack_alloc, alloc),
base_t(),
fn_( fn),
callee_( 0),
caller_(
boost::bind( & fiber_object::trampoline_, this, _1),
attrs,
stack_alloc),
alloc_( alloc)
{}
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( 0 == callee_);
caller_(); // jump to trampoline
BOOST_ASSERT( 0 != callee_);
BOOST_ASSERT( * callee_);
set_ready(); // fiber is setup and now ready to run
}
fiber_object( BOOST_RV_REF( Fn) fn, attributes const& attrs,
StackAllocator const& stack_alloc,
allocator_t const& alloc) :
base_t(),
fn_( fn),
callee_( 0),
caller_(
boost::bind( & fiber_object::trampoline_, this, _1),
attrs,
stack_alloc),
alloc_( alloc)
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( 0 == callee_);
caller_(); // jump to trampoline
BOOST_ASSERT( 0 != callee_);
BOOST_ASSERT( * callee_);
set_ready(); // fiber is setup and now ready to run
}
#endif
void deallocate_object()
{ destroy_( alloc_, this); }
void run()
{ fn_(); }
void resume()
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm
caller_();
}
void suspend()
{
BOOST_ASSERT( callee_);
BOOST_ASSERT( * callee_);
( * callee_)();
BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm
}
};
}}}

View File

@@ -77,7 +77,7 @@ public:
impl_()
{
typedef detail::fiber_object<
fiber_fn, std::allocator< fiber >
fiber_fn, stack_allocator, std::allocator< fiber >
> object_t;
object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -93,7 +93,7 @@ public:
impl_()
{
typedef detail::fiber_object<
fiber_fn, std::allocator< fiber >
fiber_fn, StackAllocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -109,7 +109,7 @@ public:
impl_()
{
typedef detail::fiber_object<
fiber_fn, Allocator
fiber_fn, StackAllocator, Allocator
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -125,7 +125,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, stack_allocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -141,7 +141,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, StackAllocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -157,7 +157,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, Allocator
Fn, StackAllocator, Allocator
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -173,7 +173,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, stack_allocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -189,7 +189,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, StackAllocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -205,7 +205,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, Allocator
Fn, StackAllocator, Allocator
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -221,7 +221,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, stack_allocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -237,7 +237,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, std::allocator< fiber >
Fn, StackAllocator, std::allocator< fiber >
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(
@@ -253,7 +253,7 @@ public:
impl_()
{
typedef detail::fiber_object<
Fn, Allocator
Fn, StackAllocator, Allocator
> object_t;
typename object_t::allocator_t a( alloc);
impl_ = ptr_t(

View File

@@ -16,21 +16,24 @@
#include "../bind_processor.hpp"
#include "../clock.hpp"
#include "../preallocated_stack_allocator.hpp"
boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved;
boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::no_stack_unwind;
boost::uint64_t jobs = 1000;
bool prealloc = true;
void worker() {}
duration_type measure( duration_type overhead)
template< typename StackAllocator >
duration_type measure( duration_type overhead, StackAllocator const& stack_alloc)
{
boost::fibers::attributes attrs( unwind_stack, preserve_fpu);
boost::fibers::fiber( worker, attrs).join();
boost::fibers::fiber( worker, attrs, stack_alloc).join();
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
boost::fibers::fiber( worker, attrs).join();
boost::fibers::fiber( worker, attrs, stack_alloc).join();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
@@ -39,6 +42,20 @@ duration_type measure( duration_type overhead)
return total;
}
duration_type measure_standard( duration_type overhead)
{
boost::fibers::stack_allocator stack_alloc;
return measure( overhead, stack_alloc);
}
duration_type measure_prealloc( duration_type overhead)
{
preallocated_stack_allocator stack_alloc;
return measure( overhead, stack_alloc);
}
int main( int argc, char * argv[])
{
try
@@ -50,6 +67,7 @@ int main( int argc, char * argv[])
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("unwind,u", boost::program_options::value< bool >( & unwind), "unwind fiber-stack")
("prealloc,p", boost::program_options::value< bool >( & prealloc), "use preallocated stack")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
@@ -72,7 +90,10 @@ int main( int argc, char * argv[])
duration_type overhead = overhead_clock();
std::cout << "overhead " << overhead.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure( overhead).count();
boost::uint64_t res =
prealloc
? measure_prealloc( overhead).count()
: measure_standard( overhead).count();
std::cout << "average of " << res << " nano seconds" << std::endl;
return EXIT_SUCCESS;

View File

@@ -0,0 +1,63 @@
// Copyright Oliver Kowalke 2009.
// 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_FIBER_PREALLOCATED_STACK_ALLOCATOR_H
#define BOOST_FIBER_PREALLOCATED_STACK_ALLOCATOR_H
#include <cstddef>
#include <cstdlib>
#include <stdexcept>
#include <vector>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/coroutine/standard_stack_allocator.hpp>
#include <boost/coroutine/stack_context.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
class preallocated_stack_allocator
{
private:
boost::coroutines::stack_context stack_ctx_;
public:
preallocated_stack_allocator() :
stack_ctx_()
{
boost::coroutines::standard_stack_allocator allocator;
allocator.allocate( stack_ctx_, default_stacksize() );
}
void allocate( boost::coroutines::stack_context & ctx, std::size_t size)
{
ctx.sp = stack_ctx_.sp;
ctx.size = stack_ctx_.size;
}
void deallocate( boost::coroutines::stack_context & ctx)
{
}
static std::size_t maximum_stacksize()
{ return boost::coroutines::standard_stack_allocator::maximum_stacksize(); }
static std::size_t default_stacksize()
{ return boost::coroutines::standard_stack_allocator::default_stacksize(); }
static std::size_t minimum_stacksize()
{ return boost::coroutines::standard_stack_allocator::minimum_stacksize(); }
};
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_FIBER_PREALLOCATED_STACK_ALLOCATOR_H

View File

@@ -23,46 +23,14 @@ namespace boost {
namespace fibers {
namespace detail {
void
fiber_base::set_terminated_() BOOST_NOEXCEPT
{
state_t previous = state_.exchange( TERMINATED);
BOOST_ASSERT( RUNNING == previous);
}
void
fiber_base::trampoline_( coro::symmetric_coroutine< void >::yield_type & yield)
{
BOOST_ASSERT( yield);
BOOST_ASSERT( ! is_terminated() );
callee_ = & yield;
set_running();
suspend();
try
{
BOOST_ASSERT( is_running() );
run();
BOOST_ASSERT( is_running() );
}
catch ( coro::detail::forced_unwind const&)
{
set_terminated_();
release();
throw;
}
catch ( fiber_interrupted const&)
{ except_ = current_exception(); }
catch (...)
{ std::terminate(); }
set_terminated_();
release();
suspend();
BOOST_ASSERT_MSG( false, "fiber already terminated");
}
fiber_base::fiber_base() :
fss_data_(),
state_( READY),
flags_( 0),
priority_( 0),
except_(),
waiting_()
{}
fiber_base::~fiber_base()
{
@@ -70,26 +38,6 @@ fiber_base::~fiber_base()
BOOST_ASSERT( waiting_.empty() );
}
void
fiber_base::resume()
{
BOOST_ASSERT( caller_);
BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm
caller_();
}
void
fiber_base::suspend()
{
BOOST_ASSERT( callee_);
BOOST_ASSERT( * callee_);
( * callee_)();
BOOST_ASSERT( is_running() ); // set by the scheduler-algorithm
}
void
fiber_base::release()
{
@@ -147,6 +95,13 @@ fiber_base::thread_affinity( bool req) BOOST_NOEXCEPT
flags_ &= ~flag_thread_affinity;
}
void
fiber_base::set_terminated() BOOST_NOEXCEPT
{
state_t previous = state_.exchange( TERMINATED);
BOOST_ASSERT( RUNNING == previous);
}
void
fiber_base::set_ready() BOOST_NOEXCEPT
{