mirror of
https://github.com/mangosfour/server.git
synced 2026-01-01 16:37:12 +00:00
(based on cipherCOM's repo commit e8a538e) Signed-off-by: VladimirMangos <vladimir@getmangos.com>
366 lines
12 KiB
C++
366 lines
12 KiB
C++
/*
|
|
Copyright 2005-2009 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;
|
|
}
|
|
|