| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- // Copyright (c) 2006-2018 Maxim Khizhinsky
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- #include "stack_type.h"
- namespace {
- static size_t s_nPushThreadCount = 4;
- static size_t s_nPopThreadCount = 4;
- static size_t s_nStackSize = 1000000;
- static size_t s_nEliminationSize = 4;
- static atomics::atomic<size_t> s_nWorkingProducers( 0 );
- class stack_push_pop : public cds_test::stress_fixture
- {
- protected:
- enum thread_type
- {
- producer_thread,
- consumer_thread
- };
- struct value_type {
- size_t nNo;
- size_t nThread;
- value_type()
- : nNo( 0 )
- , nThread( 0 )
- {}
- value_type( size_t n )
- : nNo( n )
- , nThread( 0 )
- {}
- };
- static size_t const c_nValArraySize = 1024;
- template <class Stack>
- class Producer: public cds_test::thread
- {
- typedef cds_test::thread base_class;
- public:
- Producer( cds_test::thread_pool& pool, Stack& stack, size_t push_count )
- : base_class( pool, producer_thread )
- , m_stack( stack )
- , m_nItemCount( push_count )
- , m_nPushError( 0 )
- {}
- Producer( Producer& src )
- : base_class( src )
- , m_stack( src.m_stack )
- , m_nItemCount( src.m_nItemCount )
- , m_nPushError( 0 )
- {}
- virtual thread * clone()
- {
- return new Producer( *this );
- }
- virtual void test()
- {
- memset( m_arrPush, 0, sizeof( m_arrPush ));
- value_type v;
- v.nThread = id();
- for ( size_t i = 0; i < m_nItemCount; ++i ) {
- v.nNo = i % c_nValArraySize;
- if ( m_stack.push( v ))
- ++m_arrPush[v.nNo];
- else
- ++m_nPushError;
- }
- s_nWorkingProducers.fetch_sub( 1, atomics::memory_order_release );
- }
- public:
- Stack& m_stack;
- size_t const m_nItemCount;
- size_t m_nPushError;
- size_t m_arrPush[c_nValArraySize];
- };
- template <class Stack>
- class Consumer : public cds_test::thread
- {
- typedef cds_test::thread base_class;
- public:
- Consumer( cds_test::thread_pool& pool, Stack& stack )
- : base_class( pool, consumer_thread )
- , m_stack( stack )
- , m_nPopCount( 0 )
- , m_nPopEmpty( 0 )
- , m_nDirtyPop( 0 )
- {}
- Consumer( Consumer& src )
- : base_class( src )
- , m_stack( src.m_stack )
- , m_nPopCount( 0 )
- , m_nPopEmpty( 0 )
- , m_nDirtyPop( 0 )
- {}
- virtual thread * clone()
- {
- return new Consumer( *this );
- }
- virtual void test()
- {
- memset( m_arrPop, 0, sizeof( m_arrPop ));
- value_type v;
- while ( !( s_nWorkingProducers.load( atomics::memory_order_acquire ) == 0 && m_stack.empty())) {
- if ( m_stack.pop( v )) {
- ++m_nPopCount;
- if ( v.nNo < sizeof( m_arrPop ) / sizeof( m_arrPop[0] ))
- ++m_arrPop[v.nNo];
- else
- ++m_nDirtyPop;
- }
- else
- ++m_nPopEmpty;
- }
- }
- public:
- Stack& m_stack;
- size_t m_nPopCount;
- size_t m_nPopEmpty;
- size_t m_arrPop[c_nValArraySize];
- size_t m_nDirtyPop;
- };
- protected:
- static void SetUpTestCase()
- {
- cds_test::config const& cfg = get_config("Stack_PushPop");
- s_nPushThreadCount = cfg.get_size_t( "PushThreadCount", s_nPushThreadCount );
- s_nPopThreadCount = cfg.get_size_t( "PopThreadCount", s_nPopThreadCount );
- s_nStackSize = cfg.get_size_t( "StackSize", s_nStackSize );
- s_nEliminationSize = cfg.get_size_t( "EliminationSize", s_nEliminationSize );
- if ( s_nPushThreadCount == 0 )
- s_nPushThreadCount = 1;
- if ( s_nPopThreadCount == 0 )
- s_nPopThreadCount = 1;
- }
- //static void TearDownTestCase();
- template <typename Stack>
- void test( Stack& stack )
- {
- cds_test::thread_pool& pool = get_pool();
- size_t const nPushCount = s_nStackSize / s_nPushThreadCount;
- pool.add( new Producer<Stack>( pool, stack, nPushCount ), s_nPushThreadCount );
- pool.add( new Consumer<Stack>( pool, stack ), s_nPopThreadCount );
- s_nWorkingProducers.store( s_nPushThreadCount );
- s_nStackSize = nPushCount * s_nPushThreadCount;
- propout() << std::make_pair( "producer_thread_count", s_nPushThreadCount )
- << std::make_pair( "consumer_thread_count", s_nPopThreadCount )
- << std::make_pair( "push_count", s_nStackSize )
- ;
- std::chrono::milliseconds duration = pool.run();
- propout() << std::make_pair( "duration", duration );
- analyze( stack );
- propout() << stack.statistics();
- }
- template <typename Stack>
- void test_elimination( Stack& stack )
- {
- test( stack );
- check_elimination_stat( stack.statistics());
- }
- void check_elimination_stat( cds::container::treiber_stack::empty_stat const& )
- {}
- void check_elimination_stat( cds::container::treiber_stack::stat<> const& s )
- {
- EXPECT_EQ( s.m_PushCount.get() + s.m_ActivePushCollision.get() + s.m_PassivePushCollision.get(), s_nStackSize );
- EXPECT_EQ( s.m_PopCount.get() + s.m_ActivePopCollision.get() + s.m_PassivePopCollision.get(), s_nStackSize );
- EXPECT_EQ( s.m_PushCount.get(), s.m_PopCount.get());
- EXPECT_EQ( s.m_ActivePopCollision.get(), s.m_PassivePushCollision.get());
- EXPECT_EQ( s.m_ActivePushCollision.get(), s.m_PassivePopCollision.get());
- }
- template< class Stack>
- void analyze( Stack& /*stack*/ )
- {
- cds_test::thread_pool& pool = get_pool();
- size_t nPushError = 0;
- size_t nPopEmpty = 0;
- size_t nPopCount = 0;
- size_t arrVal[c_nValArraySize];
- memset( arrVal, 0, sizeof( arrVal ));
- size_t nDirtyPop = 0;
- for ( size_t threadNo = 0; threadNo < pool.size(); ++threadNo ) {
- cds_test::thread& thread = pool.get( threadNo );
- if ( thread.type() == producer_thread ) {
- Producer<Stack>& producer = static_cast<Producer<Stack>&>( thread );
- nPushError += producer.m_nPushError;
- for ( size_t i = 0; i < c_nValArraySize; ++i )
- arrVal[i] += producer.m_arrPush[i];
- }
- else {
- ASSERT_EQ( thread.type(), consumer_thread );
- Consumer<Stack>& consumer = static_cast<Consumer<Stack>&>(thread);
- nPopEmpty += consumer.m_nPopEmpty;
- nPopCount += consumer.m_nPopCount;
- nDirtyPop += consumer.m_nDirtyPop;
- for ( size_t i = 0; i < c_nValArraySize; ++i )
- arrVal[i] -= consumer.m_arrPop[i];
- }
- }
- EXPECT_EQ( nPopCount, s_nStackSize );
- EXPECT_EQ( nDirtyPop, 0u );
- for ( size_t i = 0; i < sizeof( arrVal ) / sizeof( arrVal[0] ); ++i ) {
- EXPECT_EQ( arrVal[i], 0u );
- }
- propout() << std::make_pair( "push_count", s_nStackSize )
- << std::make_pair( "push_error", nPushError )
- << std::make_pair( "pop_empty", nPopEmpty )
- << std::make_pair( "dirty_pop", nDirtyPop )
- ;
- }
- };
- CDSSTRESS_TreiberStack( stack_push_pop )
- CDSSTRESS_EliminationStack( stack_push_pop )
- CDSSTRESS_FCStack( stack_push_pop )
- CDSSTRESS_FCDeque( stack_push_pop )
- CDSSTRESS_StdStack( stack_push_pop )
- } // namespace
|