blob: 800a803d3e7db166404eb747d8ec2d637f64c68c [file] [log] [blame]
/*
Copyright 2005-2010 Intel Corporation. All Rights Reserved.
This file is part of Threading Building Blocks.
Threading Building Blocks is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
Threading Building Blocks is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Threading Building Blocks; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
As a special exception, you may use this file as part of a free software
library without restriction. Specifically, if other files instantiate
templates or use macros or inline functions from this file, or you compile
this file and link it with other files to produce an executable, this
file does not by itself cause the resulting executable to be covered by
the GNU General Public License. This exception does not however
invalidate any other reasons why the executable file might be covered by
the GNU General Public License.
*/
// configuration:
//! enable/disable std::map tests
#define STDTABLE 1
//! enable/disable old implementation tests (correct include file also)
#define OLDTABLE 0
#define OLDTABLEHEADER "tbb/concurrent_hash_map-4078.h"//-4329
//! enable/disable experimental implementation tests (correct include file also)
#define TESTTABLE 0
#define TESTTABLEHEADER "tbb/concurrent_unordered_map.h"
//////////////////////////////////////////////////////////////////////////////////
#include <cstdlib>
#include <math.h>
#include "tbb/tbb_stddef.h"
#include <vector>
#include <map>
// needed by hash_maps
#include <stdexcept>
#include <iterator>
#include <algorithm> // std::swap
#include <utility> // Need std::pair from here
#include "tbb/cache_aligned_allocator.h"
#include "tbb/tbb_allocator.h"
#include "tbb/spin_rw_mutex.h"
#include "tbb/aligned_space.h"
#include "tbb/atomic.h"
// for test
#include "tbb/spin_mutex.h"
#include "time_framework.h"
using namespace tbb;
using namespace tbb::internal;
struct IntHashCompare {
size_t operator() ( int x ) const { return x; }
bool operator() ( int x, int y ) const { return x==y; }
static long hash( int x ) { return x; }
bool equal( int x, int y ) const { return x==y; }
};
namespace version_current {
namespace tbb { using namespace ::tbb; namespace internal { using namespace ::tbb::internal; } }
#include "tbb/concurrent_hash_map.h"
}
typedef version_current::tbb::concurrent_hash_map<int,int,IntHashCompare> IntTable;
#if OLDTABLE
#undef __TBB_concurrent_hash_map_H
namespace version_base {
namespace tbb { using namespace ::tbb; namespace internal { using namespace ::tbb::internal; } }
#include OLDTABLEHEADER
}
typedef version_base::tbb::concurrent_hash_map<int,int,IntHashCompare> OldTable;
#endif
#if TESTTABLE
#undef __TBB_concurrent_hash_map_H
namespace version_new {
namespace tbb { using namespace ::tbb; namespace internal { using namespace ::tbb::internal; } }
#include TESTTABLEHEADER
}
typedef version_new::tbb::concurrent_unordered_map<int,int,IntHashCompare,IntHashCompare> TestTable;
#define TESTTABLE 1
#endif
///////////////////////////////////////
static const char *map_testnames[] = {
"1.insert", "2.count(w/rehash)", "3.find/wr", "4.erase"
};
template<typename TableType>
struct TestTBBMap : TesterBase {
typedef typename TableType::accessor accessor;
typedef typename TableType::const_accessor const_accessor;
TableType Table;
int n_items;
TestTBBMap() : TesterBase(4) {}
void init() { n_items = value/threads_count; }
std::string get_name(int testn) {
return std::string(map_testnames[testn]);
}
double test(int test, int t)
{
switch(test) {
case 0: // fill
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a;
Table.insert( a, i );
a->second = 0;
}
break;
case 1: // work1
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
size_t c = Table.count( i );
ASSERT( c == 1, NULL);
}
break;
case 2: // work2
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a;
Table.find( a, i );
ASSERT( !a->second, "A key should be incremented only once");
a->second += 1;
}
break;
case 3: // clean
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
ASSERT( Table.erase( i ), NULL);
}
}
return 0;
}
};
template<typename M>
struct TestSTLMap : TesterBase {
std::map<int, int> Table;
M mutex;
int n_items;
TestSTLMap() : TesterBase(4) {}
void init() { n_items = value/threads_count; }
std::string get_name(int testn) {
return std::string(map_testnames[testn]);
}
double test(int test, int t)
{
switch(test) {
case 0: // fill
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
typename M::scoped_lock with(mutex);
Table[i] = 0;
}
break;
case 1: // work1
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
typename M::scoped_lock with(mutex);
size_t c = Table.count(i);
ASSERT( c == 1, NULL);
}
break;
case 2: // work2
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
typename M::scoped_lock with(mutex);
Table[i] += 1;
}
break;
case 3: // clean
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
typename M::scoped_lock with(mutex);
Table.erase(i);
}
}
return 0;
}
};
class fake_mutex {
int a;
public:
class scoped_lock {
fake_mutex *p;
public:
scoped_lock() {}
scoped_lock( fake_mutex &m ) { p = &m; }
~scoped_lock() { p->a = 0; }
void acquire( fake_mutex &m ) { p = &m; }
void release() { }
};
};
class test_hash_map : public TestProcessor {
public:
test_hash_map() : TestProcessor("test_hash_map") {}
void factory(int value, int threads) {
if(Verbose) printf("Processing with %d threads: %d...\n", threads, value);
process( value, threads,
#if STDTABLE
run("std::map ", new NanosecPerValue<TestSTLMap<spin_mutex> >() ),
#endif
#if OLDTABLE
run("old::hmap", new NanosecPerValue<TestTBBMap<OldTable> >() ),
#endif
run("tbb::hmap", new NanosecPerValue<TestTBBMap<IntTable> >() ),
#if TESTTABLE
run("new::hmap", new NanosecPerValue<TestTBBMap<TestTable> >() ),
#endif
end );
//stat->Print(StatisticsCollector::Stdout);
if(value >= 2097152) stat->Print(StatisticsCollector::HTMLFile);
}
};
/////////////////////////////////////////////////////////////////////////////////////////
template<typename TableType>
struct TestHashMapFind : TesterBase {
typedef typename TableType::accessor accessor;
typedef typename TableType::const_accessor const_accessor;
TableType Table;
int n_items;
std::string get_name(int testn) {
return std::string(!testn?"find":"insert");
}
TestHashMapFind() : TesterBase(2) {}
void init() {
n_items = value/threads_count;
for(int i = 0; i < value; i++) {
accessor a; Table.insert( a, i );
}
}
double test(int test, int t)
{
switch(test) {
case 0: // find
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a;
Table.find( a, i );
a->second = i;
}
break;
case 1: // insert
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a;
Table.insert( a, i );
a->second = -i;
}
break;
}
return 0;
}
};
const int test2_size = 65536;
int Data[test2_size];
template<typename TableType>
struct TestHashCountStrings : TesterBase {
typedef typename TableType::accessor accessor;
typedef typename TableType::const_accessor const_accessor;
TableType Table;
int n_items;
std::string get_name(int testn) {
return !testn?"insert":"find";
}
TestHashCountStrings() : TesterBase(2) {}
void init() {
n_items = value/threads_count;
}
double test(int testn, int t)
{
if(!testn) {
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a; Table.insert( a, Data[i] );
}
} else { //
for(int i = t*n_items, e = (t+1)*n_items; i < e; i++) {
accessor a; Table.find( a, Data[i] );
}
}
return 0;
}
};
class test_hash_map_find : public TestProcessor {
public:
test_hash_map_find() : TestProcessor("test_hash_map_find") {}
void factory(int value, int threads) {
if(Verbose) printf("Processing with %d threads: %d...\n", threads, value);
process( value, threads,
#if OLDTABLE
run("Filled old::hashmap", new NanosecPerValue<TestHashMapFind<OldTable> >() ),
#endif
run("Filled tbb::hashmap", new NanosecPerValue<TestHashMapFind<IntTable> >() ),
#if TESTTABLE
run("Filled new::hashmap", new NanosecPerValue<TestHashMapFind<TestTable> >() ),
#endif
#if OLDTABLE
run("CountStr old::hashmap", new TimeTest<TestHashCountStrings<OldTable> >() ),
#endif
run("CountStr tbb::hashmap", new TimeTest<TestHashCountStrings<IntTable> >() ),
#if TESTTABLE
run("CountStr new::hashmap", new TimeTest<TestHashCountStrings<TestTable> >() ),
#endif
end );
//stat->Print(StatisticsCollector::HTMLFile);
}
};
/////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
if(argc>1) Verbose = true;
//if(argc>2) ExtraVerbose = true;
MinThread = 1; MaxThread = task_scheduler_init::default_num_threads();
ParseCommandLine( argc, argv );
ASSERT(tbb_allocator<int>::allocator_type() == tbb_allocator<int>::scalable, "expecting scalable allocator library to be loaded. Please build it by:\n\t\tmake tbbmalloc");
{
test_hash_map_find test_find; int o = test2_size;
for(int i = 0; i < o; i++)
Data[i] = i%60;
for( int t=MinThread; t <= MaxThread; t++)
test_find.factory(o, t);
test_find.report.SetTitle("Nanoseconds per operation of finding operation (Mode) for %d items", o);
test_find.report.Print(StatisticsCollector::HTMLFile|StatisticsCollector::ExcelXML);
}
{
test_hash_map the_test;
for( int t=MinThread; t <= MaxThread; t*=2)
for( int o=/*2048*/(1<<8)*8; o<2200000; o*=2 )
the_test.factory(o, t);
the_test.report.SetTitle("Nanoseconds per operation of (Mode) for N items in container (Name)");
the_test.report.SetStatisticFormula("1AVG per size", "=AVERAGE(ROUNDS)");
the_test.report.Print(StatisticsCollector::HTMLFile|StatisticsCollector::ExcelXML);
}
return 0;
}