/*
This file is a part of libcds - Concurrent Data Structures library
- (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+ (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
Source code repo: http://github.com/khizmax/libcds/
Download: http://sourceforge.net/projects/libcds/files/
*/
#include "set_type.h"
+#include <cds/os/topology.h>
namespace set {
{
uint32_t nKey;
uint16_t nThread;
- uint16_t pad_;
key_thread( size_t key, size_t threadNo )
: nKey( static_cast<uint32_t>(key))
, nThread( static_cast<uint16_t>(threadNo))
- , pad_(0)
{}
key_thread()
: nKey()
, nThread()
- , pad_( 0 )
{}
};
static size_t s_nDelThreadCount; // delete thread count
static size_t s_nExtractThreadCount; // extract thread count
static size_t s_nMaxLoadFactor; // maximum load factor
+ static size_t s_nInsertPassCount;
+ static size_t s_nFindThreadCount; // find thread count
static size_t s_nCuckooInitialSize; // initial size for CuckooSet
static size_t s_nCuckooProbesetSize; // CuckooSet probeset size (only for list-based probeset)
static void SetUpTestCase();
static void TearDownTestCase();
+ template <typename Pred>
+ static void prepare_array( std::vector<size_t>& arr, Pred pred )
+ {
+ arr.reserve( m_arrData.size() );
+ for ( auto el : m_arrData ) {
+ if ( pred( el ) )
+ arr.push_back( el );
+ }
+ arr.resize( arr.size() );
+ shuffle( arr.begin(), arr.end() );
+ }
+
protected:
typedef key_thread key_type;
typedef size_t value_type;
inserter_thread,
deleter_thread,
extractor_thread,
+ find_thread
};
void operator()(key_value_pair& /*cur*/, key_value_pair * /*prev*/) const
{}
};
+
+ void init_data()
+ {
+ prepare_array( m_arr, []( size_t ) -> bool { return true; } );
+ for ( size_t i = 0; i < m_arr.size(); ++i ) {
+ if ( m_Set.insert( key_type( m_arr[i], id() ) ) )
+ ++m_nInsertInitSuccess;
+ else
+ ++m_nInsertInitFailed;
+ }
+ }
+
public:
size_t m_nInsertSuccess = 0;
size_t m_nInsertFailed = 0;
+ size_t m_nInsertInitSuccess = 0;
+ size_t m_nInsertInitFailed = 0;
+
+ std::vector<size_t> m_arr;
public:
Inserter( cds_test::thread_pool& pool, Set& set )
: base_class( pool, inserter_thread )
, m_Set( set )
- {}
+ {
+ init_data();
+ }
Inserter( Inserter& src )
: base_class( src )
, m_Set( src.m_Set )
- {}
+ {
+ init_data();
+ }
virtual thread * clone()
{
Set& rSet = m_Set;
Set_DelOdd& fixture = pool().template fixture<Set_DelOdd>();
- std::vector<size_t>& arrData = fixture.m_arrData;
- for ( size_t i = 0; i < arrData.size(); ++i ) {
- if ( rSet.insert( key_type( arrData[i], id())))
- ++m_nInsertSuccess;
- else
- ++m_nInsertFailed;
- }
-
- update_functor f;
- for ( size_t i = arrData.size() - 1; i > 0; --i ) {
- if ( arrData[i] & 1 )
- rSet.update( key_type( arrData[i], id()), f, true );
+ for ( size_t nPass = 0; nPass < s_nInsertPassCount; ++nPass ) {
+ if ( nPass & 1 ) {
+ // insert pass
+ for ( auto el : m_arr ) {
+ if ( el & 1 ) {
+ if ( rSet.insert( key_type( el, id() ) ) )
+ ++m_nInsertSuccess;
+ else
+ ++m_nInsertFailed;
+ }
+ }
+ }
+ else {
+ // update pass
+ for ( auto el : m_arr ) {
+ if ( el & 1 ) {
+ bool success;
+ bool inserted;
+ std::tie( success, inserted ) = rSet.update( key_type( el, id() ), update_functor() );
+ if ( success && inserted )
+ ++m_nInsertSuccess;
+ else
+ ++m_nInsertFailed;
+ }
+ }
+ }
}
fixture.m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_release );
+ m_arr.resize( 0 );
}
};
typedef cds_test::thread base_class;
Set& m_Set;
+ void init_data()
+ {
+ prepare_array( m_arr, []( size_t el ) ->bool { return ( el & 1 ) != 0; } );
+ }
+
public:
size_t m_nDeleteSuccess = 0;
size_t m_nDeleteFailed = 0;
+ std::vector<size_t> m_arr;
+
public:
Deleter( cds_test::thread_pool& pool, Set& set )
: base_class( pool, deleter_thread )
, m_Set( set )
- {}
+ {
+ init_data();
+ }
Deleter( Deleter& src )
: base_class( src )
, m_Set( src.m_Set )
- {}
+ {
+ init_data();
+ }
virtual thread * clone()
{
size_t const nInsThreadCount = s_nInsThreadCount;
Set_DelOdd& fixture = pool().template fixture<Set_DelOdd>();
- std::vector<size_t>& arrData = fixture.m_arrData;
- if ( id() & 1 ) {
- for (size_t i = 0; i < arrData.size(); ++i) {
- if ( arrData[i] & 1 ) {
+ do {
+ if ( id() & 1 ) {
+ for ( auto el : m_arr ) {
for ( size_t k = 0; k < nInsThreadCount; ++k ) {
- if ( eraser<Set, Set::c_bEraseExactKey>::erase( rSet, arrData[i], k ))
+ if ( rSet.erase( key_type( el, k ) ) )
++m_nDeleteSuccess;
else
++m_nDeleteFailed;
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
- else {
- for ( size_t i = arrData.size() - 1; i > 0; --i ) {
- if ( arrData[i] & 1 ) {
- for ( size_t k = 0; k < nInsThreadCount; ++k ) {
- if (eraser<Set, Set::c_bEraseExactKey>::erase(rSet, arrData[i], k))
+ else {
+ for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ for ( auto el : m_arr ) {
+ if ( rSet.erase( key_type( el, k ) ) )
++m_nDeleteSuccess;
else
++m_nDeleteFailed;
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
+ } while ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) != 0 );
+
+ m_arr.resize( 0 );
}
};
typedef cds_test::thread base_class;
Set& m_Set;
+ std::vector<size_t> m_arr;
+
+ void init_data()
+ {
+ prepare_array( m_arr, []( size_t el ) ->bool { return ( el & 1 ) != 0; } );
+ }
+
public:
size_t m_nExtractSuccess = 0;
size_t m_nExtractFailed = 0;
Extractor( cds_test::thread_pool& pool, Set& set )
: base_class( pool, extractor_thread )
, m_Set( set )
- {}
+ {
+ init_data();
+ }
Extractor( Extractor& src )
: base_class( src )
, m_Set( src.m_Set )
- {}
+ {
+ init_data();
+ }
virtual thread * clone()
{
return new Extractor( *this );
}
- template <typename SetType, bool>
- struct extractor {
- static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t /*thread*/)
- {
- return s.extract_with( key, key_less());
- }
- };
-
- template <typename SetType>
- struct extractor<SetType, true> {
- static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t thread)
- {
- return s.extract( key_type(key, thread));
- }
- };
-
virtual void test()
{
Set& rSet = m_Set;
-
typename Set::guarded_ptr gp;
Set_DelOdd& fixture = pool().template fixture<Set_DelOdd>();
- std::vector<size_t>& arrData = fixture.m_arrData;
size_t const nInsThreadCount = s_nInsThreadCount;
- if ( id() & 1 ) {
- for ( size_t i = 0; i < arrData.size(); ++i ) {
- if ( arrData[i] & 1 ) {
+ do {
+ if ( id() & 1 ) {
+ for ( auto el : m_arr ) {
for ( size_t k = 0; k < nInsThreadCount; ++k ) {
- gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k );
+ gp = rSet.extract( key_type( el, k ) );
if ( gp )
++m_nExtractSuccess;
else
gp.release();
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
- else {
- for ( size_t i = arrData.size() - 1; i > 0; --i ) {
- if ( arrData[i] & 1 ) {
- for ( size_t k = 0; k < nInsThreadCount; ++k ) {
- gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
+ else {
+ for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ for ( auto el : m_arr ) {
+ gp = rSet.extract( key_type( el, k ) );
if ( gp )
++m_nExtractSuccess;
else
gp.release();
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
+ } while ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) != 0 );
+
+ m_arr.resize( 0 );
}
};
{
typedef cds_test::thread base_class;
Set& m_Set;
+ std::vector<size_t> m_arr;
+
+ void init_data()
+ {
+ prepare_array( m_arr, []( size_t el ) -> bool { return ( el & 1 ) != 0; } );
+ }
public:
size_t m_nExtractSuccess = 0;
Extractor( cds_test::thread_pool& pool, Set& set )
: base_class( pool, extractor_thread )
, m_Set( set )
- {}
+ {
+ init_data();
+ }
Extractor( Extractor& src )
: base_class( src )
, m_Set( src.m_Set )
- {}
+ {
+ init_data();
+ }
virtual thread * clone()
{
return new Extractor( *this );
}
- template <typename SetType, bool>
- struct extractor {
- static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t /*thread*/)
- {
- return s.extract_with(key, key_less());
- }
- };
-
- template <typename SetType>
- struct extractor<SetType, true> {
- static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t thread)
- {
- return s.extract(key_type(key, thread));
- }
- };
-
virtual void test()
{
Set& rSet = m_Set;
-
typename Set::exempt_ptr xp;
Set_DelOdd& fixture = pool().template fixture<Set_DelOdd>();
- std::vector<size_t>& arrData = fixture.m_arrData;
size_t const nInsThreadCount = fixture.s_nInsThreadCount;
- if ( id() & 1 ) {
- for ( size_t i = 0; i < arrData.size(); ++i ) {
- if ( arrData[i] & 1 ) {
- for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ do {
+ if ( id() & 1 ) {
+ for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ for ( auto el : m_arr ) {
if ( Set::c_bExtractLockExternal ) {
typename Set::rcu_lock l;
- xp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
+ xp = rSet.extract( key_type( el, k ) );
if ( xp )
++m_nExtractSuccess;
else
++m_nExtractFailed;
}
else {
- xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
+ xp = rSet.extract( key_type( el, k ) );
if ( xp )
++m_nExtractSuccess;
else
xp.release();
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
- else {
- for ( size_t i = arrData.size() - 1; i > 0; --i ) {
- if ( arrData[i] & 1 ) {
+ else {
+ for ( auto el : m_arr ) {
for ( size_t k = 0; k < nInsThreadCount; ++k ) {
if ( Set::c_bExtractLockExternal ) {
typename Set::rcu_lock l;
- xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
+ xp = rSet.extract( key_type( el, k ) );
if ( xp )
++m_nExtractSuccess;
else
++m_nExtractFailed;
}
else {
- xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
+ xp = rSet.extract( key_type( el, k ) );
if ( xp )
++m_nExtractSuccess;
else
xp.release();
}
}
- if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
- break;
}
- }
+ } while ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) != 0 );
+
+ m_arr.resize( 0 );
+ }
+ };
+
+ // Finds keys
+ template <class Set>
+ class Observer: public cds_test::thread
+ {
+ typedef cds_test::thread base_class;
+ Set& m_Set;
+
+ public:
+ size_t m_nFindEvenSuccess = 0;
+ size_t m_nFindEvenFailed = 0;
+ size_t m_nFindOddSuccess = 0;
+ size_t m_nFindOddFailed = 0;
+
+ public:
+ Observer( cds_test::thread_pool& pool, Set& set )
+ : base_class( pool, find_thread )
+ , m_Set( set )
+ {}
+
+ Observer( Observer& src )
+ : base_class( src )
+ , m_Set( src.m_Set )
+ {}
+
+ virtual thread * clone()
+ {
+ return new Observer( *this );
+ }
+
+ virtual void test()
+ {
+ Set& set = m_Set;
+ Set_DelOdd& fixture = pool().template fixture<Set_DelOdd>();
+ std::vector<size_t> const& arr = m_arrData;
+ size_t const nInsThreadCount = s_nInsThreadCount;
+
+ do {
+ for ( size_t key : arr ) {
+ if ( key & 1 ) {
+ for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ if ( set.contains( key_thread( key, k ) ) )
+ ++m_nFindOddSuccess;
+ else
+ ++m_nFindOddFailed;
+ }
+ }
+ else {
+ // even keys MUST be in the map
+ for ( size_t k = 0; k < nInsThreadCount; ++k ) {
+ if ( set.contains( key_thread( key, k ) ) )
+ ++m_nFindEvenSuccess;
+ else
+ ++m_nFindEvenFailed;
+ }
+ }
+ }
+ } while ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) != 0 );
}
};
{
typedef Inserter<Set> insert_thread;
typedef Deleter<Set> delete_thread;
+ typedef Observer<Set> observer_thread;
m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
cds_test::thread_pool& pool = get_pool();
pool.add( new insert_thread( pool, testSet ), s_nInsThreadCount );
pool.add( new delete_thread( pool, testSet ), s_nDelThreadCount ? s_nDelThreadCount : cds::OS::topology::processor_count());
+ if ( s_nFindThreadCount )
+ pool.add( new observer_thread( pool, testSet ), s_nFindThreadCount );
propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
<< std::make_pair( "delete_thread_count", s_nDelThreadCount )
- << std::make_pair( "set_size", s_nSetSize );
+ << std::make_pair( "find_thread_count", s_nFindThreadCount )
+ << std::make_pair( "set_size", s_nSetSize )
+ << std::make_pair( "pass_count", s_nInsertPassCount );
std::chrono::milliseconds duration = pool.run();
propout() << std::make_pair( "duration", duration );
+ size_t nInsertInitFailed = 0;
+ size_t nInsertInitSuccess = 0;
size_t nInsertSuccess = 0;
size_t nInsertFailed = 0;
size_t nDeleteSuccess = 0;
size_t nDeleteFailed = 0;
+ size_t nFindEvenSuccess = 0;
+ size_t nFindEvenFailed = 0;
+ size_t nFindOddSuccess = 0;
+ size_t nFindOddFailed = 0;
+
for ( size_t i = 0; i < pool.size(); ++i ) {
cds_test::thread& thr = pool.get( i );
- if ( thr.type() == inserter_thread ) {
- insert_thread& inserter = static_cast<insert_thread&>(thr);
- nInsertSuccess += inserter.m_nInsertSuccess;
- nInsertFailed += inserter.m_nInsertFailed;
- }
- else {
- assert( thr.type() == deleter_thread );
- delete_thread& deleter = static_cast<delete_thread&>(thr);
- nDeleteSuccess += deleter.m_nDeleteSuccess;
- nDeleteFailed += deleter.m_nDeleteFailed;
+ switch ( thr.type()) {
+ case inserter_thread:
+ {
+ insert_thread& inserter = static_cast<insert_thread&>(thr);
+ nInsertSuccess += inserter.m_nInsertSuccess;
+ nInsertFailed += inserter.m_nInsertFailed;
+ nInsertInitSuccess += inserter.m_nInsertInitSuccess;
+ nInsertInitFailed += inserter.m_nInsertInitFailed;
+ }
+ break;
+ case deleter_thread:
+ {
+ delete_thread& deleter = static_cast<delete_thread&>(thr);
+ nDeleteSuccess += deleter.m_nDeleteSuccess;
+ nDeleteFailed += deleter.m_nDeleteFailed;
+ }
+ break;
+ case find_thread:
+ {
+ observer_thread& observer = static_cast<observer_thread&>( thr );
+ nFindEvenSuccess = observer.m_nFindEvenSuccess;
+ nFindEvenFailed = observer.m_nFindEvenFailed;
+ nFindOddSuccess = observer.m_nFindOddSuccess;
+ nFindOddFailed = observer.m_nFindOddFailed;
+ }
+ break;
+ default:
+ assert( false );
}
}
- EXPECT_EQ( nInsertSuccess, s_nSetSize * s_nInsThreadCount );
- EXPECT_EQ( nInsertFailed, 0u );
+ size_t const nInitialOddKeys = ( s_nSetSize * s_nInsThreadCount ) / 2;
+
+ EXPECT_EQ( nInsertInitFailed, 0u );
+ EXPECT_EQ( nInsertInitSuccess, s_nSetSize * s_nInsThreadCount );
+ EXPECT_EQ( nFindEvenFailed, 0u );
+ EXPECT_GE( nInsertSuccess + nInitialOddKeys, nDeleteSuccess );
+ EXPECT_LE( nInsertSuccess, nDeleteSuccess );
propout()
+ << std::make_pair( "insert_init_success", nInsertInitSuccess )
+ << std::make_pair( "insert_init_failed", nInsertInitFailed )
<< std::make_pair( "insert_success", nInsertSuccess )
<< std::make_pair( "insert_failed", nInsertFailed )
<< std::make_pair( "delete_success", nDeleteSuccess )
- << std::make_pair( "delete_failed", nDeleteFailed );
+ << std::make_pair( "delete_failed", nDeleteFailed )
+ << std::make_pair( "find_even_success", nFindEvenSuccess )
+ << std::make_pair( "find_even_failed", nFindEvenFailed )
+ << std::make_pair( "find_odd_success", nFindOddSuccess )
+ << std::make_pair( "find_odd_failed", nFindOddFailed );
}
template <class Set>
typedef Inserter<Set> insert_thread;
typedef Deleter<Set> delete_thread;
typedef Extractor< typename Set::gc, Set > extract_thread;
+ typedef Observer<Set> observer_thread;
m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
pool.add( new delete_thread( pool, testSet ), s_nDelThreadCount );
if ( s_nExtractThreadCount )
pool.add( new extract_thread( pool, testSet ), s_nExtractThreadCount );
+ if ( s_nFindThreadCount )
+ pool.add( new observer_thread( pool, testSet ), s_nFindThreadCount );
propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
<< std::make_pair( "delete_thread_count", s_nDelThreadCount )
<< std::make_pair( "extract_thread_count", s_nExtractThreadCount )
- << std::make_pair( "set_size", s_nSetSize );
+ << std::make_pair( "find_thread_count", s_nFindThreadCount )
+ << std::make_pair( "set_size", s_nSetSize )
+ << std::make_pair( "pass_count", s_nInsertPassCount );
std::chrono::milliseconds duration = pool.run();
propout() << std::make_pair( "duration", duration );
+ size_t nInsertInitFailed = 0;
+ size_t nInsertInitSuccess = 0;
size_t nInsertSuccess = 0;
size_t nInsertFailed = 0;
size_t nDeleteSuccess = 0;
size_t nDeleteFailed = 0;
size_t nExtractSuccess = 0;
size_t nExtractFailed = 0;
+
+ size_t nFindEvenSuccess = 0;
+ size_t nFindEvenFailed = 0;
+ size_t nFindOddSuccess = 0;
+ size_t nFindOddFailed = 0;
+
for ( size_t i = 0; i < pool.size(); ++i ) {
cds_test::thread& thr = pool.get( i );
switch ( thr.type()) {
insert_thread& inserter = static_cast<insert_thread&>( thr );
nInsertSuccess += inserter.m_nInsertSuccess;
nInsertFailed += inserter.m_nInsertFailed;
+ nInsertInitSuccess += inserter.m_nInsertInitSuccess;
+ nInsertInitFailed += inserter.m_nInsertInitFailed;
}
break;
case deleter_thread:
nExtractFailed += extractor.m_nExtractFailed;
}
break;
+ case find_thread:
+ {
+ observer_thread& observer = static_cast<observer_thread&>( thr );
+ nFindEvenSuccess = observer.m_nFindEvenSuccess;
+ nFindEvenFailed = observer.m_nFindEvenFailed;
+ nFindOddSuccess = observer.m_nFindOddSuccess;
+ nFindOddFailed = observer.m_nFindOddFailed;
+ }
+ break;
default:
assert( false );
}
}
- EXPECT_EQ( nInsertSuccess, s_nSetSize * s_nInsThreadCount );
- EXPECT_EQ( nInsertFailed, 0u );
+ size_t const nInitialOddKeys = ( s_nSetSize * s_nInsThreadCount ) / 2;
+
+ EXPECT_EQ( nInsertInitFailed, 0u );
+ EXPECT_EQ( nInsertInitSuccess, s_nSetSize * s_nInsThreadCount );
+ EXPECT_EQ( nFindEvenFailed, 0u );
+ EXPECT_GE( nInsertSuccess + nInitialOddKeys, nDeleteSuccess + nExtractSuccess );
+ EXPECT_LE( nInsertSuccess, nDeleteSuccess + nExtractSuccess );
propout()
+ << std::make_pair( "insert_init_success", nInsertInitSuccess )
+ << std::make_pair( "insert_init_failed", nInsertInitFailed )
<< std::make_pair( "insert_success", nInsertSuccess )
<< std::make_pair( "insert_failed", nInsertFailed )
<< std::make_pair( "delete_success", nDeleteSuccess )
<< std::make_pair( "delete_failed", nDeleteFailed )
<< std::make_pair( "extract_success", nExtractSuccess )
- << std::make_pair( "extract_failed", nExtractFailed );
+ << std::make_pair( "extract_failed", nExtractFailed )
+ << std::make_pair( "find_even_success", nFindEvenSuccess )
+ << std::make_pair( "find_even_failed", nFindEvenFailed )
+ << std::make_pair( "find_odd_success", nFindOddSuccess )
+ << std::make_pair( "find_odd_failed", nFindOddFailed );
}
template <typename Set>
do_test_extract_with( testSet );
analyze( testSet );
}
+
+ template <class Map>
+ void run_feldman();
};
class Set_DelOdd_LF: public Set_DelOdd