From e8590df090bd937f21a3193b814d0a2920eb2f8a Mon Sep 17 00:00:00 2001 From: Roma Date: Tue, 26 Dec 2017 23:54:44 +0300 Subject: [PATCH 01/19] hopscotch plus some tests --- cds/container/ConcurrentHopscotchHashSet.h | 246 +++++++++++++++++++++ test/unit/main.cpp | 130 ++++++++++- 2 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 cds/container/ConcurrentHopscotchHashSet.h diff --git a/cds/container/ConcurrentHopscotchHashSet.h b/cds/container/ConcurrentHopscotchHashSet.h new file mode 100644 index 000000000..4db4e5c2a --- /dev/null +++ b/cds/container/ConcurrentHopscotchHashSet.h @@ -0,0 +1,246 @@ +/* +* ConcurrentHopscotchHashSet.h +* +* Created on: 23 дек. 2017 г. +* Author: Рома +*/ + +#ifndef CONCURRENTHOPSCOTCHHASHSET_H_ +#define CONCURRENTHOPSCOTCHHASHSET_H_ +#include +#include +#include +#include +#include + + +template + class ConcurrentHopscotchHashSet { + private: + static const int HOP_RANGE = 32; + static const int ADD_RANGE = 256; + static const int MAX_SEGMENTS = 1024; + KEY* BUSY; + static const int MAX_TRIES = 2; + struct Bucket { + unsigned int volatile hop_info; + KEY* volatile key; + DATA* volatile data; + unsigned int volatile _lock; + unsigned int volatile timestamp; + pthread_mutex_t lock_mutex; + pthread_cond_t lock_cv; + Bucket() { + _hop_info = 0; + _lock = 0; + _key = NULL; + _data = NULL; + _timestamp = 0; + pthread_mutex_init(&lock_mutex, NULL); + pthread_cond_init(&lock_cv, NULL); + } + + void lock() { + pthread_mutex_lock(&lock_mutex); + while (1) { + if (_lock == 0) { + _lock = 1; + pthread_mutex_unlock(&lock_mutex); + break; + } + pthread_cond_wait(&lock_cv, &lock_mutex); + } + } + + void unlock() { + pthread_mutex_lock(&lock_mutex); + _lock = 0; + pthread_cond_signal(&lock_cv); + pthread_mutex_unlock(&lock_mutex); + } + + }; + Bucket* segments_arys; + unsigned int segment_mask; + unsigned int bucket_mask; + + + void resize() { + //resize will be in the next year! + } + void find_closer_free_bucket(Bucket** free_bucket, int* free_distance) { + Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); + for (int free_dist = (HOP_RANGE - 1); free_dist>0; --free_dist) { + unsigned int start_hop_info = move_bucket->_hop_info; + int move_free_distance = -1; + unsigned int mask = 1; + for (int i = 0; ilock(); + if (start_hop_info == move_bucket->_hop_info) { + Bucket* new_free_bucket = move_bucket + move_free_distance; + move_bucket->_hop_info |= (1 << free_dist); + (*free_bucket)->_data = new_free_bucket->_data; + (*free_bucket)->_key = new_free_bucket->_key; + ++(move_bucket->_timestamp); + new_free_bucket->_key = BUSY; + new_free_bucket->_data = BUSY; + move_bucket->_hop_info &= ~(1 << move_free_distance); + *free_bucket = new_free_bucket; + *free_distance -= free_dist; + move_bucket->unlock(); + return; + } + move_bucket->unlock(); + } + ++move_bucket; + } + (*free_bucket)->_key = NULL; + val = 0; + *free_distance = 0; + } + + int CalcHashFunc(KEY* key) + { + std::hash hash_fn; + return hash_fn(key); + } + + public: + DATA* get(KEY* key) + { + unsigned int hash = CalcHashFunc(key); + unsigned int iSegment = hash & segment_mask; + unsigned int iBucket = hash & bucket_mask; + Bucket*start_bucket = segments_ary + iBucket; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + timestamp = start_bucket->timestamp; + unsigned int hop_info = start_bucket->hop_info; + for (unsigned int i = 0; i < sizeof(hop_info); i++) { + Bucket* check_bucket = start_bucket + (1 << ((hop_info >> i) & 1)); + if (key == check_bucket->key) + return check_bucket->data; + } + ++try_counter; + } while (timestamp != start_bucket->timestamp && try_counter < MAX_TRIES); + return NULL; + } + + bool contains(KEY* key) { + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + //cout << "Fast Path : try counter" << try_counter << endl; + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + + Bucket* check_bucket = start_bucket; + unsigned int temp; + + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (*key == *(check_bucket->_key)) { + //cout << "Found!!" << endl; + return true; + } + } + + ++check_bucket; + } + + ++try_counter; + } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + //cout << "Slow path " << endl; + if (timestamp != start_bucket->_timestamp) { + Bucket* check_bucket = start_bucket; + for (int i = 0; i < HOP_RANGE; i++) { + if (*key == *(check_bucket->_key)) + return true; + ++check_bucket; + } + } + //cout << "Not found!!" << endl; + return false; + } + + bool add(KEY* key, DATA* data) { + int val = 1; + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + //sleep(1); + if (contains(key)) { + start_bucket->unlock(); + return false; + } + + Bucket* free_bucket = start_bucket; + int free_distance = 0; + for (; free_distance_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) + break; + ++free_bucket; + } + + if (free_distance < ADD_RANGE) { + do { + if (free_distance < HOP_RANGE) { + start_bucket->_hop_info |= (1 << free_distance); + free_bucket->_data = data; + free_bucket->_key = key; + start_bucket->unlock(); + return true; + } + find_closer_bucket(&free_bucket, &free_distance, val); + } while (0 != val); + } + start_bucket->unlock(); + cout << "Called Resize" << endl; + return false; + } + + DATA* remove(KEY* key) { + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + + unsigned int hop_info = start_bucket->hop_info; + unsigned int mask = 1; + for (int i = 0; i_key)) { + int* rc = check_bucket->_data; + check_bucket->_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); + start_bucket->unlock(); + return rc; + } + } + } + start_bucket->unlock(); + return NULL; + } + ConcurrentHopscotchHashSet() { + segments_arys = new Bucket[MAX_SEGMENTS + 256]; + BUSY = (KEY *)malloc(sizeof(KEY)); + *BUSY = -1; + } + // ~ConcurrentHopscotchHashSet(); +}; + +#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ \ No newline at end of file diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 973007c7c..b9b8c4dd5 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -30,16 +30,144 @@ #include #include - +#include #include +#include namespace cds_test { /*static*/ std::random_device fixture::random_dev_; /*static*/ std::mt19937 fixture::random_gen_( random_dev_() ); } // namespace cds_test + +using namespace std; + + +ConcurrentHopscotchHashSet *obj; +void testInt() { + obj = new ConcurrentHopscotchHashSet(); + int key1 = 0; + int val1 = 100; + int key2 = 1; + int val2 = 200; + int key3 = 1025; + int val3 = 300; + + obj->add(&key1, &val1); + cout << "Add 1 complete" << endl; + obj->add(&key2, &val2); + cout << "Add 2 completes" << endl; + obj->add(&key3, &val3); + cout << "Add 3 completes" << endl; + + if (obj->contains(&key1)) + cout << "Key 1 present" << endl; + if (obj->contains(&key2)) + cout << "Key 2 present" << endl; + + if (obj->contains(&key3)) + cout << "Key 3 present" << endl; + +} + +//---------------------------------------- + +void* adding() { + int key = rand() & 1048575; + int value = rand() & 1048575; + if(!obj->add(&key, &value)) + throw exception("Fuuuuu!!!"); +} + +void testIntAddParallel() { + obj = new ConcurrentHopscotchHashSet(); + pthread_t threads[1000]; + for (int r = 0; r<1000; r++) { + pthread_create(&threads[r], NULL, adding); + } + for (int r = 0; r<1000; r++) { + pthread_join(threads[r], NULL); + } +} + +//----------------------------------------------- + +void* contains(void * arg) { + int *data = reinterpret_cast(arg); + if (!obj->contains(data)) + throw exception("Fuuuuu!!!"); +} + +void testIntContainsParallel() { + obj = new ConcurrentHopscotchHashSet(); + for (int r = 0; r<1000; r++) { + obj->add(&r, &r); + } + pthread_t threads[1000]; + for (int r = 0; r<1000; r++) { + pthread_create(&threads[r], NULL, contains, new int(r)); + } + for (int r = 0; r<1000; r++) { + pthread_join(threads[r], NULL); + } +} + +//------------------------- + +void* removing(void * arg) { + int *data = reinterpret_cast(arg); + if (obj->remove(data) == NULL) + throw exception("Fuuuuu!!!"); +} + +void testIntRemoveParallel() { + obj = new ConcurrentHopscotchHashSet(); + for (int r = 0; r<1000; r++) { + obj->add(&r, &r); + } + pthread_t threads[1000]; + for (int r = 0; r<1000; r++) { + pthread_create(&threads[r], NULL, removing, new int(r)); + } + for (int r = 0; r<1000; r++) { + pthread_join(threads[r], NULL); + } +} + +//------------------------- + +void* getting(void * arg) { + int *data = reinterpret_cast(arg); + int* res = obj->get(data); + if (res == NULL) + throw exception("Bad return!!!"); + + if (*res != *data) { + throw exception("Not equals!!!"); + } +} + +void testIntGetParallel() { + obj = new ConcurrentHopscotchHashSet(); + for (int r = 0; r<1000; r++) { + obj->add(&r, &r); + } + pthread_t threads[1000]; + for (int r = 0; r<1000; r++) { + pthread_create(&threads[r], NULL, getting, new int(r)); + } + for (int r = 0; r<1000; r++) { + pthread_join(threads[r], NULL); + } +} + int main( int argc, char **argv ) { + testInt(); + testIntAddParallel(); + testIntContainsParallel(); + testIntGetParallel(); + testIntRemoveParallel(); int result; cds::Initialize(); { From 8f6e40a9a6614d0de0f07283cb01c8426caf70e7 Mon Sep 17 00:00:00 2001 From: Roman Stetskevich Date: Wed, 27 Dec 2017 09:38:18 +0300 Subject: [PATCH 02/19] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20namespace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cds/container/ConcurrentHopscotchHashSet.h | 246 ----------------- cds/container/concurrent_hopscotch_hash_set.h | 249 ++++++++++++++++++ projects/Win/vc141/cds.vcxproj | 1 + projects/Win/vc141/cds.vcxproj.filters | 3 + test/unit/main.cpp | 14 +- 5 files changed, 260 insertions(+), 253 deletions(-) delete mode 100644 cds/container/ConcurrentHopscotchHashSet.h create mode 100644 cds/container/concurrent_hopscotch_hash_set.h diff --git a/cds/container/ConcurrentHopscotchHashSet.h b/cds/container/ConcurrentHopscotchHashSet.h deleted file mode 100644 index 4db4e5c2a..000000000 --- a/cds/container/ConcurrentHopscotchHashSet.h +++ /dev/null @@ -1,246 +0,0 @@ -/* -* ConcurrentHopscotchHashSet.h -* -* Created on: 23 дек. 2017 г. -* Author: Рома -*/ - -#ifndef CONCURRENTHOPSCOTCHHASHSET_H_ -#define CONCURRENTHOPSCOTCHHASHSET_H_ -#include -#include -#include -#include -#include - - -template - class ConcurrentHopscotchHashSet { - private: - static const int HOP_RANGE = 32; - static const int ADD_RANGE = 256; - static const int MAX_SEGMENTS = 1024; - KEY* BUSY; - static const int MAX_TRIES = 2; - struct Bucket { - unsigned int volatile hop_info; - KEY* volatile key; - DATA* volatile data; - unsigned int volatile _lock; - unsigned int volatile timestamp; - pthread_mutex_t lock_mutex; - pthread_cond_t lock_cv; - Bucket() { - _hop_info = 0; - _lock = 0; - _key = NULL; - _data = NULL; - _timestamp = 0; - pthread_mutex_init(&lock_mutex, NULL); - pthread_cond_init(&lock_cv, NULL); - } - - void lock() { - pthread_mutex_lock(&lock_mutex); - while (1) { - if (_lock == 0) { - _lock = 1; - pthread_mutex_unlock(&lock_mutex); - break; - } - pthread_cond_wait(&lock_cv, &lock_mutex); - } - } - - void unlock() { - pthread_mutex_lock(&lock_mutex); - _lock = 0; - pthread_cond_signal(&lock_cv); - pthread_mutex_unlock(&lock_mutex); - } - - }; - Bucket* segments_arys; - unsigned int segment_mask; - unsigned int bucket_mask; - - - void resize() { - //resize will be in the next year! - } - void find_closer_free_bucket(Bucket** free_bucket, int* free_distance) { - Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); - for (int free_dist = (HOP_RANGE - 1); free_dist>0; --free_dist) { - unsigned int start_hop_info = move_bucket->_hop_info; - int move_free_distance = -1; - unsigned int mask = 1; - for (int i = 0; ilock(); - if (start_hop_info == move_bucket->_hop_info) { - Bucket* new_free_bucket = move_bucket + move_free_distance; - move_bucket->_hop_info |= (1 << free_dist); - (*free_bucket)->_data = new_free_bucket->_data; - (*free_bucket)->_key = new_free_bucket->_key; - ++(move_bucket->_timestamp); - new_free_bucket->_key = BUSY; - new_free_bucket->_data = BUSY; - move_bucket->_hop_info &= ~(1 << move_free_distance); - *free_bucket = new_free_bucket; - *free_distance -= free_dist; - move_bucket->unlock(); - return; - } - move_bucket->unlock(); - } - ++move_bucket; - } - (*free_bucket)->_key = NULL; - val = 0; - *free_distance = 0; - } - - int CalcHashFunc(KEY* key) - { - std::hash hash_fn; - return hash_fn(key); - } - - public: - DATA* get(KEY* key) - { - unsigned int hash = CalcHashFunc(key); - unsigned int iSegment = hash & segment_mask; - unsigned int iBucket = hash & bucket_mask; - Bucket*start_bucket = segments_ary + iBucket; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - timestamp = start_bucket->timestamp; - unsigned int hop_info = start_bucket->hop_info; - for (unsigned int i = 0; i < sizeof(hop_info); i++) { - Bucket* check_bucket = start_bucket + (1 << ((hop_info >> i) & 1)); - if (key == check_bucket->key) - return check_bucket->data; - } - ++try_counter; - } while (timestamp != start_bucket->timestamp && try_counter < MAX_TRIES); - return NULL; - } - - bool contains(KEY* key) { - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - //cout << "Fast Path : try counter" << try_counter << endl; - timestamp = start_bucket->_timestamp; - unsigned int hop_info = start_bucket->_hop_info; - - Bucket* check_bucket = start_bucket; - unsigned int temp; - - for (int i = 0; i < HOP_RANGE; i++) { - temp = hop_info; - temp = temp >> i; - - if (temp & 1) { - if (*key == *(check_bucket->_key)) { - //cout << "Found!!" << endl; - return true; - } - } - - ++check_bucket; - } - - ++try_counter; - } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - //cout << "Slow path " << endl; - if (timestamp != start_bucket->_timestamp) { - Bucket* check_bucket = start_bucket; - for (int i = 0; i < HOP_RANGE; i++) { - if (*key == *(check_bucket->_key)) - return true; - ++check_bucket; - } - } - //cout << "Not found!!" << endl; - return false; - } - - bool add(KEY* key, DATA* data) { - int val = 1; - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - //sleep(1); - if (contains(key)) { - start_bucket->unlock(); - return false; - } - - Bucket* free_bucket = start_bucket; - int free_distance = 0; - for (; free_distance_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) - break; - ++free_bucket; - } - - if (free_distance < ADD_RANGE) { - do { - if (free_distance < HOP_RANGE) { - start_bucket->_hop_info |= (1 << free_distance); - free_bucket->_data = data; - free_bucket->_key = key; - start_bucket->unlock(); - return true; - } - find_closer_bucket(&free_bucket, &free_distance, val); - } while (0 != val); - } - start_bucket->unlock(); - cout << "Called Resize" << endl; - return false; - } - - DATA* remove(KEY* key) { - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - - unsigned int hop_info = start_bucket->hop_info; - unsigned int mask = 1; - for (int i = 0; i_key)) { - int* rc = check_bucket->_data; - check_bucket->_key = NULL; - check_bucket->_data = NULL; - start_bucket->_hop_info &= ~(1 << i); - start_bucket->unlock(); - return rc; - } - } - } - start_bucket->unlock(); - return NULL; - } - ConcurrentHopscotchHashSet() { - segments_arys = new Bucket[MAX_SEGMENTS + 256]; - BUSY = (KEY *)malloc(sizeof(KEY)); - *BUSY = -1; - } - // ~ConcurrentHopscotchHashSet(); -}; - -#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ \ No newline at end of file diff --git a/cds/container/concurrent_hopscotch_hash_set.h b/cds/container/concurrent_hopscotch_hash_set.h new file mode 100644 index 000000000..cfb0850c8 --- /dev/null +++ b/cds/container/concurrent_hopscotch_hash_set.h @@ -0,0 +1,249 @@ +/* +* concurrent_hopscotch_hash_set.h +* +* Created on: 23 дек. 2017 г. +* Author: LEONID, ANDREY, ROMAN +*/ + +#ifndef CONCURRENTHOPSCOTCHHASHSET_H_ +#define CONCURRENTHOPSCOTCHHASHSET_H_ +#include +#include +#include +#include +#include + +namespace cds { + namespace container { + template + class concurrent_hopscotch_hash_set { + private: + static const int HOP_RANGE = 32; + static const int ADD_RANGE = 256; + static const int MAX_SEGMENTS = 1024; + KEY* BUSY; + static const int MAX_TRIES = 2; + struct Bucket { + unsigned int volatile hop_info; + KEY* volatile key; + DATA* volatile data; + unsigned int volatile _lock; + unsigned int volatile timestamp; + pthread_mutex_t lock_mutex; + pthread_cond_t lock_cv; + Bucket() { + _hop_info = 0; + _lock = 0; + _key = NULL; + _data = NULL; + _timestamp = 0; + pthread_mutex_init(&lock_mutex, NULL); + pthread_cond_init(&lock_cv, NULL); + } + + void lock() { + pthread_mutex_lock(&lock_mutex); + while (1) { + if (_lock == 0) { + _lock = 1; + pthread_mutex_unlock(&lock_mutex); + break; + } + pthread_cond_wait(&lock_cv, &lock_mutex); + } + } + + void unlock() { + pthread_mutex_lock(&lock_mutex); + _lock = 0; + pthread_cond_signal(&lock_cv); + pthread_mutex_unlock(&lock_mutex); + } + + }; + Bucket* segments_arys; + unsigned int segment_mask; + unsigned int bucket_mask; + + + void resize() { + //resize will be in the next year! + } + void find_closer_free_bucket(Bucket** free_bucket, int* free_distance) { + Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); + for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { + unsigned int start_hop_info = move_bucket->_hop_info; + int move_free_distance = -1; + unsigned int mask = 1; + for (int i = 0; i < free_dist; ++i, mask <<= 1) { + if (mask & start_hop_info) { + move_free_distance = i; + break; + } + } + if (-1 != move_free_distance) { + move_bucket->lock(); + if (start_hop_info == move_bucket->_hop_info) { + Bucket* new_free_bucket = move_bucket + move_free_distance; + move_bucket->_hop_info |= (1 << free_dist); + (*free_bucket)->_data = new_free_bucket->_data; + (*free_bucket)->_key = new_free_bucket->_key; + ++(move_bucket->_timestamp); + new_free_bucket->_key = BUSY; + new_free_bucket->_data = BUSY; + move_bucket->_hop_info &= ~(1 << move_free_distance); + *free_bucket = new_free_bucket; + *free_distance -= free_dist; + move_bucket->unlock(); + return; + } + move_bucket->unlock(); + } + ++move_bucket; + } + (*free_bucket)->_key = NULL; + val = 0; + *free_distance = 0; + } + + int CalcHashFunc(KEY* key) + { + std::hash hash_fn; + return hash_fn(key); + } + + public: + DATA* get(KEY* key) + { + unsigned int hash = CalcHashFunc(key); + unsigned int iSegment = hash & segment_mask; + unsigned int iBucket = hash & bucket_mask; + Bucket*start_bucket = segments_ary + iBucket; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + timestamp = start_bucket->timestamp; + unsigned int hop_info = start_bucket->hop_info; + for (unsigned int i = 0; i < sizeof(hop_info); i++) { + Bucket* check_bucket = start_bucket + (1 << ((hop_info >> i) & 1)); + if (key == check_bucket->key) + return check_bucket->data; + } + ++try_counter; + } while (timestamp != start_bucket->timestamp && try_counter < MAX_TRIES); + return NULL; + } + + bool contains(KEY* key) { + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + //cout << "Fast Path : try counter" << try_counter << endl; + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + + Bucket* check_bucket = start_bucket; + unsigned int temp; + + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (*key == *(check_bucket->_key)) { + //cout << "Found!!" << endl; + return true; + } + } + + ++check_bucket; + } + + ++try_counter; + } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + //cout << "Slow path " << endl; + if (timestamp != start_bucket->_timestamp) { + Bucket* check_bucket = start_bucket; + for (int i = 0; i < HOP_RANGE; i++) { + if (*key == *(check_bucket->_key)) + return true; + ++check_bucket; + } + } + //cout << "Not found!!" << endl; + return false; + } + + bool add(KEY* key, DATA* data) { + int val = 1; + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + //sleep(1); + if (contains(key)) { + start_bucket->unlock(); + return false; + } + + Bucket* free_bucket = start_bucket; + int free_distance = 0; + for (; free_distance < ADD_RANGE; ++free_distance) { + if (NULL == free_bucket->_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) + break; + ++free_bucket; + } + + if (free_distance < ADD_RANGE) { + do { + if (free_distance < HOP_RANGE) { + start_bucket->_hop_info |= (1 << free_distance); + free_bucket->_data = data; + free_bucket->_key = key; + start_bucket->unlock(); + return true; + } + find_closer_bucket(&free_bucket, &free_distance, val); + } while (0 != val); + } + start_bucket->unlock(); + cout << "Called Resize" << endl; + return false; + } + + DATA* remove(KEY* key) { + unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + + unsigned int hop_info = start_bucket->hop_info; + unsigned int mask = 1; + for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { + if (mask & hop_info) { + Bucket* check_bucket = start_bucket + i; + if (*key == *(check_bucket->_key)) { + int* rc = check_bucket->_data; + check_bucket->_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); + start_bucket->unlock(); + return rc; + } + } + } + start_bucket->unlock(); + return NULL; + } + concurrent_hopscotch_hash_set() { + segments_arys = new Bucket[MAX_SEGMENTS + 256]; + BUSY = (KEY *)malloc(sizeof(KEY)); + *BUSY = -1; + } + // ~concurrent_hopscotch_hash_set(); + }; + } +} + +#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ \ No newline at end of file diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index 37446d066..a86392193 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -659,6 +659,7 @@ + diff --git a/projects/Win/vc141/cds.vcxproj.filters b/projects/Win/vc141/cds.vcxproj.filters index 04f33e003..721e05b59 100644 --- a/projects/Win/vc141/cds.vcxproj.filters +++ b/projects/Win/vc141/cds.vcxproj.filters @@ -1261,5 +1261,8 @@ Header Files\cds\details + + Header Files\cds\container + \ No newline at end of file diff --git a/test/unit/main.cpp b/test/unit/main.cpp index b9b8c4dd5..f94a83446 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -43,9 +43,9 @@ namespace cds_test { using namespace std; -ConcurrentHopscotchHashSet *obj; +cds::container::concurrent_hopscotch_hash_set *obj; void testInt() { - obj = new ConcurrentHopscotchHashSet(); + obj = new cds::container::concurrent_hopscotch_hash_set(); int key1 = 0; int val1 = 100; int key2 = 1; @@ -80,7 +80,7 @@ void* adding() { } void testIntAddParallel() { - obj = new ConcurrentHopscotchHashSet(); + obj = new cds::container::concurrent_hopscotch_hash_set(); pthread_t threads[1000]; for (int r = 0; r<1000; r++) { pthread_create(&threads[r], NULL, adding); @@ -99,7 +99,7 @@ void* contains(void * arg) { } void testIntContainsParallel() { - obj = new ConcurrentHopscotchHashSet(); + obj = new cds::container::concurrent_hopscotch_hash_set(); for (int r = 0; r<1000; r++) { obj->add(&r, &r); } @@ -121,7 +121,7 @@ void* removing(void * arg) { } void testIntRemoveParallel() { - obj = new ConcurrentHopscotchHashSet(); + obj = new cds::container::concurrent_hopscotch_hash_set(); for (int r = 0; r<1000; r++) { obj->add(&r, &r); } @@ -148,7 +148,7 @@ void* getting(void * arg) { } void testIntGetParallel() { - obj = new ConcurrentHopscotchHashSet(); + obj = new cds::container::concurrent_hopscotch_hash_set(); for (int r = 0; r<1000; r++) { obj->add(&r, &r); } From de07dda1052e581b2b081212541eb7b34b2c37fe Mon Sep 17 00:00:00 2001 From: LeoSko Date: Wed, 27 Dec 2017 15:04:08 +0300 Subject: [PATCH 03/19] New version --- cds/container/concurrent_hopscotch_hash_set.h | 398 ++++++++---------- test/unit/main.cpp | 8 +- 2 files changed, 188 insertions(+), 218 deletions(-) diff --git a/cds/container/concurrent_hopscotch_hash_set.h b/cds/container/concurrent_hopscotch_hash_set.h index cfb0850c8..a7f4d8e06 100644 --- a/cds/container/concurrent_hopscotch_hash_set.h +++ b/cds/container/concurrent_hopscotch_hash_set.h @@ -15,235 +15,205 @@ namespace cds { namespace container { - template - class concurrent_hopscotch_hash_set { - private: - static const int HOP_RANGE = 32; - static const int ADD_RANGE = 256; - static const int MAX_SEGMENTS = 1024; - KEY* BUSY; - static const int MAX_TRIES = 2; - struct Bucket { - unsigned int volatile hop_info; - KEY* volatile key; - DATA* volatile data; - unsigned int volatile _lock; - unsigned int volatile timestamp; - pthread_mutex_t lock_mutex; - pthread_cond_t lock_cv; - Bucket() { - _hop_info = 0; - _lock = 0; - _key = NULL; - _data = NULL; - _timestamp = 0; - pthread_mutex_init(&lock_mutex, NULL); - pthread_cond_init(&lock_cv, NULL); - } - - void lock() { - pthread_mutex_lock(&lock_mutex); - while (1) { - if (_lock == 0) { - _lock = 1; - pthread_mutex_unlock(&lock_mutex); - break; - } - pthread_cond_wait(&lock_cv, &lock_mutex); - } - } - - void unlock() { - pthread_mutex_lock(&lock_mutex); - _lock = 0; - pthread_cond_signal(&lock_cv); - pthread_mutex_unlock(&lock_mutex); - } - - }; - Bucket* segments_arys; - unsigned int segment_mask; - unsigned int bucket_mask; - - - void resize() { - //resize will be in the next year! - } - void find_closer_free_bucket(Bucket** free_bucket, int* free_distance) { - Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); - for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { - unsigned int start_hop_info = move_bucket->_hop_info; - int move_free_distance = -1; - unsigned int mask = 1; - for (int i = 0; i < free_dist; ++i, mask <<= 1) { - if (mask & start_hop_info) { - move_free_distance = i; - break; - } - } - if (-1 != move_free_distance) { - move_bucket->lock(); - if (start_hop_info == move_bucket->_hop_info) { - Bucket* new_free_bucket = move_bucket + move_free_distance; - move_bucket->_hop_info |= (1 << free_dist); - (*free_bucket)->_data = new_free_bucket->_data; - (*free_bucket)->_key = new_free_bucket->_key; - ++(move_bucket->_timestamp); - new_free_bucket->_key = BUSY; - new_free_bucket->_data = BUSY; - move_bucket->_hop_info &= ~(1 << move_free_distance); - *free_bucket = new_free_bucket; - *free_distance -= free_dist; - move_bucket->unlock(); - return; - } - move_bucket->unlock(); - } - ++move_bucket; - } - (*free_bucket)->_key = NULL; - val = 0; - *free_distance = 0; + template + class Hopscotch { + private: + static const int HOP_RANGE = 32; + static const int ADD_RANGE = 256; + static const int MAX_SEGMENTS = 1048576; // Including neighbourhodd for last hash location + static const int MAX_TRIES = 2; + KEY* BUSY; + + struct Bucket { + + unsigned int volatile _hop_info; + KEY* volatile _key; + DATA* volatile _data; + unsigned int volatile _lock; + unsigned int volatile _timestamp; + pthread_mutex_t lock_mutex; + pthread_cond_t lock_cv; + + Bucket(){ + _hop_info = 0; + _lock = 0; + _key = NULL; + _data=NULL; + _timestamp = 0; + pthread_mutex_init(&lock_mutex,NULL); + pthread_cond_init(&lock_cv, NULL); + } + + void lock(){ + + pthread_mutex_lock(&lock_mutex); + while(1){ + if (_lock==0){ + _lock =1; + pthread_mutex_unlock(&lock_mutex); + break; + } + pthread_cond_wait(&lock_cv, &lock_mutex); + + } } - int CalcHashFunc(KEY* key) - { - std::hash hash_fn; - return hash_fn(key); + void unlock(){ + pthread_mutex_lock(&lock_mutex); + _lock = 0; + pthread_cond_signal(&lock_cv); + pthread_mutex_unlock(&lock_mutex); } - public: - DATA* get(KEY* key) - { - unsigned int hash = CalcHashFunc(key); - unsigned int iSegment = hash & segment_mask; - unsigned int iBucket = hash & bucket_mask; - Bucket*start_bucket = segments_ary + iBucket; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - timestamp = start_bucket->timestamp; - unsigned int hop_info = start_bucket->hop_info; - for (unsigned int i = 0; i < sizeof(hop_info); i++) { - Bucket* check_bucket = start_bucket + (1 << ((hop_info >> i) & 1)); - if (key == check_bucket->key) - return check_bucket->data; - } - ++try_counter; - } while (timestamp != start_bucket->timestamp && try_counter < MAX_TRIES); - return NULL; - } - - bool contains(KEY* key) { - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - //cout << "Fast Path : try counter" << try_counter << endl; - timestamp = start_bucket->_timestamp; - unsigned int hop_info = start_bucket->_hop_info; - - Bucket* check_bucket = start_bucket; - unsigned int temp; - - for (int i = 0; i < HOP_RANGE; i++) { - temp = hop_info; - temp = temp >> i; - - if (temp & 1) { - if (*key == *(check_bucket->_key)) { - //cout << "Found!!" << endl; - return true; - } - } - - ++check_bucket; - } - - ++try_counter; - } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - //cout << "Slow path " << endl; - if (timestamp != start_bucket->_timestamp) { - Bucket* check_bucket = start_bucket; - for (int i = 0; i < HOP_RANGE; i++) { - if (*key == *(check_bucket->_key)) + }; + + Bucket* segments_arys; + + public: + Hopscotch(){ + segments_arys = new Bucket[MAX_SEGMENTS+256]; + BUSY=(int *)malloc(sizeof(int)); + *BUSY=-1; + } + ~Hopscotch(); + bool contains(KEY* key){ + unsigned int hash = ((*key)&(MAX_SEGMENTS-1)); + Bucket* start_bucket = segments_arys+hash; + unsigned int try_counter = 0; + unsigned int timestamp; + do{ + //cout << "Fast Path : try counter" << try_counter << endl; + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + + Bucket* check_bucket = start_bucket; + unsigned int temp; + + for( int i = 0 ; i < HOP_RANGE ; i++){ + temp=hop_info; + temp=temp>>i; + + if(temp&1){ + if(*key == *(check_bucket->_key)){ + //cout << "Found!!" << endl; return true; - ++check_bucket; + } } - } - //cout << "Not found!!" << endl; - return false; - } - - bool add(KEY* key, DATA* data) { - int val = 1; - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - //sleep(1); - if (contains(key)) { - start_bucket->unlock(); - return false; + + ++check_bucket; } - Bucket* free_bucket = start_bucket; - int free_distance = 0; - for (; free_distance < ADD_RANGE; ++free_distance) { - if (NULL == free_bucket->_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) - break; - ++free_bucket; + ++try_counter; + }while(timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + //cout << "Slow path " << endl; + if(timestamp != start_bucket->_timestamp){ + Bucket* check_bucket = start_bucket; + for(int i=0; i_key)) + return true; + ++check_bucket; } - - if (free_distance < ADD_RANGE) { - do { - if (free_distance < HOP_RANGE) { - start_bucket->_hop_info |= (1 << free_distance); - free_bucket->_data = data; - free_bucket->_key = key; - start_bucket->unlock(); - return true; - } - find_closer_bucket(&free_bucket, &free_distance, val); - } while (0 != val); - } - start_bucket->unlock(); - cout << "Called Resize" << endl; - return false; } - - DATA* remove(KEY* key) { - unsigned int hash = ((*key)&(MAX_SEGMENTS - 1)); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - - unsigned int hop_info = start_bucket->hop_info; - unsigned int mask = 1; - for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { - if (mask & hop_info) { - Bucket* check_bucket = start_bucket + i; - if (*key == *(check_bucket->_key)) { - int* rc = check_bucket->_data; - check_bucket->_key = NULL; - check_bucket->_data = NULL; - start_bucket->_hop_info &= ~(1 << i); - start_bucket->unlock(); - return rc; - } + //cout << "Not found!!" << endl; + return false; + } + bool add(KEY *key, DATA *data){ + int val = 1; + unsigned int hash=((*key)&(MAX_SEGMENTS-1)); + Bucket* start_bucket=segments_arys+hash; + start_bucket->lock(); + //sleep(1); + if(contains(key)){ + start_bucket->unlock(); + return false; + } + + Bucket* free_bucket=start_bucket; + int free_distance=0; + for(;free_distance_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key),NULL,BUSY)) + break; + ++free_bucket; + } + + if(free_distance < ADD_RANGE){ + do{ + if(free_distance < HOP_RANGE){ + start_bucket->_hop_info |= (1<_data=data; + free_bucket->_key=key; + start_bucket->unlock(); + return true; + } + find_closer_bucket(&free_bucket,&free_distance,val); + }while(0 != val); + } + start_bucket->unlock(); + //cout<<"Called Resize"<lock(); + + unsigned int hop_info = start_bucket->_hop_info; + unsigned int mask = 1; + for(int i=0;i_key)){ + int* rc = check_bucket->_data; + check_bucket->_key=NULL; + check_bucket->_data=NULL; + start_bucket->_hop_info &=~(1<unlock(); + return rc; } } - start_bucket->unlock(); - return NULL; } - concurrent_hopscotch_hash_set() { - segments_arys = new Bucket[MAX_SEGMENTS + 256]; - BUSY = (KEY *)malloc(sizeof(KEY)); - *BUSY = -1; + start_bucket->unlock(); + return NULL; + } + void find_closer_bucket(Bucket** free_bucket,int* free_distance,int &val) { + Bucket* move_bucket = *free_bucket -(HOP_RANGE-1); + for(int free_dist = (HOP_RANGE -1);free_dist>0;--free_dist) { + unsigned int start_hop_info = move_bucket->_hop_info; + int move_free_distance = -1; + unsigned int mask =1; + for (int i=0;ilock(); + if(start_hop_info == move_bucket->_hop_info) { + Bucket* new_free_bucket = move_bucket + move_free_distance; + move_bucket->_hop_info |= (1 << free_dist); + (*free_bucket)->_data = new_free_bucket->_data; + (*free_bucket)->_key = new_free_bucket->_key; + ++(move_bucket->_timestamp); + new_free_bucket->_key =BUSY; + new_free_bucket->_data =BUSY; + move_bucket->_hop_info &= ~(1<unlock(); + return; + } + move_bucket->unlock(); + } + ++move_bucket; } - // ~concurrent_hopscotch_hash_set(); + (*free_bucket)->_key = NULL; + val = 0; + *free_distance = 0; + } + }; } } -#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ \ No newline at end of file +#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ diff --git a/test/unit/main.cpp b/test/unit/main.cpp index f94a83446..8a5f42b24 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -28,15 +28,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +//#include #include #include -#include +//#include #include namespace cds_test { - /*static*/ std::random_device fixture::random_dev_; - /*static*/ std::mt19937 fixture::random_gen_( random_dev_() ); + /*static*/ //std::random_device fixture::random_dev_; + /*static*/ //std::mt19937 fixture::random_gen_( random_dev_() ); } // namespace cds_test From 78e476cc17b877d4d99a4a0f7605cfdecab55dbc Mon Sep 17 00:00:00 2001 From: Roman Stetskevich Date: Thu, 28 Dec 2017 13:06:29 +0300 Subject: [PATCH 04/19] =?UTF-8?q?=D0=A3=D1=85=D0=BE=D0=B4=20=D0=BE=D1=82?= =?UTF-8?q?=20pthreads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cds/container/concurrent_hopscotch_hash_set.h | 254 +++++++++--------- 1 file changed, 129 insertions(+), 125 deletions(-) diff --git a/cds/container/concurrent_hopscotch_hash_set.h b/cds/container/concurrent_hopscotch_hash_set.h index a7f4d8e06..0dc99e09b 100644 --- a/cds/container/concurrent_hopscotch_hash_set.h +++ b/cds/container/concurrent_hopscotch_hash_set.h @@ -2,171 +2,175 @@ * concurrent_hopscotch_hash_set.h * * Created on: 23 дек. 2017 г. -* Author: LEONID, ANDREY, ROMAN +* Author: LEONID, ANDREY, ROMAN */ #ifndef CONCURRENTHOPSCOTCHHASHSET_H_ #define CONCURRENTHOPSCOTCHHASHSET_H_ #include -#include +#include #include -#include -#include +#include namespace cds { namespace container { template - class Hopscotch { - private: + class concurrent_hopscotch_hashset { + private: static const int HOP_RANGE = 32; static const int ADD_RANGE = 256; - static const int MAX_SEGMENTS = 1048576; // Including neighbourhodd for last hash location + static const int MAX_SEGMENTS = 1048576; static const int MAX_TRIES = 2; - KEY* BUSY; - - struct Bucket { - + KEY* BUSY; + + struct Bucket { + unsigned int volatile _hop_info; KEY* volatile _key; DATA* volatile _data; - unsigned int volatile _lock; + unsigned int volatile _lock; unsigned int volatile _timestamp; - pthread_mutex_t lock_mutex; - pthread_cond_t lock_cv; + std::mutex lock_mutex; + // pthread_cond_t lock_cv; - Bucket(){ - _hop_info = 0; + Bucket() { + _hop_info = 0; _lock = 0; _key = NULL; - _data=NULL; + _data = NULL; _timestamp = 0; - pthread_mutex_init(&lock_mutex,NULL); - pthread_cond_init(&lock_cv, NULL); - } - - void lock(){ - - pthread_mutex_lock(&lock_mutex); - while(1){ - if (_lock==0){ - _lock =1; - pthread_mutex_unlock(&lock_mutex); - break; - } - pthread_cond_wait(&lock_cv, &lock_mutex); - - } } - void unlock(){ - pthread_mutex_lock(&lock_mutex); + void lock() { + lock_mutex.lock(); + // pthread_mutex_lock(&lock_mutex); + while (1) { + if (_lock == 0) { + _lock = 1; + lock_mutex.unlock(); + break; + } + //pthread_cond_wait(&lock_cv, &lock_mutex); + } + } + + void unlock() { + lock_mutex.lock(); _lock = 0; - pthread_cond_signal(&lock_cv); - pthread_mutex_unlock(&lock_mutex); + // pthread_cond_signal(&lock_cv); + lock_mutex.unlock(); } }; - + Bucket* segments_arys; - public: - Hopscotch(){ - segments_arys = new Bucket[MAX_SEGMENTS+256]; - BUSY=(int *)malloc(sizeof(int)); - *BUSY=-1; + int calc_hash(KEY* key) { + std::hash hash_fn; + return hash_fn(key); + } + + void resize() { + } - ~Hopscotch(); - bool contains(KEY* key){ - unsigned int hash = ((*key)&(MAX_SEGMENTS-1)); - Bucket* start_bucket = segments_arys+hash; + + public: + concurrent_hopscotch_hashset() { + segments_arys = new Bucket[MAX_SEGMENTS + 256]; + BUSY = (KEY*)std::malloc(sizeof(KEY)); + *BUSY = -1; + } + + ~concurrent_hopscotch_hashset() { + std::free(BUSY); + } + + bool contains(KEY* key) { + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; unsigned int try_counter = 0; unsigned int timestamp; - do{ - //cout << "Fast Path : try counter" << try_counter << endl; + do { timestamp = start_bucket->_timestamp; unsigned int hop_info = start_bucket->_hop_info; - Bucket* check_bucket = start_bucket; - unsigned int temp; - - for( int i = 0 ; i < HOP_RANGE ; i++){ - temp=hop_info; - temp=temp>>i; - - if(temp&1){ - if(*key == *(check_bucket->_key)){ - //cout << "Found!!" << endl; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (*key == *(check_bucket->_key)) { return true; - } + } } - ++check_bucket; } - ++try_counter; - }while(timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - //cout << "Slow path " << endl; - if(timestamp != start_bucket->_timestamp){ + } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + + if (timestamp != start_bucket->_timestamp) { Bucket* check_bucket = start_bucket; - for(int i=0; i_key)) + for (int i = 0; i_key)) return true; ++check_bucket; } } - //cout << "Not found!!" << endl; return false; } - bool add(KEY *key, DATA *data){ - int val = 1; - unsigned int hash=((*key)&(MAX_SEGMENTS-1)); - Bucket* start_bucket=segments_arys+hash; - start_bucket->lock(); - //sleep(1); - if(contains(key)){ - start_bucket->unlock(); - return false; - } - - Bucket* free_bucket=start_bucket; - int free_distance=0; - for(;free_distance_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key),NULL,BUSY)) - break; - ++free_bucket; - } - - if(free_distance < ADD_RANGE){ - do{ - if(free_distance < HOP_RANGE){ - start_bucket->_hop_info |= (1<_data=data; - free_bucket->_key=key; + bool add(KEY *key, DATA *data) { + int val = 1; + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + if (contains(key)) { start_bucket->unlock(); - return true; - } - find_closer_bucket(&free_bucket,&free_distance,val); - }while(0 != val); - } - start_bucket->unlock(); - //cout<<"Called Resize"<_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) + if (NULL == free_bucket->_key) + break; + ++free_bucket; + } + + if (free_distance < ADD_RANGE) { + do { + if (free_distance < HOP_RANGE) { + start_bucket->_hop_info |= (1 << free_distance); + free_bucket->_data = data; + free_bucket->_key = key; + start_bucket->unlock(); + return true; + } + find_closer_bucket(&free_bucket, &free_distance, val); + } while (0 != val); + } + start_bucket->unlock(); + + this->resize(); + + return false; + }; + DATA* remove(KEY *key) { + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; start_bucket->lock(); - + unsigned int hop_info = start_bucket->_hop_info; unsigned int mask = 1; - for(int i=0;i_key)){ + for (int i = 0; i_key)) { int* rc = check_bucket->_data; - check_bucket->_key=NULL; - check_bucket->_data=NULL; - start_bucket->_hop_info &=~(1<_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); start_bucket->unlock(); return rc; } @@ -175,31 +179,31 @@ namespace cds { start_bucket->unlock(); return NULL; } - void find_closer_bucket(Bucket** free_bucket,int* free_distance,int &val) { - Bucket* move_bucket = *free_bucket -(HOP_RANGE-1); - for(int free_dist = (HOP_RANGE -1);free_dist>0;--free_dist) { + void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { + Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); + for (int free_dist = (HOP_RANGE - 1); free_dist>0; --free_dist) { unsigned int start_hop_info = move_bucket->_hop_info; int move_free_distance = -1; - unsigned int mask =1; - for (int i=0;ilock(); - if(start_hop_info == move_bucket->_hop_info) { + if (start_hop_info == move_bucket->_hop_info) { Bucket* new_free_bucket = move_bucket + move_free_distance; move_bucket->_hop_info |= (1 << free_dist); (*free_bucket)->_data = new_free_bucket->_data; (*free_bucket)->_key = new_free_bucket->_key; ++(move_bucket->_timestamp); - new_free_bucket->_key =BUSY; - new_free_bucket->_data =BUSY; - move_bucket->_hop_info &= ~(1<_key = BUSY; + new_free_bucket->_data = BUSY; + move_bucket->_hop_info &= ~(1 << move_free_distance); *free_bucket = new_free_bucket; - *free_distance -=free_dist; + *free_distance -= free_dist; move_bucket->unlock(); return; } From 6547d7db66002c5efe3e150a24937e90b099cf38 Mon Sep 17 00:00:00 2001 From: Roman Stetskevich Date: Fri, 29 Dec 2017 13:21:30 +0300 Subject: [PATCH 05/19] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20get.=20=D0=91?= =?UTF-8?q?=D0=BE=D0=BB=D0=B5=D0=B5-=D0=BC=D0=B5=D0=BD=D0=B5=D0=B5=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cds/container/concurrent_hopscotch_hash_set.h | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/cds/container/concurrent_hopscotch_hash_set.h b/cds/container/concurrent_hopscotch_hash_set.h index 0dc99e09b..c8c3f323d 100644 --- a/cds/container/concurrent_hopscotch_hash_set.h +++ b/cds/container/concurrent_hopscotch_hash_set.h @@ -22,7 +22,6 @@ namespace cds { static const int MAX_SEGMENTS = 1048576; static const int MAX_TRIES = 2; KEY* BUSY; - struct Bucket { unsigned int volatile _hop_info; @@ -67,7 +66,7 @@ namespace cds { int calc_hash(KEY* key) { std::hash hash_fn; - return hash_fn(key); + return hash_fn(key) % MAX_SEGMENTS; } void resize() { @@ -78,7 +77,6 @@ namespace cds { concurrent_hopscotch_hashset() { segments_arys = new Bucket[MAX_SEGMENTS + 256]; BUSY = (KEY*)std::malloc(sizeof(KEY)); - *BUSY = -1; } ~concurrent_hopscotch_hashset() { @@ -86,6 +84,10 @@ namespace cds { } bool contains(KEY* key) { + return get(key) != NULL; + } + + DATA* get(KEY* key) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; unsigned int try_counter = 0; @@ -101,7 +103,7 @@ namespace cds { if (temp & 1) { if (*key == *(check_bucket->_key)) { - return true; + return check_bucket->_data; } } ++check_bucket; @@ -113,12 +115,13 @@ namespace cds { Bucket* check_bucket = start_bucket; for (int i = 0; i_key)) - return true; + return check_bucket->_data; ++check_bucket; } } - return false; + return NULL; } + bool add(KEY *key, DATA *data) { int val = 1; unsigned int hash = calc_hash(key); @@ -132,8 +135,9 @@ namespace cds { Bucket* free_bucket = start_bucket; int free_distance = 0; for (; free_distance_key && NULL == __sync_val_compare_and_swap(&(free_bucket->_key), NULL, BUSY)) - if (NULL == free_bucket->_key) + std::atomic _atomic = free_bucket->_key; + KEY* _null_key = NULL; + if (NULL == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) break; ++free_bucket; } @@ -167,7 +171,7 @@ namespace cds { if (mask & hop_info) { Bucket* check_bucket = start_bucket + i; if (*key == *(check_bucket->_key)) { - int* rc = check_bucket->_data; + DATA* rc = check_bucket->_data; check_bucket->_key = NULL; check_bucket->_data = NULL; start_bucket->_hop_info &= ~(1 << i); From c87179d2ece58d278dcd6c40069790e3b6566e24 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Thu, 4 Jan 2018 22:18:01 +0300 Subject: [PATCH 06/19] Trying to add tests --- ...pscotch_hash_set.h => hopscotch_hashmap.h} | 46 +- projects/Win/vc141/cds.sln | 25 +- projects/Win/vc141/cds.vcxproj | 2 +- projects/Win/vc141/cds.vcxproj.filters | 2 +- .../Win/vc141/gtest-map-hopscotch.vcxproj | 440 ++++++++++++++++ test/unit/main.cpp | 135 +---- test/unit/map/hopscotch_hashmap.cpp | 196 +++++++ test/unit/map/test_hopscotch_hashmap.h | 482 ++++++++++++++++++ 8 files changed, 1174 insertions(+), 154 deletions(-) rename cds/container/{concurrent_hopscotch_hash_set.h => hopscotch_hashmap.h} (84%) create mode 100644 projects/Win/vc141/gtest-map-hopscotch.vcxproj create mode 100644 test/unit/map/hopscotch_hashmap.cpp create mode 100644 test/unit/map/test_hopscotch_hashmap.h diff --git a/cds/container/concurrent_hopscotch_hash_set.h b/cds/container/hopscotch_hashmap.h similarity index 84% rename from cds/container/concurrent_hopscotch_hash_set.h rename to cds/container/hopscotch_hashmap.h index c8c3f323d..35bb53ac9 100644 --- a/cds/container/concurrent_hopscotch_hash_set.h +++ b/cds/container/hopscotch_hashmap.h @@ -1,12 +1,15 @@ /* * concurrent_hopscotch_hash_set.h * -* Created on: 23 дек. 2017 г. -* Author: LEONID, ANDREY, ROMAN +* Created on: 23.12.2017 +* Original idea by Maurice Herlihy, Nir Shavit, Moran Tzafrir: http://people.csail.mit.edu/shanir/publications/disc2008_submission_98.pdf +* Based on implementation by Sathya Hariesh Prakash, Royston Monteiro, Mohan Pandiyan: https://github.com/harieshsathya/Hopscotch-Hashing +* +* This version modifiers: Leonid Skorospelov, Andrey Chulanov, Roman Stetskevich */ -#ifndef CONCURRENTHOPSCOTCHHASHSET_H_ -#define CONCURRENTHOPSCOTCHHASHSET_H_ +#ifndef HOPSCOTCHHASHMAP_H_ +#define HOPSCOTCHHASHMAP_H_ #include #include #include @@ -15,7 +18,7 @@ namespace cds { namespace container { template - class concurrent_hopscotch_hashset { + class hopscotch_hashmap { private: static const int HOP_RANGE = 32; static const int ADD_RANGE = 256; @@ -24,13 +27,12 @@ namespace cds { KEY* BUSY; struct Bucket { - unsigned int volatile _hop_info; - KEY* volatile _key; - DATA* volatile _data; - unsigned int volatile _lock; - unsigned int volatile _timestamp; + std::atomic _hop_info; + std::atomic volatile _key; + std::atomic volatile _data; + std::atomic _lock; + std::atomic _timestamp; std::mutex lock_mutex; - // pthread_cond_t lock_cv; Bucket() { _hop_info = 0; @@ -42,21 +44,18 @@ namespace cds { void lock() { lock_mutex.lock(); - // pthread_mutex_lock(&lock_mutex); while (1) { if (_lock == 0) { _lock = 1; lock_mutex.unlock(); break; } - //pthread_cond_wait(&lock_cv, &lock_mutex); } } void unlock() { lock_mutex.lock(); _lock = 0; - // pthread_cond_signal(&lock_cv); lock_mutex.unlock(); } @@ -70,17 +69,18 @@ namespace cds { } void resize() { - + // TODO need to find out if we need one } public: - concurrent_hopscotch_hashset() { + hopscotch_hashmap() { segments_arys = new Bucket[MAX_SEGMENTS + 256]; BUSY = (KEY*)std::malloc(sizeof(KEY)); } - ~concurrent_hopscotch_hashset() { + ~hopscotch_hashmap() { std::free(BUSY); + std::free(segments_arys); } bool contains(KEY* key) { @@ -114,8 +114,9 @@ namespace cds { if (timestamp != start_bucket->_timestamp) { Bucket* check_bucket = start_bucket; for (int i = 0; i_key)) + if (*key == *(check_bucket->_key)) { return check_bucket->_data; + } ++check_bucket; } } @@ -137,8 +138,9 @@ namespace cds { for (; free_distance _atomic = free_bucket->_key; KEY* _null_key = NULL; - if (NULL == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) + if (NULL == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { break; + } ++free_bucket; } @@ -159,7 +161,8 @@ namespace cds { this->resize(); return false; - }; + } + DATA* remove(KEY *key) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; @@ -183,6 +186,7 @@ namespace cds { start_bucket->unlock(); return NULL; } + void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); for (int free_dist = (HOP_RANGE - 1); free_dist>0; --free_dist) { @@ -224,4 +228,4 @@ namespace cds { } } -#endif /* CONCURRENTHOPSCOTCHHASHSET_H_ */ +#endif /* HOPSCOTCHHASHMAP_H_ */ diff --git a/projects/Win/vc141/cds.sln b/projects/Win/vc141/cds.sln index 86e588d2a..617e261e0 100644 --- a/projects/Win/vc141/cds.sln +++ b/projects/Win/vc141/cds.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2010 +VisualStudioVersion = 15.0.27130.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cds", "cds.vcxproj", "{408FE9BC-44F0-4E6A-89FA-D6F952584239}" EndProject @@ -459,6 +459,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress-spsc-queue", "stress {408FE9BC-44F0-4E6A-89FA-D6F952584239} = {408FE9BC-44F0-4E6A-89FA-D6F952584239} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-map-hopscotch", "gtest-map-hopscotch.vcxproj", "{C6099C17-DF98-45AF-92F6-6B40169D39FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -1873,6 +1875,26 @@ Global {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.Build.0 = vc14-Release|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|Win32.ActiveCfg = Debug|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|Win32.Build.0 = Debug|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|x64.ActiveCfg = Debug|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|x64.Build.0 = Debug|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|x64.Build.0 = DebugVLD|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|Win32.ActiveCfg = Release|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|Win32.Build.0 = Release|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|x64.ActiveCfg = Release|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|x64.Build.0 = Release|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 + {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|x64.Build.0 = vc14-Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1960,6 +1982,7 @@ Global {EACBF8EC-C58F-4CC8-9435-21BE9812287A} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {47B9F1F9-C9F1-4F25-842B-EE8E6447FE50} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {CDC54B7B-B882-4CC8-87A2-E237D0D945B2} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} + {C6099C17-DF98-45AF-92F6-6B40169D39FA} = {10DC2D86-83B3-428A-8190-463EF4ED03E3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3673AF8B-E81E-49E1-8241-B5FC94791FB8} diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index a86392193..cd92882d9 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -659,7 +659,7 @@ - + diff --git a/projects/Win/vc141/cds.vcxproj.filters b/projects/Win/vc141/cds.vcxproj.filters index 721e05b59..708c1798c 100644 --- a/projects/Win/vc141/cds.vcxproj.filters +++ b/projects/Win/vc141/cds.vcxproj.filters @@ -1261,7 +1261,7 @@ Header Files\cds\details - + Header Files\cds\container diff --git a/projects/Win/vc141/gtest-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-map-hopscotch.vcxproj new file mode 100644 index 000000000..e0eb017d2 --- /dev/null +++ b/projects/Win/vc141/gtest-map-hopscotch.vcxproj @@ -0,0 +1,440 @@ +п»ї + + + + DebugVLD + Win32 + + + DebugVLD + x64 + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + vc14-Debug + Win32 + + + vc14-Debug + x64 + + + vc14-Release + Win32 + + + vc14-Release + x64 + + + + + + + + + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + + + + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + 4503 + + + + + + + + + + + + + + + + {C6099C17-DF98-45AF-92F6-6B40169D39FA} + Win32Proj + map_skip + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + true + v140 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + true + v140 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + true + true + true + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + true + true + true + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + true + true + true + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj /Zc:inline /permissive- %(AdditionalOptions) + 4503 + + + Console + true + true + true + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 8a5f42b24..84e9ae7b8 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -28,147 +28,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -//#include +#include #include -#include -//#include +#include #include namespace cds_test { - /*static*/ //std::random_device fixture::random_dev_; - /*static*/ //std::mt19937 fixture::random_gen_( random_dev_() ); + /*static*/ std::random_device fixture::random_dev_; + /*static*/ std::mt19937 fixture::random_gen_( random_dev_() ); } // namespace cds_test using namespace std; - -cds::container::concurrent_hopscotch_hash_set *obj; -void testInt() { - obj = new cds::container::concurrent_hopscotch_hash_set(); - int key1 = 0; - int val1 = 100; - int key2 = 1; - int val2 = 200; - int key3 = 1025; - int val3 = 300; - - obj->add(&key1, &val1); - cout << "Add 1 complete" << endl; - obj->add(&key2, &val2); - cout << "Add 2 completes" << endl; - obj->add(&key3, &val3); - cout << "Add 3 completes" << endl; - - if (obj->contains(&key1)) - cout << "Key 1 present" << endl; - if (obj->contains(&key2)) - cout << "Key 2 present" << endl; - - if (obj->contains(&key3)) - cout << "Key 3 present" << endl; - -} - -//---------------------------------------- - -void* adding() { - int key = rand() & 1048575; - int value = rand() & 1048575; - if(!obj->add(&key, &value)) - throw exception("Fuuuuu!!!"); -} - -void testIntAddParallel() { - obj = new cds::container::concurrent_hopscotch_hash_set(); - pthread_t threads[1000]; - for (int r = 0; r<1000; r++) { - pthread_create(&threads[r], NULL, adding); - } - for (int r = 0; r<1000; r++) { - pthread_join(threads[r], NULL); - } -} - -//----------------------------------------------- - -void* contains(void * arg) { - int *data = reinterpret_cast(arg); - if (!obj->contains(data)) - throw exception("Fuuuuu!!!"); -} - -void testIntContainsParallel() { - obj = new cds::container::concurrent_hopscotch_hash_set(); - for (int r = 0; r<1000; r++) { - obj->add(&r, &r); - } - pthread_t threads[1000]; - for (int r = 0; r<1000; r++) { - pthread_create(&threads[r], NULL, contains, new int(r)); - } - for (int r = 0; r<1000; r++) { - pthread_join(threads[r], NULL); - } -} - -//------------------------- - -void* removing(void * arg) { - int *data = reinterpret_cast(arg); - if (obj->remove(data) == NULL) - throw exception("Fuuuuu!!!"); -} - -void testIntRemoveParallel() { - obj = new cds::container::concurrent_hopscotch_hash_set(); - for (int r = 0; r<1000; r++) { - obj->add(&r, &r); - } - pthread_t threads[1000]; - for (int r = 0; r<1000; r++) { - pthread_create(&threads[r], NULL, removing, new int(r)); - } - for (int r = 0; r<1000; r++) { - pthread_join(threads[r], NULL); - } -} - -//------------------------- - -void* getting(void * arg) { - int *data = reinterpret_cast(arg); - int* res = obj->get(data); - if (res == NULL) - throw exception("Bad return!!!"); - - if (*res != *data) { - throw exception("Not equals!!!"); - } -} - -void testIntGetParallel() { - obj = new cds::container::concurrent_hopscotch_hash_set(); - for (int r = 0; r<1000; r++) { - obj->add(&r, &r); - } - pthread_t threads[1000]; - for (int r = 0; r<1000; r++) { - pthread_create(&threads[r], NULL, getting, new int(r)); - } - for (int r = 0; r<1000; r++) { - pthread_join(threads[r], NULL); - } -} - int main( int argc, char **argv ) { - testInt(); - testIntAddParallel(); - testIntContainsParallel(); - testIntGetParallel(); - testIntRemoveParallel(); - int result; + int result = 0; cds::Initialize(); { ::testing::InitGoogleTest( &argc, argv ); diff --git a/test/unit/map/hopscotch_hashmap.cpp b/test/unit/map/hopscotch_hashmap.cpp new file mode 100644 index 000000000..a0775f4e3 --- /dev/null +++ b/test/unit/map/hopscotch_hashmap.cpp @@ -0,0 +1,196 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "test_feldman_hashmap_hp.h" + +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::HP gc_type; + + class HopscotchHashMap : public cds_test::hopscotch_hashmap + { + protected: + typedef cds_test::feldman_hashmap_hp base_class; + + void SetUp() + { + typedef cc::hopscotch_hashmap< key_type, value_type > map_type; + + // +1 - for guarded_ptr + cds::gc::hp::GarbageCollector::Construct( map_type::c_nHazardPtrCount + 1, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + TEST_F( FeldmanHashMap_HP, defaulted ) + { + typedef cc::FeldmanHashMap< gc_type, key_type, value_type > map_type; + + map_type m; + test( m ); + } + + TEST_F( FeldmanHashMap_HP, compare ) + { + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, + typename cc::feldman_hashmap::make_traits< + cds::opt::compare< cmp > + >::type + > map_type; + + map_type m( 4, 5 ); + EXPECT_GE( m.head_size(), static_cast( 1 << 4 )); + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 5 )); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, less ) + { + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, + typename cc::feldman_hashmap::make_traits< + cds::opt::less< less > + >::type + > map_type; + + map_type m( 3, 2 ); + EXPECT_GE( m.head_size(), static_cast( 1 << 4 )); // min = 2 ** 4 + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, cmpmix ) + { + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, + typename cc::feldman_hashmap::make_traits< + cds::opt::less< less > + ,cds::opt::compare< cmp > + >::type + > map_type; + + map_type m( 4, 4 ); + EXPECT_EQ( m.head_size(), static_cast( 1 << 4 )); + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 4 )); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, backoff ) + { + struct map_traits: public cc::feldman_hashmap::traits + { + typedef cmp compare; + typedef cds::atomicity::item_counter item_counter; + typedef cds::backoff::yield back_off; + }; + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, map_traits > map_type; + + map_type m( 8, 2 ); + EXPECT_EQ( m.head_size(), static_cast( 1 << 8 )); + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, stat ) + { + struct map_traits: public cc::feldman_hashmap::traits + { + typedef cds::backoff::yield back_off; + typedef cc::feldman_hashmap::stat<> stat; + }; + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, map_traits > map_type; + + map_type m( 1, 1 ); + EXPECT_EQ( m.head_size(), static_cast( 1 << 4 )); // min = 2**4 + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); // min = 2**2 + test( m ); + } + + TEST_F( FeldmanHashMap_HP, explicit_key_size ) + { + struct map_traits: public cc::feldman_hashmap::traits + { + enum: size_t { + hash_size = sizeof(int) + sizeof( uint16_t) + }; + typedef hash2 hash; + typedef less2 less; + typedef cc::feldman_hashmap::stat<> stat; + }; + typedef cc::FeldmanHashMap< gc_type, key_type2, value_type, map_traits > map_type; + + map_type m( 5, 3 ); + EXPECT_EQ( m.head_size(), static_cast(1 << 6)); + EXPECT_EQ( m.array_node_size(), static_cast(1 << 3)); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, byte_cut ) + { + typedef cc::FeldmanHashMap< gc_type, key_type, value_type, + typename cc::feldman_hashmap::make_traits< + cds::opt::compare< cmp > + , cc::feldman_hashmap::hash_splitter< cds::algo::byte_splitter< key_type >> + >::type + > map_type; + + map_type m( 8, 8 ); + EXPECT_EQ( m.head_size(), static_cast( 1 << 8 )); + EXPECT_EQ( m.array_node_size(), static_cast( 1 << 8 )); + test( m ); + } + + TEST_F( FeldmanHashMap_HP, byte_cut_explicit_key_size ) + { + struct map_traits: public cc::feldman_hashmap::traits + { + enum: size_t { + hash_size = sizeof(int) + sizeof( uint16_t) + }; + typedef cds::algo::byte_splitter< key_type2, hash_size > hash_splitter; + typedef hash2 hash; + typedef less2 less; + typedef cc::feldman_hashmap::stat<> stat; + }; + typedef cc::FeldmanHashMap< gc_type, key_type2, value_type, map_traits > map_type; + + map_type m( 8, 8 ); + EXPECT_EQ( m.head_size(), static_cast(1 << 8)); + EXPECT_EQ( m.array_node_size(), static_cast(1 << 8)); + test( m ); + } + +} // namespace diff --git a/test/unit/map/test_hopscotch_hashmap.h b/test/unit/map/test_hopscotch_hashmap.h new file mode 100644 index 000000000..b16b6a1e9 --- /dev/null +++ b/test/unit/map/test_hopscotch_hashmap.h @@ -0,0 +1,482 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ +#define CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ + +#include "test_map_data.h" + +// forward declaration +namespace cds { namespace container {}} +namespace co = cds::opt; +namespace cc = cds::container; + +namespace cds_test { + + class hopscotch_hashmap : public map_fixture + { + public: + static size_t const kSize = 1000; + + struct key_type2 { + int nKey; + uint16_t subkey; + + explicit key_type2( int n ) + : nKey( n ) + , subkey( static_cast( n )) + {} + + explicit key_type2( size_t n ) + : nKey( static_cast( n )) + , subkey( static_cast( n )) + {} + + explicit key_type2( std::string const& str ) + : nKey( std::stoi( str )) + , subkey( static_cast( nKey )) + {} + + key_type2( key_type2 const& s ) + : nKey( s.nKey ) + , subkey( s.subkey ) + {} + }; + + struct less2 + { + bool operator ()( key_type2 const& v1, key_type2 const& v2 ) const + { + return v1.nKey < v2.nKey; + } + + bool operator ()( key_type2 const& v1, int v2 ) const + { + return v1.nKey < v2; + } + + bool operator ()( int v1, key_type2 const& v2 ) const + { + return v1 < v2.nKey; + } + + bool operator ()( key_type2 const& v1, std::string const& v2 ) const + { + return v1.nKey < std::stoi( v2 ); + } + + bool operator ()( std::string const& v1, key_type2 const& v2 ) const + { + return std::stoi( v1 ) < v2.nKey; + } + }; + + struct cmp2 { + int operator ()( key_type2 const& v1, key_type2 const& v2 ) const + { + if ( v1.nKey < v2.nKey ) + return -1; + return v1.nKey > v2.nKey ? 1 : 0; + } + + int operator ()( key_type2 const& v1, int v2 ) const + { + if ( v1.nKey < v2 ) + return -1; + return v1.nKey > v2 ? 1 : 0; + } + + int operator ()( int v1, key_type2 const& v2 ) const + { + if ( v1 < v2.nKey ) + return -1; + return v1 > v2.nKey ? 1 : 0; + } + + int operator ()( key_type2 const& v1, std::string const& v2 ) const + { + int n2 = std::stoi( v2 ); + if ( v1.nKey < n2 ) + return -1; + return v1.nKey > n2 ? 1 : 0; + } + + int operator ()( std::string const& v1, key_type2 const& v2 ) const + { + int n1 = std::stoi( v1 ); + if ( n1 < v2.nKey ) + return -1; + return n1 > v2.nKey ? 1 : 0; + } + }; + + struct hash2 { + key_type2 operator()( int i ) const + { + return key_type2( cds::opt::v::hash()(i)); + } + + key_type2 operator()( std::string const& str ) const + { + return key_type2( cds::opt::v::hash()(std::stoi( str ))); + } + + template + key_type2 operator()( T const& i ) const + { + return key_type2( cds::opt::v::hash()(i.nKey)); + } + }; + + + protected: + template + void test( Map& m ) + { + // Precondition: map is empty + // Postcondition: map is empty + + ASSERT_TRUE( m.empty()); + ASSERT_CONTAINER_SIZE( m, 0 ); + + typedef typename Map::key_type key_type; + typedef typename Map::mapped_type value_type; + typedef typename Map::value_type map_pair; + size_t const kkSize = kSize; + + std::vector arrKeys; + for ( int i = 0; i < static_cast(kkSize); ++i ) + arrKeys.push_back( key_type( i )); + shuffle( arrKeys.begin(), arrKeys.end()); + + std::vector< value_type > arrVals; + for ( size_t i = 0; i < kkSize; ++i ) { + value_type val; + val.nVal = static_cast(i); + val.strVal = std::to_string( i ); + arrVals.push_back( val ); + } + + // insert/find + for ( auto const& i : arrKeys ) { + value_type const& val( arrVals.at( i.nKey )); + + ASSERT_FALSE( m.contains( i.nKey )); + ASSERT_FALSE( m.contains( i )); + ASSERT_FALSE( m.find( i, []( map_pair const& ) { + ASSERT_TRUE( false ); + } )); + ASSERT_FALSE( m.find( i.nKey, []( map_pair const& ) { + EXPECT_TRUE( false ); + } )); + + std::pair< bool, bool > updResult; + + switch ( i.nKey % 16 ) { + case 0: + ASSERT_TRUE( m.insert( i )); + ASSERT_FALSE( m.insert( i )); + ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + break; + case 1: + ASSERT_TRUE( m.insert( i.nKey )); + ASSERT_FALSE( m.insert( i.nKey )); + ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + break; + case 2: + ASSERT_TRUE( m.insert( std::to_string( i.nKey ))); + ASSERT_FALSE( m.insert( std::to_string( i.nKey ))); + ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + break; + case 3: + ASSERT_TRUE( m.insert( i, val )); + ASSERT_FALSE( m.insert( i, val )); + break; + case 4: + ASSERT_TRUE( m.insert( i.nKey, val.strVal )); + ASSERT_FALSE( m.insert( i.nKey, val.strVal )); + break; + case 5: + ASSERT_TRUE( m.insert( val.strVal, i.nKey )); + ASSERT_FALSE( m.insert( val.strVal, i.nKey )); + break; + case 6: + ASSERT_TRUE( m.insert_with( i, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + ASSERT_FALSE( m.insert_with( i, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + case 7: + ASSERT_TRUE( m.insert_with( i.nKey, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + ASSERT_FALSE( m.insert_with( i.nKey, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + case 8: + ASSERT_TRUE( m.insert_with( val.strVal, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + ASSERT_FALSE( m.insert_with( val.strVal, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + case 9: + updResult = m.update( i.nKey, []( map_pair&, map_pair* ) { + EXPECT_TRUE( false ); + }, false ); + ASSERT_FALSE( updResult.first ); + ASSERT_FALSE( updResult.second ); + + updResult = m.update( i.nKey, []( map_pair& v, map_pair* old ) { + EXPECT_TRUE( old == nullptr ); + v.second.nVal = v.first.nKey; + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_TRUE( updResult.second ); + + updResult = m.update( i.nKey, []( map_pair& v, map_pair* old ) { + ASSERT_FALSE( old == nullptr ); + EXPECT_EQ( v.first.nKey, old->second.nVal ); + v.second.nVal = old->second.nVal; + v.second.strVal = std::to_string( v.second.nVal ); + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_FALSE( updResult.second ); + break; + case 10: + updResult = m.update( i, []( map_pair&, map_pair* ) { + EXPECT_TRUE( false ); + }, false ); + ASSERT_FALSE( updResult.first ); + ASSERT_FALSE( updResult.second ); + + updResult = m.update( i, []( map_pair& v, map_pair* old ) { + EXPECT_TRUE( old == nullptr ); + v.second.nVal = v.first.nKey; + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_TRUE( updResult.second ); + + updResult = m.update( i, []( map_pair& v, map_pair* old ) { + ASSERT_FALSE( old == nullptr ); + EXPECT_EQ( v.first.nKey, old->second.nVal ); + v.second.nVal = old->second.nVal; + v.second.strVal = std::to_string( v.second.nVal ); + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_FALSE( updResult.second ); + break; + case 11: + updResult = m.update( val.strVal, []( map_pair&, map_pair* ) { + EXPECT_TRUE( false ); + }, false ); + ASSERT_FALSE( updResult.first ); + ASSERT_FALSE( updResult.second ); + + updResult = m.update( val.strVal, []( map_pair& v, map_pair* old ) { + EXPECT_TRUE( old == nullptr ); + v.second.nVal = v.first.nKey; + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_TRUE( updResult.second ); + + updResult = m.update( val.strVal, []( map_pair& v, map_pair* old ) { + ASSERT_FALSE( old == nullptr ); + EXPECT_EQ( v.first.nKey, old->second.nVal ); + v.second.nVal = old->second.nVal; + v.second.strVal = std::to_string( v.second.nVal ); + } ); + ASSERT_TRUE( updResult.first ); + ASSERT_FALSE( updResult.second ); + break; + case 12: + ASSERT_TRUE( m.emplace( i.nKey )); + ASSERT_FALSE( m.emplace( i.nKey )); + ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { + v.second.nVal = v.first.nKey; + v.second.strVal = std::to_string( v.first.nKey ); + } )); + break; + case 13: + ASSERT_TRUE( m.emplace( i, i.nKey )); + ASSERT_FALSE( m.emplace( i, i.nKey )); + break; + case 14: + { + std::string str = val.strVal; + ASSERT_TRUE( m.emplace( i, std::move( str ))); + ASSERT_TRUE( str.empty()); + str = val.strVal; + ASSERT_FALSE( m.emplace( i, std::move( str ))); + ASSERT_TRUE( str.empty()); + } + break; + case 15: + { + std::string str = val.strVal; + ASSERT_TRUE( m.emplace( i, i.nKey, std::move( str ))); + ASSERT_TRUE( str.empty()); + str = val.strVal; + ASSERT_FALSE( m.emplace( i, i.nKey, std::move( str ))); + ASSERT_TRUE( str.empty()); + } + break; + } + + ASSERT_TRUE( m.contains( i.nKey )); + ASSERT_TRUE( m.contains( i )); + ASSERT_TRUE( m.find( i, []( map_pair const& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + ASSERT_TRUE( m.find( i.nKey, []( map_pair const& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + } + ASSERT_FALSE( m.empty()); + ASSERT_CONTAINER_SIZE( m, kkSize ); + ASSERT_FALSE( m.begin() == m.end()); + ASSERT_FALSE( m.cbegin() == m.cend()); + + shuffle( arrKeys.begin(), arrKeys.end()); + + { + std::vector< typename Map::level_statistics > vect; + m.get_level_statistics( vect ); + } + + // erase/find + for ( auto const& i : arrKeys ) { + value_type const& val( arrVals.at( i.nKey )); + + ASSERT_TRUE( m.contains( i.nKey )); + ASSERT_TRUE( m.contains( val.strVal )); + ASSERT_TRUE( m.contains( i )); + ASSERT_TRUE( m.find( i, []( map_pair const& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + ASSERT_TRUE( m.find( i.nKey, []( map_pair const& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + + switch ( i.nKey % 6 ) { + case 0: + ASSERT_TRUE( m.erase( i )); + ASSERT_FALSE( m.erase( i )); + break; + case 1: + ASSERT_TRUE( m.erase( i.nKey )); + ASSERT_FALSE( m.erase( i.nKey )); + break; + case 2: + ASSERT_TRUE( m.erase( val.strVal )); + ASSERT_FALSE( m.erase( val.strVal )); + break; + case 3: + ASSERT_TRUE( m.erase( i, []( map_pair& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + ASSERT_FALSE( m.erase( i, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + case 4: + ASSERT_TRUE( m.erase( i.nKey, []( map_pair& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + ASSERT_FALSE( m.erase( i.nKey, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + case 5: + ASSERT_TRUE( m.erase( val.strVal, []( map_pair& v ) { + EXPECT_EQ( v.first.nKey, v.second.nVal ); + EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); + } )); + ASSERT_FALSE( m.erase( val.strVal, []( map_pair& ) { + EXPECT_TRUE( false ); + } )); + break; + } + + ASSERT_FALSE( m.contains( i.nKey )); + ASSERT_FALSE( m.contains( i )); + ASSERT_FALSE( m.contains( val.strVal )); + ASSERT_FALSE( m.find( i, []( map_pair const& ) { + ASSERT_TRUE( false ); + } )); + ASSERT_FALSE( m.find( i.nKey, []( map_pair const& ) { + EXPECT_TRUE( false ); + } )); + } + ASSERT_TRUE( m.empty()); + ASSERT_CONTAINER_SIZE( m, 0 ); + + ASSERT_TRUE( m.begin() == m.end()); + ASSERT_TRUE( m.cbegin() == m.cend()); + + // clear + for ( auto const& i : arrKeys ) + ASSERT_TRUE( m.insert( i )); + + ASSERT_FALSE( m.empty()); + ASSERT_CONTAINER_SIZE( m, kkSize ); + + m.clear(); + + ASSERT_TRUE( m.empty()); + ASSERT_CONTAINER_SIZE( m, 0 ); + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ From 72c68746d98a1746d902ce68f6ad45044ccd816d Mon Sep 17 00:00:00 2001 From: LeoSko Date: Mon, 8 Jan 2018 04:00:03 +0300 Subject: [PATCH 07/19] Gonna add methods --- cds/container/hopscotch_hashmap.h | 87 ++-- projects/Win/vc141/cds.sln | 44 +- .../Win/vc141/gtest-map-hopscotch.vcxproj | 1 + test/unit/map/hopscotch_hashmap.cpp | 196 ------- test/unit/map/test_hopscotch_hashmap.h | 482 ------------------ test/unit/striped-map/hopscotch_hashmap.cpp | 71 +++ 6 files changed, 151 insertions(+), 730 deletions(-) delete mode 100644 test/unit/map/hopscotch_hashmap.cpp delete mode 100644 test/unit/map/test_hopscotch_hashmap.h create mode 100644 test/unit/striped-map/hopscotch_hashmap.cpp diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index 35bb53ac9..591733e35 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -18,27 +18,37 @@ namespace cds { namespace container { template - class hopscotch_hashmap { + struct hopscotch_hashmap { + + typedef KEY key_type; ///< key type + typedef DATA mapped_type; ///< type of value stored in the map + typedef std::pair value_type; ///< Pair type + private: static const int HOP_RANGE = 32; static const int ADD_RANGE = 256; static const int MAX_SEGMENTS = 1048576; static const int MAX_TRIES = 2; KEY* BUSY; + DATA* BUSYD; struct Bucket { + static const unsigned int _empty_hop_info = 0; + KEY* _empty_key = NULL; + DATA* _empty_data = NULL; + std::atomic _hop_info; - std::atomic volatile _key; - std::atomic volatile _data; + KEY* _key; + DATA* _data; std::atomic _lock; std::atomic _timestamp; std::mutex lock_mutex; Bucket() { - _hop_info = 0; + _hop_info = _empty_hop_info; _lock = 0; - _key = NULL; - _data = NULL; + _key = _empty_key; + _data = _empty_data; _timestamp = 0; } @@ -63,8 +73,8 @@ namespace cds { Bucket* segments_arys; - int calc_hash(KEY* key) { - std::hash hash_fn; + int calc_hash(KEY key) { + std::hash hash_fn; return hash_fn(key) % MAX_SEGMENTS; } @@ -73,8 +83,10 @@ namespace cds { } public: + static bool const c_isSorted = false; ///< whether the probe set should be ordered + hopscotch_hashmap() { - segments_arys = new Bucket[MAX_SEGMENTS + 256]; + segments_arys = new Bucket[MAX_SEGMENTS + ADD_RANGE]; BUSY = (KEY*)std::malloc(sizeof(KEY)); } @@ -83,11 +95,26 @@ namespace cds { std::free(segments_arys); } - bool contains(KEY* key) { + bool empty() { + return size() == 0; + } + + int size() { + unsigned int counter = 0; + const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); + for (unsigned int iElm = 0; iElm < num_elm; ++iElm) { + if (Bucket::_empty_hop_info != (segments_arys + iElm)->_hop_info) { + counter += __popcnt((segments_arys + iElm)->_hop_info); + } + } + return counter; + } + + bool contains(KEY key) { return get(key) != NULL; } - DATA* get(KEY* key) { + DATA get(KEY key) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; unsigned int try_counter = 0; @@ -102,8 +129,8 @@ namespace cds { temp = temp >> i; if (temp & 1) { - if (*key == *(check_bucket->_key)) { - return check_bucket->_data; + if (key == *(check_bucket->_key)) { + return *(check_bucket->_data); } } ++check_bucket; @@ -113,9 +140,9 @@ namespace cds { if (timestamp != start_bucket->_timestamp) { Bucket* check_bucket = start_bucket; - for (int i = 0; i_key)) { - return check_bucket->_data; + for (int i = 0; i < HOP_RANGE; i++) { + if (key == *(check_bucket->_key)) { + return *(check_bucket->_data); } ++check_bucket; } @@ -123,7 +150,7 @@ namespace cds { return NULL; } - bool add(KEY *key, DATA *data) { + bool add(KEY key, DATA data) { int val = 1; unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; @@ -135,10 +162,10 @@ namespace cds { Bucket* free_bucket = start_bucket; int free_distance = 0; - for (; free_distance _atomic = free_bucket->_key; - KEY* _null_key = NULL; - if (NULL == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { + for (; free_distance < ADD_RANGE; ++free_distance) { + std::atomic _atomic = free_bucket->_key; + KEY* _null_key = Bucket::_empty_key; + if (_null_key == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { break; } ++free_bucket; @@ -148,8 +175,8 @@ namespace cds { do { if (free_distance < HOP_RANGE) { start_bucket->_hop_info |= (1 << free_distance); - free_bucket->_data = data; - free_bucket->_key = key; + *(free_bucket->_data) = data; + *(free_bucket->_key) = key; start_bucket->unlock(); return true; } @@ -163,18 +190,18 @@ namespace cds { return false; } - DATA* remove(KEY *key) { + DATA remove(KEY key) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; start_bucket->lock(); unsigned int hop_info = start_bucket->_hop_info; unsigned int mask = 1; - for (int i = 0; i_key)) { - DATA* rc = check_bucket->_data; + if (key == *(check_bucket->_key)) { + DATA rc = *(check_bucket->_data); check_bucket->_key = NULL; check_bucket->_data = NULL; start_bucket->_hop_info &= ~(1 << i); @@ -189,11 +216,11 @@ namespace cds { void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); - for (int free_dist = (HOP_RANGE - 1); free_dist>0; --free_dist) { + for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { unsigned int start_hop_info = move_bucket->_hop_info; int move_free_distance = -1; unsigned int mask = 1; - for (int i = 0; i_key = new_free_bucket->_key; ++(move_bucket->_timestamp); new_free_bucket->_key = BUSY; - new_free_bucket->_data = BUSY; + new_free_bucket->_data = BUSYD; move_bucket->_hop_info &= ~(1 << move_free_distance); *free_bucket = new_free_bucket; *free_distance -= free_dist; diff --git a/projects/Win/vc141/cds.sln b/projects/Win/vc141/cds.sln index 617e261e0..4b61eb8d4 100644 --- a/projects/Win/vc141/cds.sln +++ b/projects/Win/vc141/cds.sln @@ -459,7 +459,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress-spsc-queue", "stress {408FE9BC-44F0-4E6A-89FA-D6F952584239} = {408FE9BC-44F0-4E6A-89FA-D6F952584239} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-map-hopscotch", "gtest-map-hopscotch.vcxproj", "{C6099C17-DF98-45AF-92F6-6B40169D39FA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-map-hopscotch", "gtest-striped-map-hopscotch.vcxproj", "{78FF8F0D-9394-41CF-B895-BDC8538CDB1D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1875,26 +1875,26 @@ Global {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.Build.0 = vc14-Release|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|Win32.ActiveCfg = Debug|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|Win32.Build.0 = Debug|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|x64.ActiveCfg = Debug|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Debug|x64.Build.0 = Debug|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.DebugVLD|x64.Build.0 = DebugVLD|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|Win32.ActiveCfg = Release|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|Win32.Build.0 = Release|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|x64.ActiveCfg = Release|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.Release|x64.Build.0 = Release|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 - {C6099C17-DF98-45AF-92F6-6B40169D39FA}.vc14-Release|x64.Build.0 = vc14-Release|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|Win32.ActiveCfg = Debug|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|Win32.Build.0 = Debug|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|x64.ActiveCfg = Debug|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|x64.Build.0 = Debug|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|x64.Build.0 = DebugVLD|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|Win32.ActiveCfg = Release|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|Win32.Build.0 = Release|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|x64.ActiveCfg = Release|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|x64.Build.0 = Release|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|x64.Build.0 = vc14-Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1982,7 +1982,7 @@ Global {EACBF8EC-C58F-4CC8-9435-21BE9812287A} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {47B9F1F9-C9F1-4F25-842B-EE8E6447FE50} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {CDC54B7B-B882-4CC8-87A2-E237D0D945B2} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} - {C6099C17-DF98-45AF-92F6-6B40169D39FA} = {10DC2D86-83B3-428A-8190-463EF4ED03E3} + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D} = {DE3B8DBB-5B4B-40DC-80DB-F0C15BC21E0F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3673AF8B-E81E-49E1-8241-B5FC94791FB8} diff --git a/projects/Win/vc141/gtest-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-map-hopscotch.vcxproj index e0eb017d2..0886c986c 100644 --- a/projects/Win/vc141/gtest-map-hopscotch.vcxproj +++ b/projects/Win/vc141/gtest-map-hopscotch.vcxproj @@ -77,6 +77,7 @@ + diff --git a/test/unit/map/hopscotch_hashmap.cpp b/test/unit/map/hopscotch_hashmap.cpp deleted file mode 100644 index a0775f4e3..000000000 --- a/test/unit/map/hopscotch_hashmap.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - This file is a part of libcds - Concurrent Data Structures library - - (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/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "test_feldman_hashmap_hp.h" - -#include - -namespace { - namespace cc = cds::container; - typedef cds::gc::HP gc_type; - - class HopscotchHashMap : public cds_test::hopscotch_hashmap - { - protected: - typedef cds_test::feldman_hashmap_hp base_class; - - void SetUp() - { - typedef cc::hopscotch_hashmap< key_type, value_type > map_type; - - // +1 - for guarded_ptr - cds::gc::hp::GarbageCollector::Construct( map_type::c_nHazardPtrCount + 1, 1, 16 ); - cds::threading::Manager::attachThread(); - } - - void TearDown() - { - cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct( true ); - } - }; - - TEST_F( FeldmanHashMap_HP, defaulted ) - { - typedef cc::FeldmanHashMap< gc_type, key_type, value_type > map_type; - - map_type m; - test( m ); - } - - TEST_F( FeldmanHashMap_HP, compare ) - { - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, - typename cc::feldman_hashmap::make_traits< - cds::opt::compare< cmp > - >::type - > map_type; - - map_type m( 4, 5 ); - EXPECT_GE( m.head_size(), static_cast( 1 << 4 )); - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 5 )); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, less ) - { - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, - typename cc::feldman_hashmap::make_traits< - cds::opt::less< less > - >::type - > map_type; - - map_type m( 3, 2 ); - EXPECT_GE( m.head_size(), static_cast( 1 << 4 )); // min = 2 ** 4 - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, cmpmix ) - { - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, - typename cc::feldman_hashmap::make_traits< - cds::opt::less< less > - ,cds::opt::compare< cmp > - >::type - > map_type; - - map_type m( 4, 4 ); - EXPECT_EQ( m.head_size(), static_cast( 1 << 4 )); - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 4 )); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, backoff ) - { - struct map_traits: public cc::feldman_hashmap::traits - { - typedef cmp compare; - typedef cds::atomicity::item_counter item_counter; - typedef cds::backoff::yield back_off; - }; - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, map_traits > map_type; - - map_type m( 8, 2 ); - EXPECT_EQ( m.head_size(), static_cast( 1 << 8 )); - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, stat ) - { - struct map_traits: public cc::feldman_hashmap::traits - { - typedef cds::backoff::yield back_off; - typedef cc::feldman_hashmap::stat<> stat; - }; - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, map_traits > map_type; - - map_type m( 1, 1 ); - EXPECT_EQ( m.head_size(), static_cast( 1 << 4 )); // min = 2**4 - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 2 )); // min = 2**2 - test( m ); - } - - TEST_F( FeldmanHashMap_HP, explicit_key_size ) - { - struct map_traits: public cc::feldman_hashmap::traits - { - enum: size_t { - hash_size = sizeof(int) + sizeof( uint16_t) - }; - typedef hash2 hash; - typedef less2 less; - typedef cc::feldman_hashmap::stat<> stat; - }; - typedef cc::FeldmanHashMap< gc_type, key_type2, value_type, map_traits > map_type; - - map_type m( 5, 3 ); - EXPECT_EQ( m.head_size(), static_cast(1 << 6)); - EXPECT_EQ( m.array_node_size(), static_cast(1 << 3)); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, byte_cut ) - { - typedef cc::FeldmanHashMap< gc_type, key_type, value_type, - typename cc::feldman_hashmap::make_traits< - cds::opt::compare< cmp > - , cc::feldman_hashmap::hash_splitter< cds::algo::byte_splitter< key_type >> - >::type - > map_type; - - map_type m( 8, 8 ); - EXPECT_EQ( m.head_size(), static_cast( 1 << 8 )); - EXPECT_EQ( m.array_node_size(), static_cast( 1 << 8 )); - test( m ); - } - - TEST_F( FeldmanHashMap_HP, byte_cut_explicit_key_size ) - { - struct map_traits: public cc::feldman_hashmap::traits - { - enum: size_t { - hash_size = sizeof(int) + sizeof( uint16_t) - }; - typedef cds::algo::byte_splitter< key_type2, hash_size > hash_splitter; - typedef hash2 hash; - typedef less2 less; - typedef cc::feldman_hashmap::stat<> stat; - }; - typedef cc::FeldmanHashMap< gc_type, key_type2, value_type, map_traits > map_type; - - map_type m( 8, 8 ); - EXPECT_EQ( m.head_size(), static_cast(1 << 8)); - EXPECT_EQ( m.array_node_size(), static_cast(1 << 8)); - test( m ); - } - -} // namespace diff --git a/test/unit/map/test_hopscotch_hashmap.h b/test/unit/map/test_hopscotch_hashmap.h deleted file mode 100644 index b16b6a1e9..000000000 --- a/test/unit/map/test_hopscotch_hashmap.h +++ /dev/null @@ -1,482 +0,0 @@ -/* - This file is a part of libcds - Concurrent Data Structures library - - (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/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ -#define CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ - -#include "test_map_data.h" - -// forward declaration -namespace cds { namespace container {}} -namespace co = cds::opt; -namespace cc = cds::container; - -namespace cds_test { - - class hopscotch_hashmap : public map_fixture - { - public: - static size_t const kSize = 1000; - - struct key_type2 { - int nKey; - uint16_t subkey; - - explicit key_type2( int n ) - : nKey( n ) - , subkey( static_cast( n )) - {} - - explicit key_type2( size_t n ) - : nKey( static_cast( n )) - , subkey( static_cast( n )) - {} - - explicit key_type2( std::string const& str ) - : nKey( std::stoi( str )) - , subkey( static_cast( nKey )) - {} - - key_type2( key_type2 const& s ) - : nKey( s.nKey ) - , subkey( s.subkey ) - {} - }; - - struct less2 - { - bool operator ()( key_type2 const& v1, key_type2 const& v2 ) const - { - return v1.nKey < v2.nKey; - } - - bool operator ()( key_type2 const& v1, int v2 ) const - { - return v1.nKey < v2; - } - - bool operator ()( int v1, key_type2 const& v2 ) const - { - return v1 < v2.nKey; - } - - bool operator ()( key_type2 const& v1, std::string const& v2 ) const - { - return v1.nKey < std::stoi( v2 ); - } - - bool operator ()( std::string const& v1, key_type2 const& v2 ) const - { - return std::stoi( v1 ) < v2.nKey; - } - }; - - struct cmp2 { - int operator ()( key_type2 const& v1, key_type2 const& v2 ) const - { - if ( v1.nKey < v2.nKey ) - return -1; - return v1.nKey > v2.nKey ? 1 : 0; - } - - int operator ()( key_type2 const& v1, int v2 ) const - { - if ( v1.nKey < v2 ) - return -1; - return v1.nKey > v2 ? 1 : 0; - } - - int operator ()( int v1, key_type2 const& v2 ) const - { - if ( v1 < v2.nKey ) - return -1; - return v1 > v2.nKey ? 1 : 0; - } - - int operator ()( key_type2 const& v1, std::string const& v2 ) const - { - int n2 = std::stoi( v2 ); - if ( v1.nKey < n2 ) - return -1; - return v1.nKey > n2 ? 1 : 0; - } - - int operator ()( std::string const& v1, key_type2 const& v2 ) const - { - int n1 = std::stoi( v1 ); - if ( n1 < v2.nKey ) - return -1; - return n1 > v2.nKey ? 1 : 0; - } - }; - - struct hash2 { - key_type2 operator()( int i ) const - { - return key_type2( cds::opt::v::hash()(i)); - } - - key_type2 operator()( std::string const& str ) const - { - return key_type2( cds::opt::v::hash()(std::stoi( str ))); - } - - template - key_type2 operator()( T const& i ) const - { - return key_type2( cds::opt::v::hash()(i.nKey)); - } - }; - - - protected: - template - void test( Map& m ) - { - // Precondition: map is empty - // Postcondition: map is empty - - ASSERT_TRUE( m.empty()); - ASSERT_CONTAINER_SIZE( m, 0 ); - - typedef typename Map::key_type key_type; - typedef typename Map::mapped_type value_type; - typedef typename Map::value_type map_pair; - size_t const kkSize = kSize; - - std::vector arrKeys; - for ( int i = 0; i < static_cast(kkSize); ++i ) - arrKeys.push_back( key_type( i )); - shuffle( arrKeys.begin(), arrKeys.end()); - - std::vector< value_type > arrVals; - for ( size_t i = 0; i < kkSize; ++i ) { - value_type val; - val.nVal = static_cast(i); - val.strVal = std::to_string( i ); - arrVals.push_back( val ); - } - - // insert/find - for ( auto const& i : arrKeys ) { - value_type const& val( arrVals.at( i.nKey )); - - ASSERT_FALSE( m.contains( i.nKey )); - ASSERT_FALSE( m.contains( i )); - ASSERT_FALSE( m.find( i, []( map_pair const& ) { - ASSERT_TRUE( false ); - } )); - ASSERT_FALSE( m.find( i.nKey, []( map_pair const& ) { - EXPECT_TRUE( false ); - } )); - - std::pair< bool, bool > updResult; - - switch ( i.nKey % 16 ) { - case 0: - ASSERT_TRUE( m.insert( i )); - ASSERT_FALSE( m.insert( i )); - ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - break; - case 1: - ASSERT_TRUE( m.insert( i.nKey )); - ASSERT_FALSE( m.insert( i.nKey )); - ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - break; - case 2: - ASSERT_TRUE( m.insert( std::to_string( i.nKey ))); - ASSERT_FALSE( m.insert( std::to_string( i.nKey ))); - ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - break; - case 3: - ASSERT_TRUE( m.insert( i, val )); - ASSERT_FALSE( m.insert( i, val )); - break; - case 4: - ASSERT_TRUE( m.insert( i.nKey, val.strVal )); - ASSERT_FALSE( m.insert( i.nKey, val.strVal )); - break; - case 5: - ASSERT_TRUE( m.insert( val.strVal, i.nKey )); - ASSERT_FALSE( m.insert( val.strVal, i.nKey )); - break; - case 6: - ASSERT_TRUE( m.insert_with( i, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - ASSERT_FALSE( m.insert_with( i, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - case 7: - ASSERT_TRUE( m.insert_with( i.nKey, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - ASSERT_FALSE( m.insert_with( i.nKey, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - case 8: - ASSERT_TRUE( m.insert_with( val.strVal, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - ASSERT_FALSE( m.insert_with( val.strVal, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - case 9: - updResult = m.update( i.nKey, []( map_pair&, map_pair* ) { - EXPECT_TRUE( false ); - }, false ); - ASSERT_FALSE( updResult.first ); - ASSERT_FALSE( updResult.second ); - - updResult = m.update( i.nKey, []( map_pair& v, map_pair* old ) { - EXPECT_TRUE( old == nullptr ); - v.second.nVal = v.first.nKey; - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_TRUE( updResult.second ); - - updResult = m.update( i.nKey, []( map_pair& v, map_pair* old ) { - ASSERT_FALSE( old == nullptr ); - EXPECT_EQ( v.first.nKey, old->second.nVal ); - v.second.nVal = old->second.nVal; - v.second.strVal = std::to_string( v.second.nVal ); - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_FALSE( updResult.second ); - break; - case 10: - updResult = m.update( i, []( map_pair&, map_pair* ) { - EXPECT_TRUE( false ); - }, false ); - ASSERT_FALSE( updResult.first ); - ASSERT_FALSE( updResult.second ); - - updResult = m.update( i, []( map_pair& v, map_pair* old ) { - EXPECT_TRUE( old == nullptr ); - v.second.nVal = v.first.nKey; - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_TRUE( updResult.second ); - - updResult = m.update( i, []( map_pair& v, map_pair* old ) { - ASSERT_FALSE( old == nullptr ); - EXPECT_EQ( v.first.nKey, old->second.nVal ); - v.second.nVal = old->second.nVal; - v.second.strVal = std::to_string( v.second.nVal ); - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_FALSE( updResult.second ); - break; - case 11: - updResult = m.update( val.strVal, []( map_pair&, map_pair* ) { - EXPECT_TRUE( false ); - }, false ); - ASSERT_FALSE( updResult.first ); - ASSERT_FALSE( updResult.second ); - - updResult = m.update( val.strVal, []( map_pair& v, map_pair* old ) { - EXPECT_TRUE( old == nullptr ); - v.second.nVal = v.first.nKey; - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_TRUE( updResult.second ); - - updResult = m.update( val.strVal, []( map_pair& v, map_pair* old ) { - ASSERT_FALSE( old == nullptr ); - EXPECT_EQ( v.first.nKey, old->second.nVal ); - v.second.nVal = old->second.nVal; - v.second.strVal = std::to_string( v.second.nVal ); - } ); - ASSERT_TRUE( updResult.first ); - ASSERT_FALSE( updResult.second ); - break; - case 12: - ASSERT_TRUE( m.emplace( i.nKey )); - ASSERT_FALSE( m.emplace( i.nKey )); - ASSERT_TRUE( m.find( i.nKey, []( map_pair& v ) { - v.second.nVal = v.first.nKey; - v.second.strVal = std::to_string( v.first.nKey ); - } )); - break; - case 13: - ASSERT_TRUE( m.emplace( i, i.nKey )); - ASSERT_FALSE( m.emplace( i, i.nKey )); - break; - case 14: - { - std::string str = val.strVal; - ASSERT_TRUE( m.emplace( i, std::move( str ))); - ASSERT_TRUE( str.empty()); - str = val.strVal; - ASSERT_FALSE( m.emplace( i, std::move( str ))); - ASSERT_TRUE( str.empty()); - } - break; - case 15: - { - std::string str = val.strVal; - ASSERT_TRUE( m.emplace( i, i.nKey, std::move( str ))); - ASSERT_TRUE( str.empty()); - str = val.strVal; - ASSERT_FALSE( m.emplace( i, i.nKey, std::move( str ))); - ASSERT_TRUE( str.empty()); - } - break; - } - - ASSERT_TRUE( m.contains( i.nKey )); - ASSERT_TRUE( m.contains( i )); - ASSERT_TRUE( m.find( i, []( map_pair const& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - ASSERT_TRUE( m.find( i.nKey, []( map_pair const& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - } - ASSERT_FALSE( m.empty()); - ASSERT_CONTAINER_SIZE( m, kkSize ); - ASSERT_FALSE( m.begin() == m.end()); - ASSERT_FALSE( m.cbegin() == m.cend()); - - shuffle( arrKeys.begin(), arrKeys.end()); - - { - std::vector< typename Map::level_statistics > vect; - m.get_level_statistics( vect ); - } - - // erase/find - for ( auto const& i : arrKeys ) { - value_type const& val( arrVals.at( i.nKey )); - - ASSERT_TRUE( m.contains( i.nKey )); - ASSERT_TRUE( m.contains( val.strVal )); - ASSERT_TRUE( m.contains( i )); - ASSERT_TRUE( m.find( i, []( map_pair const& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - ASSERT_TRUE( m.find( i.nKey, []( map_pair const& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - - switch ( i.nKey % 6 ) { - case 0: - ASSERT_TRUE( m.erase( i )); - ASSERT_FALSE( m.erase( i )); - break; - case 1: - ASSERT_TRUE( m.erase( i.nKey )); - ASSERT_FALSE( m.erase( i.nKey )); - break; - case 2: - ASSERT_TRUE( m.erase( val.strVal )); - ASSERT_FALSE( m.erase( val.strVal )); - break; - case 3: - ASSERT_TRUE( m.erase( i, []( map_pair& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - ASSERT_FALSE( m.erase( i, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - case 4: - ASSERT_TRUE( m.erase( i.nKey, []( map_pair& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - ASSERT_FALSE( m.erase( i.nKey, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - case 5: - ASSERT_TRUE( m.erase( val.strVal, []( map_pair& v ) { - EXPECT_EQ( v.first.nKey, v.second.nVal ); - EXPECT_EQ( std::to_string( v.first.nKey ), v.second.strVal ); - } )); - ASSERT_FALSE( m.erase( val.strVal, []( map_pair& ) { - EXPECT_TRUE( false ); - } )); - break; - } - - ASSERT_FALSE( m.contains( i.nKey )); - ASSERT_FALSE( m.contains( i )); - ASSERT_FALSE( m.contains( val.strVal )); - ASSERT_FALSE( m.find( i, []( map_pair const& ) { - ASSERT_TRUE( false ); - } )); - ASSERT_FALSE( m.find( i.nKey, []( map_pair const& ) { - EXPECT_TRUE( false ); - } )); - } - ASSERT_TRUE( m.empty()); - ASSERT_CONTAINER_SIZE( m, 0 ); - - ASSERT_TRUE( m.begin() == m.end()); - ASSERT_TRUE( m.cbegin() == m.cend()); - - // clear - for ( auto const& i : arrKeys ) - ASSERT_TRUE( m.insert( i )); - - ASSERT_FALSE( m.empty()); - ASSERT_CONTAINER_SIZE( m, kkSize ); - - m.clear(); - - ASSERT_TRUE( m.empty()); - ASSERT_CONTAINER_SIZE( m, 0 ); - } - }; - -} // namespace cds_test - -#endif // CDSUNIT_MAP_TEST_HOPSCOTCHHASHMAP_H_ diff --git a/test/unit/striped-map/hopscotch_hashmap.cpp b/test/unit/striped-map/hopscotch_hashmap.cpp new file mode 100644 index 000000000..a3fad109a --- /dev/null +++ b/test/unit/striped-map/hopscotch_hashmap.cpp @@ -0,0 +1,71 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "test_map.h" + +#include + +namespace { + namespace cc = cds::container; + + class HopscotchMap : public cds_test::container_map + { + protected: + typedef cds_test::container_map base_class; + + template + void test(Map& m ) + { + // Precondition: set is empty + // Postcondition: set is empty + + base_class::test_< Map::c_isSorted >( m ); + } + + //void SetUp() + //{} + + //void TearDown() + //{} + }; + + +//************************************************************ +// striped set + + TEST_F(HopscotchMap, striped_list_unordered ) + { + typedef cc::hopscotch_hashmap< key_type, value_type > map_type; + + map_type m; + test( m ); + } + +} // namespace From fa163321de405c2e38294881c1e0cf9e693bf887 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Mon, 8 Jan 2018 15:29:16 +0300 Subject: [PATCH 08/19] Project test --- .../vc141/gtest-striped-map-hopscotch.vcxproj | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj diff --git a/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj new file mode 100644 index 000000000..9eed7b464 --- /dev/null +++ b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj @@ -0,0 +1,396 @@ +п»ї + + + + DebugVLD + Win32 + + + DebugVLD + x64 + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + vc14-Debug + Win32 + + + vc14-Debug + x64 + + + vc14-Release + Win32 + + + vc14-Release + x64 + + + + + + + + + + + + + {78FF8F0D-9394-41CF-B895-BDC8538CDB1D} + Win32Proj + striped-map + 10.0.14393.0 + + + + Application + true + v141 + Unicode + + + Application + true + v140 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + true + v140 + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + true + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + $(ProjectName)_d + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)..\..\..\bin\vc.$(PlatformToolset)\$(Platform)-release\ + $(SolutionDir)..\..\..\obj\vc.$(PlatformToolset)\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + DebugFastLink + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtestd.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + true + true + true + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + true + true + true + $(GTEST_LIB32);$(GTEST_ROOT)/lib/x86;$(BOOST_PATH)/stage32/lib;$(BOOST_PATH)/stage/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + true + true + true + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) /Zc:inline /permissive- + + + Console + true + true + true + $(GTEST_LIB64);$(GTEST_ROOT)/lib/x64;$(BOOST_PATH)/stage64/lib;$(BOOST_PATH)/bin;%(AdditionalLibraryDirectories);$(OutDir) + gtest.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file From a53dc219ea82558fa24c105115ee197305ade6b5 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Mon, 8 Jan 2018 18:06:08 +0300 Subject: [PATCH 09/19] contains, erase, insert, insert_with, update, emplace, find --- cds/container/hopscotch_hashmap.h | 330 +++++++++++++++++++++++++++++- 1 file changed, 321 insertions(+), 9 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index 591733e35..e4084a2db 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -110,8 +110,170 @@ namespace cds { return counter; } - bool contains(KEY key) { - return get(key) != NULL; + /// Checks whether the map contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains(K const& key) + { + return get(key, [](map_pair const&) { + (false); + }) != NULL; + } + + /// Checks whether the map contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the map. + */ + template + bool contains(K const& key, Predicate pred) + { + return get(key, pred) != NULL; + } + + /// Find the key \p key + /** \anchor cds_nonintrusive_CuckooMap_find_func + + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + where \p item is the item found. + + The functor may change \p item.second. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find(K const& key, Func f) + { + return find(key, [&f](node_type& item, K const&) { f(item.m_val); }); + } + + /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace(K&& key, Args&&... args) + { + mapped_type val = std::forward(args); + if (insert(key, val)) { + return true; + } + return false; + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update(K const& key, Func func, bool bAllowInsert = true) + { + mapped_type def_val; + return update(key, def_val, func, bAllowInsert); + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update(K const& key, V const& val, Func func, bool bAllowInsert = true) + { + bool inserted = false; + + if (!contains(key)) { + if (bAllowInsert) { + if (insert(key, val)) { + inserted = true; + } + else { + return make_pair(false, false); + } + } + else { + return make_pair(false, false); + } + } + + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + Bucket* check_bucket = start_bucket; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (key == *(check_bucket->_key)) { + *(check_bucket->_data) = val; + return make_pair(true, inserted); + } + } + ++check_bucket; + } + ++try_counter; + } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + + if (timestamp != start_bucket->_timestamp) { + Bucket* check_bucket = start_bucket; + for (int i = 0; i < HOP_RANGE; i++) { + if (key == *(check_bucket->_key)) { + *(check_bucket->_data) = val; + return make_pair(true, inserted); + } + ++check_bucket; + } + } + return make_pair(false, inserted); } DATA get(KEY key) { @@ -149,9 +311,54 @@ namespace cds { } return NULL; } + + /// Inserts new node and initialize it by a functor + /** + This function inserts new node with key \p key and if inserting is successful then it calls + \p func functor with signature + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + + The argument \p item of user-defined functor \p func is the reference + to the map's item inserted: + - item.first is a const reference to item's key that cannot be changed. + - item.second is a reference to item's value that may be changed. + + The key_type should be constructible from value of type \p K. + + The function allows to split creating of new item into two part: + - create item from \p key; + - insert new item into the map; + - if inserting is successful, initialize the value of item by calling \p func functor + + This can be useful if complete initialization of object of \p value_type is heavyweight and + it is preferable that the initialization should be completed only if inserting is successful. + */ + template + bool insert_with(const K& key, Func func) + { + mapped_type def_val; + return insert_with(key, def_val, func); + } - bool add(KEY key, DATA data) { - int val = 1; + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from \p key of type \p K. + - The \ref value_type should be constructible from \p val of type \p V. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + template + bool insert_with(K const& key, V const& val, Func func) + { + int tmp_val = 1; unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; start_bucket->lock(); @@ -163,8 +370,8 @@ namespace cds { Bucket* free_bucket = start_bucket; int free_distance = 0; for (; free_distance < ADD_RANGE; ++free_distance) { - std::atomic _atomic = free_bucket->_key; - KEY* _null_key = Bucket::_empty_key; + std::atomic _atomic = free_bucket->_key; + K* _null_key = Bucket::_empty_key; if (_null_key == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { break; } @@ -175,13 +382,14 @@ namespace cds { do { if (free_distance < HOP_RANGE) { start_bucket->_hop_info |= (1 << free_distance); - *(free_bucket->_data) = data; + *(free_bucket->_data) = val; *(free_bucket->_key) = key; start_bucket->unlock(); + func(*(free_bucket->_data)); return true; } - find_closer_bucket(&free_bucket, &free_distance, val); - } while (0 != val); + find_closer_bucket(&free_bucket, &free_distance, tmp_val); + } while (0 != tmp_val); } start_bucket->unlock(); @@ -190,6 +398,110 @@ namespace cds { return false; } + /// Inserts new node with key and default value + /** + The function creates a node with \p key and default value, and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from a value of type \p K. + In trivial case, \p K is equal to \ref key_type. + - The \ref mapped_type should be default-constructible. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert(K const& key) + { + mapped_type def_data; + return insert(key, def_data, [](mapped_type&) {}); + } + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_CuckooMap_erase_val + + Return \p true if \p key is found and deleted, \p false otherwise + */ + template + bool erase(K const& key) + { + node_type * pNode = base_class::erase(key); + if (pNode) { + free_node(pNode); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_val "erase(Q const&)" + but \p pred is used for key comparing. + If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with(K const& key, Predicate pred) + { + CDS_UNUSED(pred); + node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); + if (pNode) { + free_node(pNode); + return true; + } + return false; + } + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_CuckooMap_erase_func + + The function searches an item with key \p key, calls \p f functor + and deletes the item. If \p key is not found, the functor is not called. + + The functor \p Func interface: + \code + struct extractor { + void operator()(value_type& item) { ... } + }; + \endcode + + Return \p true if key is found and deleted, \p false otherwise + + See also: \ref erase + */ + template + bool erase(K const& key, Func f) + { + node_type * pNode = base_class::erase(key); + if (pNode) { + f(pNode->m_val); + free_node(pNode); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with(K const& key, Predicate pred, Func f) + { + CDS_UNUSED(pred); + node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); + if (pNode) { + f(pNode->m_val); + free_node(pNode); + return true; + } + return false; + } + DATA remove(KEY key) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; From b702daaeacdc6f7f28b740687c61bf91fcf1fd9d Mon Sep 17 00:00:00 2001 From: Dron642 Date: Mon, 8 Jan 2018 22:04:23 +0300 Subject: [PATCH 10/19] erase, erase_with --- cds/container/hopscotch_hashmap.h | 64 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index e4084a2db..262f0eb3e 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -424,12 +424,7 @@ namespace cds { template bool erase(K const& key) { - node_type * pNode = base_class::erase(key); - if (pNode) { - free_node(pNode); - return true; - } - return false; + return erase(key, [](mapped_type&) {}); } /// Deletes the item from the list using \p pred predicate for searching @@ -443,13 +438,7 @@ namespace cds { template bool erase_with(K const& key, Predicate pred) { - CDS_UNUSED(pred); - node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); - if (pNode) { - free_node(pNode); - return true; - } - return false; + return erase(key, pred, [](mapped_type&) {}); } /// Delete \p key from the map @@ -472,12 +461,26 @@ namespace cds { template bool erase(K const& key, Func f) { - node_type * pNode = base_class::erase(key); - if (pNode) { - f(pNode->m_val); - free_node(pNode); - return true; + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + + unsigned int hop_info = start_bucket->_hop_info; + unsigned int mask = 1; + for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { + if (mask & hop_info) { + Bucket* check_bucket = start_bucket + i; + if (key == *(check_bucket->_key)) { + f(*(check_bucket->_data)); + check_bucket->_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); + start_bucket->unlock(); + return true; + } + } } + start_bucket->unlock(); return false; } @@ -492,13 +495,26 @@ namespace cds { template bool erase_with(K const& key, Predicate pred, Func f) { - CDS_UNUSED(pred); - node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); - if (pNode) { - f(pNode->m_val); - free_node(pNode); - return true; + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + + unsigned int hop_info = start_bucket->_hop_info; + unsigned int mask = 1; + for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { + if (mask & hop_info) { + Bucket* check_bucket = start_bucket + i; + if (pred(key, *(check_bucket->_key)) == 0) { + f(*(check_bucket->_data)); + check_bucket->_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); + start_bucket->unlock(); + return true; + } + } } + start_bucket->unlock(); return false; } From 8a9077809607f01590e9500d556bab844aba1a7a Mon Sep 17 00:00:00 2001 From: LeoSko Date: Mon, 8 Jan 2018 22:12:01 +0300 Subject: [PATCH 11/19] get with cmp, simple insert --- cds/container/hopscotch_hashmap.h | 36 +++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index e4084a2db..79744ea90 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -164,11 +164,7 @@ namespace cds { template bool emplace(K&& key, Args&&... args) { - mapped_type val = std::forward(args); - if (insert(key, val)) { - return true; - } - return false; + return insert(std::forward(key), mapped_type(std::forward(args)...)) } /// Updates the node @@ -276,7 +272,13 @@ namespace cds { return make_pair(false, inserted); } - DATA get(KEY key) { + template + V get(K const& key) { + return get(key, [=](K const& one, K const& two) { return one != two; }); + } + + template + V get(K const& key, Pred cmp) { unsigned int hash = calc_hash(key); Bucket* start_bucket = segments_arys + hash; unsigned int try_counter = 0; @@ -291,7 +293,7 @@ namespace cds { temp = temp >> i; if (temp & 1) { - if (key == *(check_bucket->_key)) { + if (cmp(key, *(check_bucket->_key)) == 0) { return *(check_bucket->_data); } } @@ -303,7 +305,7 @@ namespace cds { if (timestamp != start_bucket->_timestamp) { Bucket* check_bucket = start_bucket; for (int i = 0; i < HOP_RANGE; i++) { - if (key == *(check_bucket->_key)) { + if (cmp(key, *(check_bucket->_key)) == 0) { return *(check_bucket->_data); } ++check_bucket; @@ -413,7 +415,23 @@ namespace cds { bool insert(K const& key) { mapped_type def_data; - return insert(key, def_data, [](mapped_type&) {}); + return insert_with(key, def_data, [](mapped_type&) {}); + } + + /// Inserts new node with key and default value + /** + The function creates a node with \p key and \p value, and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from a value of type \p K. + In trivial case, \p K is equal to \ref key_type. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert(K const& key, V const& val) + { + return insert_with(key, val, [](mapped_type&) {}); } /// Delete \p key from the map From c693dee0faf84bf23bc014f3a9eef66cdd4465c5 Mon Sep 17 00:00:00 2001 From: Roma Date: Mon, 8 Jan 2018 22:41:38 +0300 Subject: [PATCH 12/19] find --- cds/container/hopscotch_hashmap.h | 26 ++++++++++++++++++- projects/Win/vc141/cds.vcxproj | 4 +-- .../vc141/gtest-striped-map-hopscotch.vcxproj | 2 +- test/unit/main.cpp | 1 - 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index 38a5507b1..f2c34f29c 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -154,7 +154,31 @@ namespace cds { template bool find(K const& key, Func f) { - return find(key, [&f](node_type& item, K const&) { f(item.m_val); }); + DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); + f(data); + return *data != NULL; + } + + template + bool find(K const& key, Predicate pred) + { + DATA data = get(key, pred); + return *data != NULL; + } + + template + bool find(K const& key) + { + DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); + return *data != NULL; + } + + template + bool find_with(K const& key, Predicate pred, Func f) + { + DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); + f(data); + return *data != NULL; } /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index cd92882d9..0b2f2bea3 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -238,7 +238,7 @@ /wd4512 /wd4127 /Zc:inline /permissive- %(AdditionalOptions) Disabled - $(SolutionDir)..\..\..\;$(BOOST_PATH);%(AdditionalIncludeDirectories) + $(GTEST_ROOT);$(SolutionDir)..\..\..\;$(BOOST_PATH);%(AdditionalIncludeDirectories) _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0500;CDS_BUILD_LIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -251,7 +251,7 @@ $(OutDir)$(TargetName).dll - %(AdditionalLibraryDirectories) + $(BOOST_PATH)/stage/lib;%(AdditionalLibraryDirectories) true $(TargetDir)$(TargetName).pdb Console diff --git a/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj index 9eed7b464..a64c6d4e8 100644 --- a/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj +++ b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj @@ -220,7 +220,7 @@ Level3 Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\..;$(GTEST_ROOT)\include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) /Zc:inline /permissive- diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 84e9ae7b8..cd33dd4ba 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -38,7 +38,6 @@ namespace cds_test { /*static*/ std::mt19937 fixture::random_gen_( random_dev_() ); } // namespace cds_test - using namespace std; int main( int argc, char **argv ) From ae1c78b021b764a566ac17bfba033cefed5b6106 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Mon, 8 Jan 2018 23:04:04 +0300 Subject: [PATCH 13/19] update update :) --- cds/container/hopscotch_hashmap.h | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index 38a5507b1..5aa6c810f 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -195,6 +195,38 @@ namespace cds { return update(key, def_val, func, bAllowInsert); } + template + std::pair update(K const& key, V const& val) + { + return update(key, val, [](V const&) {}); + } + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update(K const& key, V const& val, Func func, bool bAllowInsert = true) + { + return update(key, val, func, bAllowInsert); + } + /// Updates the node /** The operation performs inserting or changing data with lock-free manner. @@ -251,6 +283,7 @@ namespace cds { if (temp & 1) { if (key == *(check_bucket->_key)) { *(check_bucket->_data) = val; + func(*(check_bucket->_data)); return make_pair(true, inserted); } } @@ -264,6 +297,7 @@ namespace cds { for (int i = 0; i < HOP_RANGE; i++) { if (key == *(check_bucket->_key)) { *(check_bucket->_data) = val; + func(*(check_bucket->_data)); return make_pair(true, inserted); } ++check_bucket; From 7e9b1063b5bc64837322f5460cd57be387f3c8d4 Mon Sep 17 00:00:00 2001 From: Roma Date: Mon, 8 Jan 2018 23:10:29 +0300 Subject: [PATCH 14/19] fix --- cds/container/hopscotch_hashmap.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index f2c34f29c..15a39616d 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -118,9 +118,7 @@ namespace cds { template bool contains(K const& key) { - return get(key, [](map_pair const&) { - (false); - }) != NULL; + return get(key, [=](K const& one, K const& two) { return one != two; }) != NULL; } /// Checks whether the map contains \p key using \p pred predicate for searching From 4ab51d932c1fb7a21c5565db5416feba46c1e022 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Tue, 9 Jan 2018 00:16:32 +0300 Subject: [PATCH 15/19] Fixing tests --- cds/container/hopscotch_hashmap.h | 79 +++++++-------------- test/unit/striped-map/hopscotch_hashmap.cpp | 4 +- 2 files changed, 26 insertions(+), 57 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index e8d692dc2..d32610ec2 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -24,6 +24,9 @@ namespace cds { typedef DATA mapped_type; ///< type of value stored in the map typedef std::pair value_type; ///< Pair type + typedef cds::atomicity::item_counter item_counter; + item_counter m_item_counter; + private: static const int HOP_RANGE = 32; static const int ADD_RANGE = 256; @@ -87,6 +90,7 @@ namespace cds { hopscotch_hashmap() { segments_arys = new Bucket[MAX_SEGMENTS + ADD_RANGE]; + m_item_counter.reset(); BUSY = (KEY*)std::malloc(sizeof(KEY)); } @@ -100,6 +104,8 @@ namespace cds { } int size() { + return m_item_counter; + /* unsigned int counter = 0; const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); for (unsigned int iElm = 0; iElm < num_elm; ++iElm) { @@ -108,6 +114,7 @@ namespace cds { } } return counter; + */ } /// Checks whether the map contains \p key @@ -118,9 +125,7 @@ namespace cds { template bool contains(K const& key) { - return get(key, [](map_pair const&) { - (false); - }) != NULL; + return get(key, [=](K const& one, K const& two) { return one != two; }) != NULL; } /// Checks whether the map contains \p key using \p pred predicate for searching @@ -152,7 +157,7 @@ namespace cds { The function returns \p true if \p key is found, \p false otherwise. */ template - bool find(K const& key, Func f) + bool find_with(K const& key, Func f) { DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); f(data); @@ -188,9 +193,20 @@ namespace cds { template bool emplace(K&& key, Args&&... args) { - return insert(std::forward(key), mapped_type(std::forward(args)...)) + return insert(std::forward(key), mapped_type(std::forward(args)...)); } + /// Clears the map + void clear() + { + const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); + Bucket *start = segments_arys; + for (unsigned int iElm = 0; iElm < num_elm; ++iElm, ++start) { + start->lock(); + } + m_item_counter.reset(); + segments_arys = (Bucket *)memset(segments_arys, 0, (MAX_SEGMENTS + ADD_RANGE) * sizeof(Bucket)); + } /// Updates the node /** The operation performs inserting or changing data with lock-free manner. @@ -224,32 +240,6 @@ namespace cds { { return update(key, val, [](V const&) {}); } - /// Updates the node - /** - The operation performs inserting or changing data with lock-free manner. - - If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. - Otherwise, the functor \p func is called with item found. - The functor \p func signature is: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - an item of the map for \p key - - Returns std::pair where \p first is \p true if operation is successful, - i.e. the node has been inserted or updated, - \p second is \p true if new item has been added or \p false if the item with \p key - already exists. - */ - template - std::pair update(K const& key, V const& val, Func func, bool bAllowInsert = true) - { - return update(key, val, func, bAllowInsert); - } /// Updates the node /** @@ -444,6 +434,7 @@ namespace cds { start_bucket->_hop_info |= (1 << free_distance); *(free_bucket->_data) = val; *(free_bucket->_key) = key; + ++m_item_counter; start_bucket->unlock(); func(*(free_bucket->_data)); return true; @@ -551,6 +542,7 @@ namespace cds { check_bucket->_key = NULL; check_bucket->_data = NULL; start_bucket->_hop_info &= ~(1 << i); + --m_item_counter; start_bucket->unlock(); return true; } @@ -586,6 +578,7 @@ namespace cds { check_bucket->_data = NULL; start_bucket->_hop_info &= ~(1 << i); start_bucket->unlock(); + --m_item_counter; return true; } } @@ -594,30 +587,6 @@ namespace cds { return false; } - DATA remove(KEY key) { - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - - unsigned int hop_info = start_bucket->_hop_info; - unsigned int mask = 1; - for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { - if (mask & hop_info) { - Bucket* check_bucket = start_bucket + i; - if (key == *(check_bucket->_key)) { - DATA rc = *(check_bucket->_data); - check_bucket->_key = NULL; - check_bucket->_data = NULL; - start_bucket->_hop_info &= ~(1 << i); - start_bucket->unlock(); - return rc; - } - } - } - start_bucket->unlock(); - return NULL; - } - void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { diff --git a/test/unit/striped-map/hopscotch_hashmap.cpp b/test/unit/striped-map/hopscotch_hashmap.cpp index a3fad109a..bf0e23e43 100644 --- a/test/unit/striped-map/hopscotch_hashmap.cpp +++ b/test/unit/striped-map/hopscotch_hashmap.cpp @@ -43,8 +43,8 @@ namespace { template void test(Map& m ) { - // Precondition: set is empty - // Postcondition: set is empty + // Precondition: map is empty + // Postcondition: map is empty base_class::test_< Map::c_isSorted >( m ); } From 536b8a8ba438e1ce281f6c1e0b16fe7f36eacc08 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Wed, 10 Jan 2018 08:02:47 +0300 Subject: [PATCH 16/19] less errors maybe? --- cds/container/hopscotch_hashmap.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index d32610ec2..d27afa699 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -125,7 +125,8 @@ namespace cds { template bool contains(K const& key) { - return get(key, [=](K const& one, K const& two) { return one != two; }) != NULL; + DATA data = get(key); + return data != NULL; } /// Checks whether the map contains \p key using \p pred predicate for searching @@ -161,21 +162,21 @@ namespace cds { { DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); f(data); - return *data != NULL; + return data != NULL; } template bool find(K const& key, Predicate pred) { DATA data = get(key, pred); - return *data != NULL; + return data != NULL; } template bool find(K const& key) { DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); - return *data != NULL; + return data != NULL; } template @@ -183,7 +184,7 @@ namespace cds { { DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); f(data); - return *data != NULL; + return data != NULL; } /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... From 5bde49da434dff15934f8a6d9023c4f79fd82b01 Mon Sep 17 00:00:00 2001 From: Dron642 Date: Wed, 10 Jan 2018 10:35:20 +0300 Subject: [PATCH 17/19] some errors correction --- cds/container/hopscotch_hashmap.h | 62 ++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index d27afa699..c86cdf767 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -76,8 +76,9 @@ namespace cds { Bucket* segments_arys; - int calc_hash(KEY key) { - std::hash hash_fn; + template + unsigned int calc_hash(K const& key) { + std::hash hash_fn; return hash_fn(key) % MAX_SEGMENTS; } @@ -124,9 +125,8 @@ namespace cds { */ template bool contains(K const& key) - { - DATA data = get(key); - return data != NULL; + { + return find_with(key, [=](K const& one, K const& two) { return one != two; }, [](mapped_type&) {}); } /// Checks whether the map contains \p key using \p pred predicate for searching @@ -138,7 +138,7 @@ namespace cds { template bool contains(K const& key, Predicate pred) { - return get(key, pred) != NULL; + return find_with(key, pred, [](mapped_type&) {}); } /// Find the key \p key @@ -160,31 +160,59 @@ namespace cds { template bool find_with(K const& key, Func f) { - DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); - f(data); - return data != NULL; + return find_with(key, [=](K const& one, K const& two) { return one != two; }, f); } template bool find(K const& key, Predicate pred) { - DATA data = get(key, pred); - return data != NULL; + return find_with(key, pred, [](mapped_type&) {}); } template bool find(K const& key) { - DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); - return data != NULL; + return find_with(key, [=](K const& one, K const& two) { return one != two; }, [](mapped_type&) {}); } template bool find_with(K const& key, Predicate pred, Func f) { - DATA data = get(key, [=](K const& one, K const& two) { return one != two; }); - f(data); - return data != NULL; + unsigned int hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + unsigned int try_counter = 0; + unsigned int timestamp; + do { + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + Bucket* check_bucket = start_bucket; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (pred(key, *(check_bucket->_key)) == 0) { + f(*(check_bucket->_data)); + return true; + } + } + ++check_bucket; + } + ++try_counter; + } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); + + if (timestamp != start_bucket->_timestamp) { + Bucket* check_bucket = start_bucket; + for (int i = 0; i < HOP_RANGE; i++) { + if (pred(key, *(check_bucket->_key)) == 0) { + f(*(check_bucket->_data)); + return true; + } + ++check_bucket; + } + } + return false; } /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... @@ -506,7 +534,7 @@ namespace cds { template bool erase_with(K const& key, Predicate pred) { - return erase(key, pred, [](mapped_type&) {}); + return erase_with(key, pred, [](mapped_type&) {}); } /// Delete \p key from the map From 330235cde80fce39c5917607a6d1aae15d98ed80 Mon Sep 17 00:00:00 2001 From: LeoSko Date: Sun, 14 Jan 2018 00:44:14 +0300 Subject: [PATCH 18/19] Copied Cuckoo Map/Set with new name to setup hierarchy --- .../details/hopscotch_hashmap_base.h | 269 ++ .../details/hopscotch_hashset_base.h | 269 ++ cds/container/hopscotch_hashmap.h | 1426 +++++---- cds/container/hopscotch_hashset.h | 850 +++++ cds/intrusive/hopscotch_hashset.h | 2829 +++++++++++++++++ projects/Win/vc141/cds.sln | 69 +- projects/Win/vc141/cds.vcxproj | 10 +- .../vc141/gtest-striped-map-hopscotch.vcxproj | 5 +- ...oj => gtest-striped-set-hopscotch.vcxproj} | 99 +- projects/Win/vc141/stress-map-del3.vcxproj | 1 + test/include/cds_test/stat_hopscotch_out.h | 116 + test/stress/map/del3/map_del3_hopscotch.cpp | 38 + test/stress/map/map_type_hopscotch.h | 364 +++ test/unit/main.cpp | 6 +- test/unit/striped-map/hopscotch_hashmap.cpp | 464 ++- test/unit/striped-set/hopscotch_hashset.cpp | 517 +++ .../intrusive_hopscotch_hashset.cpp | 1406 ++++++++ 17 files changed, 7970 insertions(+), 768 deletions(-) create mode 100644 cds/container/details/hopscotch_hashmap_base.h create mode 100644 cds/container/details/hopscotch_hashset_base.h create mode 100644 cds/container/hopscotch_hashset.h create mode 100644 cds/intrusive/hopscotch_hashset.h rename projects/Win/vc141/{gtest-map-hopscotch.vcxproj => gtest-striped-set-hopscotch.vcxproj} (76%) create mode 100644 test/include/cds_test/stat_hopscotch_out.h create mode 100644 test/stress/map/del3/map_del3_hopscotch.cpp create mode 100644 test/stress/map/map_type_hopscotch.h create mode 100644 test/unit/striped-set/hopscotch_hashset.cpp create mode 100644 test/unit/striped-set/intrusive_hopscotch_hashset.cpp diff --git a/cds/container/details/hopscotch_hashmap_base.h b/cds/container/details/hopscotch_hashmap_base.h new file mode 100644 index 000000000..4e0a39a45 --- /dev/null +++ b/cds/container/details/hopscotch_hashmap_base.h @@ -0,0 +1,269 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSLIB_CONTAINER_DETAILS_HOPSCOTCH_HASHMAP_BASE_H +#define CDSLIB_CONTAINER_DETAILS_HOPSCOTCH_HASHMAP_BASE_H + +#include + +namespace cds { namespace container { + + /// Hopscotch hashmap related definitions + /** @ingroup cds_nonintrusive_helper + */ + namespace hopscotch_hashmap { +#ifdef CDS_DOXYGEN_INVOKED + /// Lock striping concurrent access policy. This is typedef for intrusive::hopscotch_hashset::striping template + class striping + {}; +#else + using intrusive::hopscotch_hashset::striping; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Refinable concurrent access policy. This is typedef for intrusive::hopscotch_hashset::refinable template + class refinable + {}; +#else + using intrusive::hopscotch_hashset::refinable; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Striping internal statistics. This is typedef for intrusive::hopscotch_hashset::striping_stat + class striping_stat + {}; +#else + using intrusive::hopscotch_hashset::striping_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Empty striping internal statistics. This is typedef for intrusive::hopscotch_hashset::empty_striping_stat + class empty_striping_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_striping_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Refinable internal statistics. This is typedef for intrusive::hopscotch_hashset::refinable_stat + class refinable_stat + {}; +#else + using intrusive::hopscotch_hashset::refinable_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Empty refinable internal statistics. This is typedef for intrusive::hopscotch_hashset::empty_refinable_stat + class empty_refinable_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_refinable_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Hopscotch hashmap statistics. This is typedef for intrusive::hopscotch_hashset::stat + class stat + {}; +#else + using intrusive::hopscotch_hashset::stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Hopscotch hashmap empty statistics.This is typedef for intrusive::hopscotch_hashset::empty_stat + class empty_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_stat; +#endif + + /// Option specifying whether to store hash values in the node + /** + This option reserves additional space in the hook to store the hash value of the object once it's introduced in the container. + When this option is used, the unordered container will store the calculated hash value in the hook and rehashing operations won't need + to recalculate the hash of the value. This option will improve the performance of unordered containers + when rehashing is frequent or hashing the value is a slow operation + + The \p Enable template parameter toggles the feature: + - the value \p true enables storing the hash values + - the value \p false disables storing the hash values + */ + template + struct store_hash + { + //@cond + template + struct pack: public Base { + static bool const store_hash = Enable; + }; + //@endcond + }; + +#ifdef CDS_DOXYGEN_INVOKED + /// Probe set type option + /** + @copydetails cds::intrusive::hopscotch_hashset::probeset_type + */ + template + struct probeset_type + {}; +#else + using intrusive::hopscotch_hashset::probeset_type; +#endif + + using intrusive::hopscotch_hashset::list; + using intrusive::hopscotch_hashset::vector; + + /// Type traits for hopscotch hashmap class + struct traits + { + /// Hash functors tuple + /** + This is mandatory type and has no predefined one. + + At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are defined as std::tuple< H1, H2, ... Hn > : + \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode + The number of hash functors specifies the number \p k - the count of hash tables in hopscotch hashing. + + To specify hash tuple in traits you should use \p cds::opt::hash_tuple: + \code + struct my_traits: public cds::container::hopscotch_hashmap::traits { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + }; + \endcode + */ + typedef cds::opt::none hash; + + /// Concurrent access policy + /** + Available opt::mutex_policy types: + - hopscotch_hashmap::striping - simple, but the lock array is not resizable + - hopscotch_hashmap::refinable - resizable lock array, but more complex access to set data. + + Default is hopscotch_hashmap::striping. + */ + typedef hopscotch_hashmap::striping<> mutex_policy; + + /// Key equality functor + /** + Default is std::equal_to + */ + typedef opt::none equal_to; + + /// Key comparison functor + /** + No default functor is provided. If the option is not specified, the \p less is used. + */ + typedef opt::none compare; + + /// specifies binary predicate used for key comparison. + /** + Default is \p std::less. + */ + typedef opt::none less; + + /// Item counter + /** + The type for item counting feature. + Default is cds::atomicity::item_counter + + Only atomic item counter type is allowed. + */ + typedef cds::intrusive::hopscotch_hashset::traits::item_counter item_counter; + + /// Allocator type + /** + The allocator type for allocating bucket tables. + Default is \p CDS_DEFAULT_ALLOCATOR that is \p std::allocator + */ + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Node allocator type + /** + If this type is not set explicitly, the \ref allocator type is used. + */ + typedef opt::none node_allocator; + + /// Store hash value into items. See hopscotch_hashmap::store_hash for explanation + static bool const store_hash = false; + + /// Probe-set type. See \ref probeset_type option for explanation + typedef hopscotch_hashmap::list probeset_type; + + /// Internal statistics + typedef empty_stat stat; + }; + + /// Metafunction converting option list to hopscotch hashmap traits + /** + Template argument list \p Options... are: + - \p opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are passed as std::tuple< H1, H2, ... Hn > . The number of hash functors specifies + the number \p k - the count of hash tables in hopscotch hashing. + - \p opt::mutex_policy - concurrent access policy. + Available policies: \p hopscotch_hashmap::striping, \p hopscotch_hashmap::refinable. + Default is \p %hopscotch_hashmap::striping. + - \p opt::equal_to - key equality functor like \p std::equal_to. + If this functor is defined then the probe-set will be unordered. + If \p %opt::compare or \p %opt::less option is specified too, then the probe-set will be ordered + and \p %opt::equal_to will be ignored. + - \p opt::compare - key comparison functor. No default functor is provided. + If the option is not specified, the \p %opt::less is used. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::item_counter - the type of item counting feature. Default is \p opt::v::sequential_item_counter. + - \p opt::allocator - the allocator type using for allocating bucket tables. + Default is \ref CDS_DEFAULT_ALLOCATOR + - \p opt::node_allocator - the allocator type using for allocating set's items. If this option + is not specified then the type defined in \p %opt::allocator option is used. + - \p hopscotch_hashmap::store_hash - this option reserves additional space in the node to store the hash value + of the object once it's introduced in the container. When this option is used, + the unordered container will store the calculated hash value in the node and rehashing operations won't need + to recalculate the hash of the value. This option will improve the performance of unordered containers + when rehashing is frequent or hashing the value is a slow operation. Default value is \p false. + - \ref intrusive::hopscotch_hashset::probeset_type "hopscotch_hashset::probeset_type" - type of probe set, may be \p hopscotch_hashmap::list or hopscotch_hashmap::vector, + Default is \p hopscotch_hashmap::list. + - \p opt::stat - internal statistics. Possibly types: \p hopscotch_hashmap::stat, \p hopscotch_hashmap::empty_stat. + Default is \p %hopscotch_hashmap::empty_stat + */ + template + struct make_traits { + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< hopscotch_hashmap::traits, Options... >::type + ,Options... + >::type type ; ///< Result of metafunction + }; + } // namespace hopscotch_hashmap +}} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_DETAILS_HOPSCOTCH_HASHMAP_BASE_H diff --git a/cds/container/details/hopscotch_hashset_base.h b/cds/container/details/hopscotch_hashset_base.h new file mode 100644 index 000000000..cd8584ec5 --- /dev/null +++ b/cds/container/details/hopscotch_hashset_base.h @@ -0,0 +1,269 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSLIB_CONTAINER_DETAILS_HOPSCOTCH_HASHSET_BASE_H +#define CDSLIB_CONTAINER_DETAILS_HOPSCOTCH_HASHSET_BASE_H + +#include + +namespace cds { namespace container { + + /// CuckooSet and CuckooMap related definitions + /** @ingroup cds_nonintrusive_helper + */ + namespace hopscotch_hashset { +#ifdef CDS_DOXYGEN_INVOKED + /// Lock striping concurrent access policy. This is typedef for intrusive::hopscotch_hashset::striping template + class striping + {}; +#else + using intrusive::hopscotch_hashset::striping; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Refinable concurrent access policy. This is typedef for intrusive::hopscotch_hashset::refinable template + class refinable + {}; +#else + using intrusive::hopscotch_hashset::refinable; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Striping internal statistics. This is typedef for intrusive::hopscotch_hashset::striping_stat + class striping_stat + {}; +#else + using intrusive::hopscotch_hashset::striping_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Empty striping internal statistics. This is typedef for intrusive::hopscotch_hashset::empty_striping_stat + class empty_striping_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_striping_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Refinable internal statistics. This is typedef for intrusive::hopscotch_hashset::refinable_stat + class refinable_stat + {}; +#else + using intrusive::hopscotch_hashset::refinable_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Empty refinable internal statistics. This is typedef for intrusive::hopscotch_hashset::empty_refinable_stat + class empty_refinable_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_refinable_stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Cuckoo statistics. This is typedef for intrusive::hopscotch_hashset::stat + class stat + {}; +#else + using intrusive::hopscotch_hashset::stat; +#endif + +#ifdef CDS_DOXYGEN_INVOKED + /// Cuckoo empty statistics.This is typedef for intrusive::hopscotch_hashset::empty_stat + class empty_stat + {}; +#else + using intrusive::hopscotch_hashset::empty_stat; +#endif + + /// Option specifying whether to store hash values in the node + /** + This option reserves additional space in the hook to store the hash value of the object once it's introduced in the container. + When this option is used, the unordered container will store the calculated hash value in the hook and rehashing operations won't need + to recalculate the hash of the value. This option will improve the performance of unordered containers + when rehashing is frequent or hashing the value is a slow operation + + The \p Enable template parameter toggles the feature: + - the value \p true enables storing the hash values + - the value \p false disables storing the hash values + */ + template + struct store_hash + { + //@cond + template + struct pack: public Base { + static bool const store_hash = Enable; + }; + //@endcond + }; + +#ifdef CDS_DOXYGEN_INVOKED + /// Probe set type option + /** + @copydetails cds::intrusive::hopscotch_hashset::probeset_type + */ + template + struct probeset_type + {}; +#else + using intrusive::hopscotch_hashset::probeset_type; +#endif + + using intrusive::hopscotch_hashset::list; + using intrusive::hopscotch_hashset::vector; + + /// Type traits for CuckooSet and CuckooMap classes + struct traits + { + /// Hash functors tuple + /** + This is mandatory type and has no predefined one. + + At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are defined as std::tuple< H1, H2, ... Hn > : + \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode + The number of hash functors specifies the number \p k - the count of hash tables in hopscotch hashing. + + To specify hash tuple in traits you should use \p cds::opt::hash_tuple: + \code + struct my_traits: public cds::container::hopscotch_hashset::traits { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + }; + \endcode + */ + typedef cds::opt::none hash; + + /// Concurrent access policy + /** + Available opt::mutex_policy types: + - hopscotch_hashset::striping - simple, but the lock array is not resizable + - hopscotch_hashset::refinable - resizable lock array, but more complex access to set data. + + Default is hopscotch_hashset::striping. + */ + typedef hopscotch_hashset::striping<> mutex_policy; + + /// Key equality functor + /** + Default is std::equal_to + */ + typedef opt::none equal_to; + + /// Key comparison functor + /** + No default functor is provided. If the option is not specified, the \p less is used. + */ + typedef opt::none compare; + + /// specifies binary predicate used for key comparison. + /** + Default is \p std::less. + */ + typedef opt::none less; + + /// Item counter + /** + The type for item counting feature. + Default is cds::atomicity::item_counter + + Only atomic item counter type is allowed. + */ + typedef cds::intrusive::hopscotch_hashset::traits::item_counter item_counter; + + /// Allocator type + /** + The allocator type for allocating bucket tables. + Default is \p CDS_DEFAULT_ALLOCATOR that is \p std::allocator + */ + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Node allocator type + /** + If this type is not set explicitly, the \ref allocator type is used. + */ + typedef opt::none node_allocator; + + /// Store hash value into items. See hopscotch_hashset::store_hash for explanation + static bool const store_hash = false; + + /// Probe-set type. See \ref probeset_type option for explanation + typedef hopscotch_hashset::list probeset_type; + + /// Internal statistics + typedef empty_stat stat; + }; + + /// Metafunction converting option list to CuckooSet/CuckooMap traits + /** + Template argument list \p Options... are: + - \p opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are passed as std::tuple< H1, H2, ... Hn > . The number of hash functors specifies + the number \p k - the count of hash tables in hopscotch_hashset hashing. + - \p opt::mutex_policy - concurrent access policy. + Available policies: \p hopscotch_hashset::striping, \p hopscotch_hashset::refinable. + Default is \p %hopscotch_hashset::striping. + - \p opt::equal_to - key equality functor like \p std::equal_to. + If this functor is defined then the probe-set will be unordered. + If \p %opt::compare or \p %opt::less option is specified too, then the probe-set will be ordered + and \p %opt::equal_to will be ignored. + - \p opt::compare - key comparison functor. No default functor is provided. + If the option is not specified, the \p %opt::less is used. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::item_counter - the type of item counting feature. Default is \p opt::v::sequential_item_counter. + - \p opt::allocator - the allocator type using for allocating bucket tables. + Default is \ref CDS_DEFAULT_ALLOCATOR + - \p opt::node_allocator - the allocator type using for allocating set's items. If this option + is not specified then the type defined in \p %opt::allocator option is used. + - \p hopscotch_hashset::store_hash - this option reserves additional space in the node to store the hash value + of the object once it's introduced in the container. When this option is used, + the unordered container will store the calculated hash value in the node and rehashing operations won't need + to recalculate the hash of the value. This option will improve the performance of unordered containers + when rehashing is frequent or hashing the value is a slow operation. Default value is \p false. + - \ref intrusive::hopscotch_hashset::probeset_type "hopscotch_hashset::probeset_type" - type of probe set, may be \p hopscotch_hashset::list or hopscotch_hashset::vector, + Default is \p hopscotch_hashset::list. + - \p opt::stat - internal statistics. Possibly types: \p hopscotch_hashset::stat, \p hopscotch_hashset::empty_stat. + Default is \p %hopscotch_hashset::empty_stat + */ + template + struct make_traits { + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< hopscotch_hashset::traits, Options... >::type + ,Options... + >::type type ; ///< Result of metafunction + }; + } // namespace hopscotch_hashset +}} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_DETAILS_CUCKOO_BASE_H diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index c86cdf767..f2bb9ac9c 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -1,660 +1,772 @@ /* -* concurrent_hopscotch_hash_set.h -* -* Created on: 23.12.2017 -* Original idea by Maurice Herlihy, Nir Shavit, Moran Tzafrir: http://people.csail.mit.edu/shanir/publications/disc2008_submission_98.pdf -* Based on implementation by Sathya Hariesh Prakash, Royston Monteiro, Mohan Pandiyan: https://github.com/harieshsathya/Hopscotch-Hashing -* -* This version modifiers: Leonid Skorospelov, Andrey Chulanov, Roman Stetskevich + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HOPSCOTCHHASHMAP_H_ -#define HOPSCOTCHHASHMAP_H_ -#include -#include -#include -#include - -namespace cds { - namespace container { - template - struct hopscotch_hashmap { - - typedef KEY key_type; ///< key type - typedef DATA mapped_type; ///< type of value stored in the map - typedef std::pair value_type; ///< Pair type - - typedef cds::atomicity::item_counter item_counter; - item_counter m_item_counter; - - private: - static const int HOP_RANGE = 32; - static const int ADD_RANGE = 256; - static const int MAX_SEGMENTS = 1048576; - static const int MAX_TRIES = 2; - KEY* BUSY; - DATA* BUSYD; - struct Bucket { - - static const unsigned int _empty_hop_info = 0; - KEY* _empty_key = NULL; - DATA* _empty_data = NULL; - - std::atomic _hop_info; - KEY* _key; - DATA* _data; - std::atomic _lock; - std::atomic _timestamp; - std::mutex lock_mutex; - - Bucket() { - _hop_info = _empty_hop_info; - _lock = 0; - _key = _empty_key; - _data = _empty_data; - _timestamp = 0; - } - - void lock() { - lock_mutex.lock(); - while (1) { - if (_lock == 0) { - _lock = 1; - lock_mutex.unlock(); - break; - } - } - } - - void unlock() { - lock_mutex.lock(); - _lock = 0; - lock_mutex.unlock(); - } - - }; - - Bucket* segments_arys; - - template - unsigned int calc_hash(K const& key) { - std::hash hash_fn; - return hash_fn(key) % MAX_SEGMENTS; - } - - void resize() { - // TODO need to find out if we need one - } - - public: - static bool const c_isSorted = false; ///< whether the probe set should be ordered - - hopscotch_hashmap() { - segments_arys = new Bucket[MAX_SEGMENTS + ADD_RANGE]; - m_item_counter.reset(); - BUSY = (KEY*)std::malloc(sizeof(KEY)); - } - - ~hopscotch_hashmap() { - std::free(BUSY); - std::free(segments_arys); - } - - bool empty() { - return size() == 0; - } - - int size() { - return m_item_counter; - /* - unsigned int counter = 0; - const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); - for (unsigned int iElm = 0; iElm < num_elm; ++iElm) { - if (Bucket::_empty_hop_info != (segments_arys + iElm)->_hop_info) { - counter += __popcnt((segments_arys + iElm)->_hop_info); - } - } - return counter; - */ - } - - /// Checks whether the map contains \p key - /** - The function searches the item with key equal to \p key - and returns \p true if it is found, and \p false otherwise. - */ - template - bool contains(K const& key) - { - return find_with(key, [=](K const& one, K const& two) { return one != two; }, [](mapped_type&) {}); - } - - /// Checks whether the map contains \p key using \p pred predicate for searching - /** - The function is similar to contains( key ) but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p Less must imply the same element order as the comparator used for building the map. - */ - template - bool contains(K const& key, Predicate pred) - { - return find_with(key, pred, [](mapped_type&) {}); - } - - /// Find the key \p key - /** \anchor cds_nonintrusive_CuckooMap_find_func - - The function searches the item with key equal to \p key and calls the functor \p f for item found. - The interface of \p Func functor is: - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - where \p item is the item found. - - The functor may change \p item.second. - - The function returns \p true if \p key is found, \p false otherwise. - */ - template - bool find_with(K const& key, Func f) - { - return find_with(key, [=](K const& one, K const& two) { return one != two; }, f); - } - - template - bool find(K const& key, Predicate pred) - { - return find_with(key, pred, [](mapped_type&) {}); - } - - template - bool find(K const& key) - { - return find_with(key, [=](K const& one, K const& two) { return one != two; }, [](mapped_type&) {}); - } - - template - bool find_with(K const& key, Predicate pred, Func f) - { - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - timestamp = start_bucket->_timestamp; - unsigned int hop_info = start_bucket->_hop_info; - Bucket* check_bucket = start_bucket; - unsigned int temp; - for (int i = 0; i < HOP_RANGE; i++) { - temp = hop_info; - temp = temp >> i; - - if (temp & 1) { - if (pred(key, *(check_bucket->_key)) == 0) { - f(*(check_bucket->_data)); - return true; - } - } - ++check_bucket; - } - ++try_counter; - } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - - if (timestamp != start_bucket->_timestamp) { - Bucket* check_bucket = start_bucket; - for (int i = 0; i < HOP_RANGE; i++) { - if (pred(key, *(check_bucket->_key)) == 0) { - f(*(check_bucket->_data)); - return true; - } - ++check_bucket; - } - } - return false; - } - - /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... - /** - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool emplace(K&& key, Args&&... args) - { - return insert(std::forward(key), mapped_type(std::forward(args)...)); - } - - /// Clears the map - void clear() - { - const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); - Bucket *start = segments_arys; - for (unsigned int iElm = 0; iElm < num_elm; ++iElm, ++start) { - start->lock(); - } - m_item_counter.reset(); - segments_arys = (Bucket *)memset(segments_arys, 0, (MAX_SEGMENTS + ADD_RANGE) * sizeof(Bucket)); - } - /// Updates the node - /** - The operation performs inserting or changing data with lock-free manner. - - If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. - Otherwise, the functor \p func is called with item found. - The functor \p func signature is: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - an item of the map for \p key - - Returns std::pair where \p first is \p true if operation is successful, - i.e. the node has been inserted or updated, - \p second is \p true if new item has been added or \p false if the item with \p key - already exists. - */ - template - std::pair update(K const& key, Func func, bool bAllowInsert = true) - { - mapped_type def_val; - return update(key, def_val, func, bAllowInsert); - } - - template - std::pair update(K const& key, V const& val) - { - return update(key, val, [](V const&) {}); - } - - /// Updates the node - /** - The operation performs inserting or changing data with lock-free manner. - - If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. - Otherwise, the functor \p func is called with item found. - The functor \p func signature is: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - an item of the map for \p key - - Returns std::pair where \p first is \p true if operation is successful, - i.e. the node has been inserted or updated, - \p second is \p true if new item has been added or \p false if the item with \p key - already exists. - */ - template - std::pair update(K const& key, V const& val, Func func, bool bAllowInsert = true) - { - bool inserted = false; - - if (!contains(key)) { - if (bAllowInsert) { - if (insert(key, val)) { - inserted = true; - } - else { - return make_pair(false, false); - } - } - else { - return make_pair(false, false); - } - } - - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - timestamp = start_bucket->_timestamp; - unsigned int hop_info = start_bucket->_hop_info; - Bucket* check_bucket = start_bucket; - unsigned int temp; - for (int i = 0; i < HOP_RANGE; i++) { - temp = hop_info; - temp = temp >> i; - - if (temp & 1) { - if (key == *(check_bucket->_key)) { - *(check_bucket->_data) = val; - func(*(check_bucket->_data)); - return make_pair(true, inserted); - } - } - ++check_bucket; - } - ++try_counter; - } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - - if (timestamp != start_bucket->_timestamp) { - Bucket* check_bucket = start_bucket; - for (int i = 0; i < HOP_RANGE; i++) { - if (key == *(check_bucket->_key)) { - *(check_bucket->_data) = val; - func(*(check_bucket->_data)); - return make_pair(true, inserted); - } - ++check_bucket; - } - } - return make_pair(false, inserted); - } - - template - V get(K const& key) { - return get(key, [=](K const& one, K const& two) { return one != two; }); - } - - template - V get(K const& key, Pred cmp) { - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - unsigned int try_counter = 0; - unsigned int timestamp; - do { - timestamp = start_bucket->_timestamp; - unsigned int hop_info = start_bucket->_hop_info; - Bucket* check_bucket = start_bucket; - unsigned int temp; - for (int i = 0; i < HOP_RANGE; i++) { - temp = hop_info; - temp = temp >> i; - - if (temp & 1) { - if (cmp(key, *(check_bucket->_key)) == 0) { - return *(check_bucket->_data); - } - } - ++check_bucket; - } - ++try_counter; - } while (timestamp != start_bucket->_timestamp && try_counter < MAX_TRIES); - - if (timestamp != start_bucket->_timestamp) { - Bucket* check_bucket = start_bucket; - for (int i = 0; i < HOP_RANGE; i++) { - if (cmp(key, *(check_bucket->_key)) == 0) { - return *(check_bucket->_data); - } - ++check_bucket; - } - } - return NULL; - } - - /// Inserts new node and initialize it by a functor - /** - This function inserts new node with key \p key and if inserting is successful then it calls - \p func functor with signature - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - - The argument \p item of user-defined functor \p func is the reference - to the map's item inserted: - - item.first is a const reference to item's key that cannot be changed. - - item.second is a reference to item's value that may be changed. - - The key_type should be constructible from value of type \p K. - - The function allows to split creating of new item into two part: - - create item from \p key; - - insert new item into the map; - - if inserting is successful, initialize the value of item by calling \p func functor - - This can be useful if complete initialization of object of \p value_type is heavyweight and - it is preferable that the initialization should be completed only if inserting is successful. - */ - template - bool insert_with(const K& key, Func func) - { - mapped_type def_val; - return insert_with(key, def_val, func); - } - - /// Inserts new node - /** - The function creates a node with copy of \p val value - and then inserts the node created into the map. - - Preconditions: - - The \ref key_type should be constructible from \p key of type \p K. - - The \ref value_type should be constructible from \p val of type \p V. - - Returns \p true if \p val is inserted into the set, \p false otherwise. - */ - template - bool insert_with(K const& key, V const& val, Func func) - { - int tmp_val = 1; - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - if (contains(key)) { - start_bucket->unlock(); - return false; - } - - Bucket* free_bucket = start_bucket; - int free_distance = 0; - for (; free_distance < ADD_RANGE; ++free_distance) { - std::atomic _atomic = free_bucket->_key; - K* _null_key = Bucket::_empty_key; - if (_null_key == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { - break; - } - ++free_bucket; - } - - if (free_distance < ADD_RANGE) { - do { - if (free_distance < HOP_RANGE) { - start_bucket->_hop_info |= (1 << free_distance); - *(free_bucket->_data) = val; - *(free_bucket->_key) = key; - ++m_item_counter; - start_bucket->unlock(); - func(*(free_bucket->_data)); - return true; - } - find_closer_bucket(&free_bucket, &free_distance, tmp_val); - } while (0 != tmp_val); - } - start_bucket->unlock(); - - this->resize(); - - return false; - } - - /// Inserts new node with key and default value - /** - The function creates a node with \p key and default value, and then inserts the node created into the map. - - Preconditions: - - The \ref key_type should be constructible from a value of type \p K. - In trivial case, \p K is equal to \ref key_type. - - The \ref mapped_type should be default-constructible. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert(K const& key) - { - mapped_type def_data; - return insert_with(key, def_data, [](mapped_type&) {}); - } - - /// Inserts new node with key and default value - /** - The function creates a node with \p key and \p value, and then inserts the node created into the map. - - Preconditions: - - The \ref key_type should be constructible from a value of type \p K. - In trivial case, \p K is equal to \ref key_type. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert(K const& key, V const& val) - { - return insert_with(key, val, [](mapped_type&) {}); - } - - /// Delete \p key from the map - /** \anchor cds_nonintrusive_CuckooMap_erase_val - - Return \p true if \p key is found and deleted, \p false otherwise - */ - template - bool erase(K const& key) - { - return erase(key, [](mapped_type&) {}); - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_val "erase(Q const&)" - but \p pred is used for key comparing. - If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. - If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. - \p Predicate must imply the same element order as the comparator used for building the map. - */ - template - bool erase_with(K const& key, Predicate pred) - { - return erase_with(key, pred, [](mapped_type&) {}); - } - - /// Delete \p key from the map - /** \anchor cds_nonintrusive_CuckooMap_erase_func - - The function searches an item with key \p key, calls \p f functor - and deletes the item. If \p key is not found, the functor is not called. - - The functor \p Func interface: - \code - struct extractor { - void operator()(value_type& item) { ... } - }; - \endcode - - Return \p true if key is found and deleted, \p false otherwise - - See also: \ref erase - */ - template - bool erase(K const& key, Func f) - { - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - - unsigned int hop_info = start_bucket->_hop_info; - unsigned int mask = 1; - for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { - if (mask & hop_info) { - Bucket* check_bucket = start_bucket + i; - if (key == *(check_bucket->_key)) { - f(*(check_bucket->_data)); - check_bucket->_key = NULL; - check_bucket->_data = NULL; - start_bucket->_hop_info &= ~(1 << i); - --m_item_counter; - start_bucket->unlock(); - return true; - } - } - } - start_bucket->unlock(); - return false; - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_func "erase(Q const&, Func)" - but \p pred is used for key comparing. - If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. - If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. - \p Predicate must imply the same element order as the comparator used for building the map. - */ - template - bool erase_with(K const& key, Predicate pred, Func f) - { - unsigned int hash = calc_hash(key); - Bucket* start_bucket = segments_arys + hash; - start_bucket->lock(); - - unsigned int hop_info = start_bucket->_hop_info; - unsigned int mask = 1; - for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { - if (mask & hop_info) { - Bucket* check_bucket = start_bucket + i; - if (pred(key, *(check_bucket->_key)) == 0) { - f(*(check_bucket->_data)); - check_bucket->_key = NULL; - check_bucket->_data = NULL; - start_bucket->_hop_info &= ~(1 << i); - start_bucket->unlock(); - --m_item_counter; - return true; - } - } - } - start_bucket->unlock(); - return false; - } - - void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { - Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); - for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { - unsigned int start_hop_info = move_bucket->_hop_info; - int move_free_distance = -1; - unsigned int mask = 1; - for (int i = 0; i < free_dist; ++i, mask <<= 1) { - if (mask & start_hop_info) { - move_free_distance = i; - break; - } - } - if (-1 != move_free_distance) { - move_bucket->lock(); - if (start_hop_info == move_bucket->_hop_info) { - Bucket* new_free_bucket = move_bucket + move_free_distance; - move_bucket->_hop_info |= (1 << free_dist); - (*free_bucket)->_data = new_free_bucket->_data; - (*free_bucket)->_key = new_free_bucket->_key; - ++(move_bucket->_timestamp); - new_free_bucket->_key = BUSY; - new_free_bucket->_data = BUSYD; - move_bucket->_hop_info &= ~(1 << move_free_distance); - *free_bucket = new_free_bucket; - *free_distance -= free_dist; - move_bucket->unlock(); - return; - } - move_bucket->unlock(); - } - ++move_bucket; - } - (*free_bucket)->_key = NULL; - val = 0; - *free_distance = 0; - } - - }; - } -} - -#endif /* HOPSCOTCHHASHMAP_H_ */ +#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H +#define CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H + +#include +#include + +namespace cds { namespace container { + + //@cond + namespace details { + template + struct make_hopscotch_hashmap + { + typedef Key key_type; ///< key type + typedef T mapped_type; ///< type of value stored in the map + typedef std::pair value_type; ///< Pair type + + typedef Traits original_traits; + typedef typename original_traits::probeset_type probeset_type; + static bool const store_hash = original_traits::store_hash; + static unsigned int const store_hash_count = store_hash ? ((unsigned int) std::tuple_size< typename original_traits::hash::hash_tuple_type >::value) : 0; + + struct node_type: public intrusive::hopscotch_hashset::node + { + value_type m_val; + + template + node_type( K const& key ) + : m_val( std::make_pair( key_type(key), mapped_type())) + {} + + template + node_type( K const& key, Q const& v ) + : m_val( std::make_pair( key_type(key), mapped_type(v))) + {} + + template + node_type( K&& key, Args&&... args ) + : m_val( std::forward(key), std::move( mapped_type(std::forward(args)...))) + {} + }; + + struct key_accessor { + key_type const& operator()( node_type const& node ) const + { + return node.m_val.first; + } + }; + + struct intrusive_traits: public original_traits + { + typedef intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< store_hash_count > + > hook; + + typedef cds::intrusive::hopscotch_hashset::traits::disposer disposer; + + typedef typename std::conditional< + std::is_same< typename original_traits::equal_to, opt::none >::value + , opt::none + , cds::details::predicate_wrapper< node_type, typename original_traits::equal_to, key_accessor > + >::type equal_to; + + typedef typename std::conditional< + std::is_same< typename original_traits::compare, opt::none >::value + , opt::none + , cds::details::compare_wrapper< node_type, typename original_traits::compare, key_accessor > + >::type compare; + + typedef typename std::conditional< + std::is_same< typename original_traits::less, opt::none >::value + ,opt::none + ,cds::details::predicate_wrapper< node_type, typename original_traits::less, key_accessor > + >::type less; + + typedef opt::details::hash_list_wrapper< typename original_traits::hash, node_type, key_accessor > hash; + }; + + typedef intrusive::HopscotchHashset< node_type, intrusive_traits > type; + }; + } // namespace details + //@endcond + + /// Cuckoo hash map + /** @ingroup cds_nonintrusive_map + + Source + - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Cuckoo Hashing. Technical report" + - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming" + + About Cuckoo hashing + + [From "The Art of Multiprocessor Programming"] + Cuckoo hashing is a hashing algorithm in which a newly added item displaces any earlier item + occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set f size + N = 2k we use a two-entry array of tables, and two independent hash functions, + h0, h1: KeyRange -> 0,...,k-1 + mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set, + find(x) tests whether either table[0][h0(x)] or table[1][h1(x)] is + equal to \p x. Similarly, erase(x)checks whether \p x is in either table[0][h0(x)] + or table[1][h1(x)], ad removes it if found. + + The insert(x) successively "kicks out" conflicting items until every key has a slot. + To add \p x, the method swaps \p x with \p y, the current occupant of table[0][h0(x)]. + If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y + for the current occupant of table[1][h1(y)] in the same way. As before, if the prior value + was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables) + until it finds an empty slot. We might not find an empty slot, either because the table is full, + or because the sequence of displacement forms a cycle. We therefore need an upper limit on the + number of successive displacements we are willing to undertake. When this limit is exceeded, + we resize the hash table, choose new hash functions and start over. + + For concurrent hopscotch hashing, rather than organizing the set as a two-dimensional table of + items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set + of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm + tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set + holds no more than THRESHOLD < PROBE_SET items. While method calls are in-flight, a probe + set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items. + + In current implementation, a probe set can be defined either as a (single-linked) list + or as a fixed-sized vector, optionally ordered. + + In description above two-table hopscotch hashing (k = 2) has been considered. + We can generalize this approach for k >= 2 when we have \p k hash functions + h[0], ... h[k-1] and \p k tables table[0], ... table[k-1]. + + The search in probe set is linear, the complexity is O(PROBE_SET) . + The probe set may be ordered or not. Ordered probe set can be a little better since + the average search complexity is O(PROBE_SET/2). + However, the overhead of sorting can eliminate a gain of ordered search. + + The probe set is ordered if \p compare or \p less is specified in \p Traits + template parameter. Otherwise, the probe set is unordered and \p Traits must contain + \p equal_to predicate. + + Template arguments: + - \p Key - key type + - \p T - the type stored in the map. + - \p Traits - map traits, default is \p hopscotch_hashmap::traits. + It is possible to declare option-based set with \p hopscotch_hashmap::make_traits metafunction + result as \p Traits template argument. + + Examples + + Declares hopscotch mapping from \p std::string to struct \p foo. + For hopscotch hashing we should provide at least two hash functions: + \code + struct hash1 { + size_t operator()(std::string const& s) const + { + return cds::opt::v::hash( s ); + } + }; + + struct hash2: private hash1 { + size_t operator()(std::string const& s) const + { + size_t h = ~( hash1::operator()(s)); + return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); + } + }; + \endcode + + Cuckoo-map with list-based unordered probe set and storing hash values + \code + #include + + // Declare type traits + struct my_traits: public cds::container::hopscotch_hashmap::traits + { + typedef std::equal_to< std::string > equal_to; + typedef std::tuple< hash1, hash2 > hash; + + static bool const store_hash = true; + }; + + // Declare HopscotchHashmap type + typedef cds::container::HopscotchHashmap< std::string, foo, my_traits > my_hopscotch_hashmap; + + // Equal option-based declaration + typedef cds::container::HopscotchHashmap< std::string, foo, + cds::container::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::equal_to< std::equal_to< std::string > > + ,cds::container::hopscotch_hashmap::store_hash< true > + >::type + > opt_hopscotch_hashmap; + \endcode + + If we provide \p less functor instead of \p equal_to + we get as a result a hopscotch map with ordered probe set that may improve + performance. + Example for ordered vector-based probe-set: + + \code + #include + + // Declare type traits + // We use a vector of capacity 4 as probe-set container and store hash values in the node + struct my_traits: public cds::container::hopscotch_hashmap::traits + { + typedef std::less< std::string > less; + typedef std::tuple< hash1, hash2 > hash; + typedef cds::container::hopscotch_hashmap::vector<4> probeset_type; + + static bool const store_hash = true; + }; + + // Declare HopscotchHashmap type + typedef cds::container::HopscotchHashmap< std::string, foo, my_traits > my_hopscotch_hashmap; + + // Equal option-based declaration + typedef cds::container::HopscotchHashmap< std::string, foo, + cds::container::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< std::less< std::string > > + ,cds::container::hopscotch_hashmap::probeset_type< cds::container::hopscotch_hashmap::vector<4> > + ,cds::container::hopscotch_hashmap::store_hash< true > + >::type + > opt_hopscotch_hashmap; + \endcode + + */ + template + class HopscotchHashmap: +#ifdef CDS_DOXYGEN_INVOKED + protected intrusive::HopscotchHashset< std::pair< Key const, T>, Traits> +#else + protected details::make_hopscotch_hashmap::type +#endif + { + //@cond + typedef details::make_hopscotch_hashmap maker; + typedef typename maker::type base_class; + //@endcond + public: + typedef Key key_type; ///< key type + typedef T mapped_type; ///< value type stored in the container + typedef std::pair value_type; ///< Key-value pair type stored in the map + typedef Traits traits; ///< Map traits + + typedef typename traits::hash hash; ///< hash functor tuple wrapped for internal use + typedef typename base_class::hash_tuple_type hash_tuple_type; ///< hash tuple type + + typedef typename base_class::mutex_policy mutex_policy; ///< Concurrent access policy, see \p hopscotch_hashmap::traits::mutex_policy + typedef typename base_class::stat stat; ///< internal statistics type + + static bool const c_isSorted = base_class::c_isSorted; ///< whether the probe set should be ordered + static size_t const c_nArity = base_class::c_nArity; ///< the arity of hopscotch_hashmap hashing: the number of hash functors provided; minimum 2. + + typedef typename base_class::key_equal_to key_equal_to; ///< Key equality functor; used only for unordered probe-set + + typedef typename base_class::key_comparator key_comparator; ///< key comparing functor based on opt::compare and opt::less option setter. Used only for ordered probe set + + typedef typename base_class::allocator allocator; ///< allocator type used for internal bucket table allocations + + /// Node allocator type + typedef typename std::conditional< + std::is_same< typename traits::node_allocator, opt::none >::value, + allocator, + typename traits::node_allocator + >::type node_allocator; + + /// item counter type + typedef typename traits::item_counter item_counter; + + protected: + //@cond + typedef typename base_class::scoped_cell_lock scoped_cell_lock; + typedef typename base_class::scoped_full_lock scoped_full_lock; + typedef typename base_class::scoped_resize_lock scoped_resize_lock; + typedef typename maker::key_accessor key_accessor; + + typedef typename base_class::value_type node_type; + typedef cds::details::Allocator< node_type, node_allocator > cxx_node_allocator; + //@endcond + + public: + static unsigned int const c_nDefaultProbesetSize = base_class::c_nDefaultProbesetSize; ///< default probeset size + static size_t const c_nDefaultInitialSize = base_class::c_nDefaultInitialSize; ///< default initial size + static unsigned int const c_nRelocateLimit = base_class::c_nRelocateLimit; ///< Count of attempts to relocate before giving up + + protected: + //@cond + template + static node_type * alloc_node( K const& key ) + { + return cxx_node_allocator().New( key ); + } + template + static node_type * alloc_node( K&& key, Args&&... args ) + { + return cxx_node_allocator().MoveNew( std::forward( key ), std::forward(args)... ); + } + + static void free_node( node_type * pNode ) + { + cxx_node_allocator().Delete( pNode ); + } + //@endcond + + protected: + //@cond + struct node_disposer { + void operator()( node_type * pNode ) + { + free_node( pNode ); + } + }; + + typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; + + //@endcond + + public: + /// Default constructor + /** + Initial size = \ref c_nDefaultInitialSize + + Probe set size: + - \ref c_nDefaultProbesetSize if \p probeset_type is \p hopscotch_hashmap::list + - \p Capacity if \p probeset_type is hopscotch_hashmap::vector + + Probe set threshold = probe set size - 1 + */ + HopscotchHashmap() + {} + + /// Constructs an object with given probe set size and threshold + /** + If probe set type is hopscotch_hashmap::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashmap( + size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold = 0 ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold ) + {} + + /// Constructs an object with given hash functor tuple + /** + The probe set size and threshold are set as default, see HopscotchHashmap() + */ + HopscotchHashmap( + hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( h ) + {} + + /// Constructs a map with given probe set properties and hash functor tuple + /** + If probe set type is hopscotch_hashmap::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashmap( + size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, h ) + {} + + /// Constructs a map with given hash functor tuple (move semantics) + /** + The probe set size and threshold are set as default, see HopscotchHashmap() + */ + HopscotchHashmap( + hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( std::forward(h)) + {} + + /// Constructs a map with given probe set properties and hash functor tuple (move semantics) + /** + If probe set type is hopscotch_hashmap::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashmap( + size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, std::forward(h)) + {} + + /// Destructor clears the map + ~HopscotchHashmap() + { + clear(); + } + + public: + /// Inserts new node with key and default value + /** + The function creates a node with \p key and default value, and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from a value of type \p K. + In trivial case, \p K is equal to \ref key_type. + - The \ref mapped_type should be default-constructible. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert( K const& key ) + { + return insert_with( key, [](value_type&){} ); + } + + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from \p key of type \p K. + - The \ref value_type should be constructible from \p val of type \p V. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + template + bool insert( K const& key, V const& val ) + { + return insert_with( key, [&val](value_type& item) { item.second = val ; } ); + } + + /// Inserts new node and initialize it by a functor + /** + This function inserts new node with key \p key and if inserting is successful then it calls + \p func functor with signature + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + + The argument \p item of user-defined functor \p func is the reference + to the map's item inserted: + - item.first is a const reference to item's key that cannot be changed. + - item.second is a reference to item's value that may be changed. + + The key_type should be constructible from value of type \p K. + + The function allows to split creating of new item into two part: + - create item from \p key; + - insert new item into the map; + - if inserting is successful, initialize the value of item by calling \p func functor + + This can be useful if complete initialization of object of \p value_type is heavyweight and + it is preferable that the initialization should be completed only if inserting is successful. + */ + template + bool insert_with( const K& key, Func func ) + { + scoped_node_ptr pNode( alloc_node( key )); + if ( base_class::insert( *pNode, [&func]( node_type& item ) { func( item.m_val ); } )) { + pNode.release(); + return true; + } + return false; + } + + /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace( K&& key, Args&&... args ) + { + scoped_node_ptr pNode( alloc_node( std::forward(key), std::forward(args)... )); + if ( base_class::insert( *pNode )) { + pNode.release(); + return true; + } + return false; + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update( K const& key, Func func, bool bAllowInsert = true ) + { + scoped_node_ptr pNode( alloc_node( key )); + std::pair res = base_class::update( *pNode, + [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_val ); }, + bAllowInsert + ); + if ( res.first && res.second ) + pNode.release(); + return res; + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( K const& key, Func func ) + { + return update( key, func, true ); + } + //@endcond + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_HopscotchHashmap_erase_val + + Return \p true if \p key is found and deleted, \p false otherwise + */ + template + bool erase( K const& key ) + { + node_type * pNode = base_class::erase(key); + if ( pNode ) { + free_node( pNode ); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_erase_val "erase(Q const&)" + but \p pred is used for key comparing. + If hopscotch map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If hopscotch map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with( K const& key, Predicate pred ) + { + CDS_UNUSED( pred ); + node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); + if ( pNode ) { + free_node( pNode ); + return true; + } + return false; + } + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_HopscotchHashmap_erase_func + + The function searches an item with key \p key, calls \p f functor + and deletes the item. If \p key is not found, the functor is not called. + + The functor \p Func interface: + \code + struct extractor { + void operator()(value_type& item) { ... } + }; + \endcode + + Return \p true if key is found and deleted, \p false otherwise + + See also: \ref erase + */ + template + bool erase( K const& key, Func f ) + { + node_type * pNode = base_class::erase( key ); + if ( pNode ) { + f( pNode->m_val ); + free_node( pNode ); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + If hopscotch map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If hopscotch map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with( K const& key, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + node_type * pNode = base_class::erase_with( key, cds::details::predicate_wrapper()); + if ( pNode ) { + f( pNode->m_val ); + free_node( pNode ); + return true; + } + return false; + } + + /// Find the key \p key + /** \anchor cds_nonintrusive_HopscotchHashmap_find_func + + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + where \p item is the item found. + + The functor may change \p item.second. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find( K const& key, Func f ) + { + return base_class::find( key, [&f](node_type& item, K const& ) { f( item.m_val );}); + } + + /// Find the key \p val using \p pred predicate for comparing + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_find_func "find(K const&, Func)" + but \p pred is used for key comparison. + If you use ordered hopscotch map, then \p Predicate should have the interface and semantics like \p std::less. + If you use unordered hopscotch map, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p pred must imply the same element order as the comparator used for building the map. + */ + template + bool find_with( K const& key, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, cds::details::predicate_wrapper(), + [&f](node_type& item, K const& ) { f( item.m_val );}); + } + + /// Checks whether the map contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains( K const& key ) + { + return base_class::contains( key ); + } + //@cond + template + CDS_DEPRECATED("the function is deprecated, use contains()") + bool find( K const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the map contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the map. + */ + template + bool contains( K const& key, Predicate pred ) + { + CDS_UNUSED( pred ); + return base_class::contains( key, cds::details::predicate_wrapper()); + } + //@cond + template + CDS_DEPRECATED("the function is deprecated, use contains()") + bool find_with( K const& key, Predicate pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Clears the map + void clear() + { + base_class::clear_and_dispose( node_disposer()); + } + + /// Checks if the map is empty + /** + Emptiness is checked by item counting: if item count is zero then the map is empty. + */ + bool empty() const + { + return base_class::empty(); + } + + /// Returns item count in the map + size_t size() const + { + return base_class::size(); + } + + /// Returns the size of hash table + /** + The hash table size is non-constant and can be increased via resizing. + */ + size_t bucket_count() const + { + return base_class::bucket_count(); + } + + /// Returns lock array size + /** + The lock array size is constant. + */ + size_t lock_count() const + { + return base_class::lock_count(); + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + + /// Returns const reference to mutex policy internal statistics + typename mutex_policy::statistics_type const& mutex_policy_statistics() const + { + return base_class::mutex_policy_statistics(); + } + }; +}} // namespace cds::container + +#endif //#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H diff --git a/cds/container/hopscotch_hashset.h b/cds/container/hopscotch_hashset.h new file mode 100644 index 000000000..6b471e051 --- /dev/null +++ b/cds/container/hopscotch_hashset.h @@ -0,0 +1,850 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHSET_H +#define CDSLIB_CONTAINER_HOPSCOTCH_HASHSET_H + +#include +#include + +namespace cds { namespace container { + + //@cond + namespace details { + template + struct make_hopscotch_hashset + { + typedef T value_type; + typedef Traits original_traits; + typedef typename original_traits::probeset_type probeset_type; + static bool const store_hash = original_traits::store_hash; + static unsigned int const store_hash_count = store_hash ? ((unsigned int) std::tuple_size< typename original_traits::hash::hash_tuple_type >::value) : 0; + + struct node_type: public intrusive::hopscotch_hashset::node + { + value_type m_val; + + template + node_type( Q const& v ) + : m_val(v) + {} + + template + node_type( Args&&... args ) + : m_val( std::forward(args)...) + {} + }; + + struct value_accessor { + value_type const& operator()( node_type const& node ) const + { + return node.m_val; + } + }; + + template + using predicate_wrapper = cds::details::binary_functor_wrapper< ReturnValue, Pred, node_type, value_accessor >; + + struct intrusive_traits: public original_traits + { + typedef intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< store_hash_count > + > hook; + + typedef cds::intrusive::hopscotch_hashset::traits::disposer disposer; + + typedef typename std::conditional< + std::is_same< typename original_traits::equal_to, opt::none >::value + , opt::none + , predicate_wrapper< typename original_traits::equal_to, bool > + >::type equal_to; + + typedef typename std::conditional< + std::is_same< typename original_traits::compare, opt::none >::value + , opt::none + , predicate_wrapper< typename original_traits::compare, int > + >::type compare; + + typedef typename std::conditional< + std::is_same< typename original_traits::less, opt::none >::value + ,opt::none + ,predicate_wrapper< typename original_traits::less, bool > + >::type less; + + typedef opt::details::hash_list_wrapper< typename original_traits::hash, node_type, value_accessor > hash; + }; + + typedef intrusive::HopscotchHashset< node_type, intrusive_traits > type; + }; + } // namespace details + //@endcond + + /// Hopscotch hash set + /** @ingroup cds_nonintrusive_set + + Source + - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Hopscotch Hashing. Technical report" + - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming" + + About Hopscotch hashing + + [From "The Art of Multiprocessor Programming"] + Hopscotch hashing is a hashing algorithm in which a newly added item displaces any earlier item + occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set f size + N = 2k we use a two-entry array of tables, and two independent hash functions, + h0, h1: KeyRange -> 0,...,k-1 + mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set, + find(x) tests whether either table[0][h0(x)] or table[1][h1(x)] is + equal to \p x. Similarly, erase(x)checks whether \p x is in either table[0][h0(x)] + or table[1][h1(x)], ad removes it if found. + + The insert(x) successively "kicks out" conflicting items until every key has a slot. + To add \p x, the method swaps \p x with \p y, the current occupant of table[0][h0(x)]. + If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y + for the current occupant of table[1][h1(y)] in the same way. As before, if the prior value + was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables) + until it finds an empty slot. We might not find an empty slot, either because the table is full, + or because the sequence of displacement forms a cycle. We therefore need an upper limit on the + number of successive displacements we are willing to undertake. When this limit is exceeded, + we resize the hash table, choose new hash functions and start over. + + For concurrent Hopscotch hashing, rather than organizing the set as a two-dimensional table of + items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set + of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm + tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set + holds no more than THRESHOLD < PROBE_SET items. While method calls are in-flight, a probe + set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items. + + In current implementation, a probe set can be defined either as a (single-linked) list + or as a fixed-sized vector, optionally ordered. + + In description above two-table Hopscotch hashing (k = 2) has been considered. + We can generalize this approach for k >= 2 when we have \p k hash functions + h[0], ... h[k-1] and \p k tables table[0], ... table[k-1]. + + The search in probe set is linear, the complexity is O(PROBE_SET) . + The probe set may be ordered or not. Ordered probe set can be a little better since + the average search complexity is O(PROBE_SET/2). + However, the overhead of sorting can eliminate a gain of ordered search. + + The probe set is ordered if \p compare or \p less is specified in \p Traits + template parameter. Otherwise, the probe set is unordered and \p Traits must contain + \p equal_to predicate. + + Template arguments: + - \p T - the type stored in the set. + - \p Traits - type traits. See hopscotch_hashset::traits for explanation. + It is possible to declare option-based set with hopscotch_hashset::make_traits metafunction result as \p Traits template argument. + + Examples + + Hopscotch-set with list-based unordered probe set and storing hash values + \code + #include + + // Data stored in hopscotch set + struct my_data + { + // key field + std::string strKey; + + // other data + // ... + }; + + // Provide equal_to functor for my_data since we will use unordered probe-set + struct my_data_equal_to { + bool operator()( const my_data& d1, const my_data& d2 ) const + { + return d1.strKey.compare( d2.strKey ) == 0; + } + + bool operator()( const my_data& d, const std::string& s ) const + { + return d.strKey.compare(s) == 0; + } + + bool operator()( const std::string& s, const my_data& d ) const + { + return s.compare( d.strKey ) == 0; + } + }; + + // Provide two hash functor for my_data + struct hash1 { + size_t operator()(std::string const& s) const + { + return cds::opt::v::hash( s ); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + struct hash2: private hash1 { + size_t operator()(std::string const& s) const + { + size_t h = ~( hash1::operator()(s)); + return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + // Declare type traits + struct my_traits: public cds::container::hopscotch_hashset::traits + { + typedef my_data_equa_to equal_to; + typedef std::tuple< hash1, hash2 > hash; + + static bool const store_hash = true; + }; + + // Declare HopscotchHashset type + typedef cds::container::HopscotchHashset< my_data, my_traits > my_hopscotch_hashset; + + // Equal option-based declaration + typedef cds::container::HopscotchHashset< my_data, + cds::container::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::equal_to< my_data_equal_to > + ,cds::container::hopscotch_hashset::store_hash< true > + >::type + > opt_hopscotch_hashset; + \endcode + + If we provide \p compare function instead of \p equal_to for \p my_data + we get as a result a hopscotch set with ordered probe set that may improve + performance. + Example for ordered vector-based probe-set: + + \code + #include + + // Data stored in hopscotch set + struct my_data + { + // key field + std::string strKey; + + // other data + // ... + }; + + // Provide compare functor for my_data since we want to use ordered probe-set + struct my_data_compare { + int operator()( const my_data& d1, const my_data& d2 ) const + { + return d1.strKey.compare( d2.strKey ); + } + + int operator()( const my_data& d, const std::string& s ) const + { + return d.strKey.compare(s); + } + + int operator()( const std::string& s, const my_data& d ) const + { + return s.compare( d.strKey ); + } + }; + + // Provide two hash functor for my_data + struct hash1 { + size_t operator()(std::string const& s) const + { + return cds::opt::v::hash( s ); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + struct hash2: private hash1 { + size_t operator()(std::string const& s) const + { + size_t h = ~( hash1::operator()(s)); + return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + // Declare type traits + // We use a vector of capacity 4 as probe-set container and store hash values in the node + struct my_traits: public cds::container::hopscotch_hashset::traits + { + typedef my_data_compare compare; + typedef std::tuple< hash1, hash2 > hash; + typedef cds::container::hopscotch_hashset::vector<4> probeset_type; + + static bool const store_hash = true; + }; + + // Declare HopscotchHashset type + typedef cds::container::HopscotchHashset< my_data, my_traits > my_hopscotch_hashset; + + // Equal option-based declaration + typedef cds::container::HopscotchHashset< my_data, + cds::container::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< my_data_compare > + ,cds::container::hopscotch_hashset::probeset_type< cds::container::hopscotch_hashset::vector<4> > + ,cds::container::hopscotch_hashset::store_hash< true > + >::type + > opt_hopscotch_hashset; + \endcode + + */ + template + class HopscotchHashset: +#ifdef CDS_DOXYGEN_INVOKED + protected intrusive::HopscotchHashset +#else + protected details::make_hopscotch_hashset::type +#endif + { + //@cond + typedef details::make_hopscotch_hashset maker; + typedef typename maker::type base_class; + //@endcond + + public: + typedef T value_type ; ///< value type stored in the container + typedef Traits traits ; ///< traits + + typedef typename traits::hash hash; ///< hash functor tuple wrapped for internal use + typedef typename base_class::hash_tuple_type hash_tuple_type; ///< Type of hash tuple + + typedef typename base_class::mutex_policy mutex_policy; ///< Concurrent access policy, see hopscotch_hashset::traits::mutex_policy + typedef typename base_class::stat stat; ///< internal statistics type + + + static bool const c_isSorted = base_class::c_isSorted; ///< whether the probe set should be ordered + static size_t const c_nArity = base_class::c_nArity; ///< the arity of hopscotch_hashset hashing: the number of hash functors provided; minimum 2. + + typedef typename base_class::key_equal_to key_equal_to; ///< Key equality functor; used only for unordered probe-set + + typedef typename base_class::key_comparator key_comparator; ///< key comparing functor based on \p Traits::compare and \p Traits::less option setter. Used only for ordered probe set + + typedef typename base_class::allocator allocator; ///< allocator type used for internal bucket table allocations + + /// Node allocator type + typedef typename std::conditional< + std::is_same< typename traits::node_allocator, opt::none >::value, + allocator, + typename traits::node_allocator + >::type node_allocator; + + /// item counter type + typedef typename traits::item_counter item_counter; + + protected: + //@cond + typedef typename base_class::value_type node_type; + typedef cds::details::Allocator< node_type, node_allocator > cxx_node_allocator; + //@endcond + + public: + static unsigned int const c_nDefaultProbesetSize = base_class::c_nDefaultProbesetSize; ///< default probeset size + static size_t const c_nDefaultInitialSize = base_class::c_nDefaultInitialSize; ///< default initial size + static unsigned int const c_nRelocateLimit = base_class::c_nRelocateLimit; ///< Count of attempts to relocate before giving up + + protected: + //@cond + template + static node_type * alloc_node( Q const& v ) + { + return cxx_node_allocator().New( v ); + } + template + static node_type * alloc_node( Args&&... args ) + { + return cxx_node_allocator().MoveNew( std::forward(args)... ); + } + + static void free_node( node_type * pNode ) + { + cxx_node_allocator().Delete( pNode ); + } + //@endcond + + protected: + //@cond + struct node_disposer { + void operator()( node_type * pNode ) + { + free_node( pNode ); + } + }; + + typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; + + //@endcond + + public: + /// Default constructor + /** + Initial size = \ref c_nDefaultInitialSize + + Probe set size: + - \ref c_nDefaultProbesetSize if \p probeset_type is \p hopscotch_hashset::list + - \p Capacity if \p probeset_type is hopscotch_hashset::vector + + Probe set threshold = probe set size - 1 + */ + HopscotchHashset() + {} + + /// Constructs the set object with given probe set size and threshold + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold = 0 ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold ) + {} + + /// Constructs the set object with given hash functor tuple + /** + The probe set size and threshold are set as default, see HopscotchHashset() + */ + HopscotchHashset( + hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( h ) + {} + + /// Constructs the set object with given probe set properties and hash functor tuple + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, h ) + {} + + /// Constructs the set object with given hash functor tuple (move semantics) + /** + The probe set size and threshold are set as default, see HopscotchHashset() + */ + HopscotchHashset( + hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( std::forward(h)) + {} + + /// Constructs the set object with given probe set properties and hash functor tuple (move semantics) + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, std::forward(h)) + {} + + /// Destructor clears the set + ~HopscotchHashset() + { + clear(); + } + + public: + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the set. + + The type \p Q should contain as minimum the complete key for the node. + The object of \ref value_type should be constructible from a value of type \p Q. + In trivial case, \p Q is equal to \ref value_type. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + template + bool insert( Q const& val ) + { + return insert( val, []( value_type& ) {} ); + } + + /// Inserts new node + /** + The function allows to split creating of new item into two part: + - create item with key only + - insert new item into the set + - if inserting is success, calls \p f functor to initialize value-field of new item . + + The functor signature is: + \code + void func( value_type& item ); + \endcode + where \p item is the item inserted. + + The type \p Q can differ from \ref value_type of items storing in the set. + Therefore, the \p value_type should be constructible from type \p Q. + + The user-defined functor is called only if the inserting is success. + */ + template + bool insert( Q const& val, Func f ) + { + scoped_node_ptr pNode( alloc_node( val )); + if ( base_class::insert( *pNode, [&f]( node_type& node ) { f( node.m_val ); } )) { + pNode.release(); + return true; + } + return false; + } + + /// Inserts data of type \ref value_type constructed with std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace( Args&&... args ) + { + scoped_node_ptr pNode( alloc_node( std::forward(args)... )); + if ( base_class::insert( *pNode )) { + pNode.release(); + return true; + } + return false; + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the set, then \p val is inserted into the set + iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item, const Q& val ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the set + - \p val - argument \p val passed into the \p %update() function + If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments + refer to the same thing. + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update( Q const& val, Func func, bool bAllowInsert = true ) + { + scoped_node_ptr pNode( alloc_node( val )); + std::pair res = base_class::update( *pNode, + [&val,&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_val, val ); }, + bAllowInsert + ); + if ( res.first && res.second ) + pNode.release(); + return res; + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( Q const& val, Func func ) + { + return update( val, func, true ); + } + //@endcond + + /// Delete \p key from the set + /** \anchor cds_nonintrusive_HopscotchHashset_erase + + Since the key of set's item type \ref value_type is not explicitly specified, + template parameter \p Q defines the key type searching in the list. + The set item comparator should be able to compare the type \p value_type + and the type \p Q. + + Return \p true if key is found and deleted, \p false otherwise + */ + template + bool erase( Q const& key ) + { + node_type * pNode = base_class::erase( key ); + if ( pNode ) { + free_node( pNode ); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashset_erase "erase(Q const&)" + but \p pred is used for key comparing. + If hopscotch set is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If hopscotch set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the set. + */ + template + bool erase_with( Q const& key, Predicate pred ) + { + CDS_UNUSED( pred ); + node_type * pNode = base_class::erase_with( key, typename maker::template predicate_wrapper()); + if ( pNode ) { + free_node( pNode ); + return true; + } + return false; + } + + /// Delete \p key from the set + /** \anchor cds_nonintrusive_HopscotchHashset_erase_func + + The function searches an item with key \p key, calls \p f functor + and deletes the item. If \p key is not found, the functor is not called. + + The functor \p Func interface is: + \code + struct functor { + void operator()(value_type const& val); + }; + \endcode + + Return \p true if key is found and deleted, \p false otherwise + */ + template + bool erase( Q const& key, Func f ) + { + node_type * pNode = base_class::erase( key ); + if ( pNode ) { + f( pNode->m_val ); + free_node( pNode ); + return true; + } + return false; + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashset_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + If you use ordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::less. + If you use unordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the set. + */ + template + bool erase_with( Q const& key, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + node_type * pNode = base_class::erase_with( key, typename maker::template predicate_wrapper()); + if ( pNode ) { + f( pNode->m_val ); + free_node( pNode ); + return true; + } + return false; + } + + /// Find the key \p val + /** \anchor cds_nonintrusive_HopscotchHashset_find_func + + The function searches the item with key equal to \p val and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& val ); + }; + \endcode + where \p item is the item found, \p val is the find function argument. + + The functor can change non-key fields of \p item. + The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor + can modify both arguments. + + The type \p Q can differ from \ref value_type of items storing in the container. + Therefore, the \p value_type should be comparable with type \p Q. + + The function returns \p true if \p val is found, \p false otherwise. + */ + template + bool find( Q& val, Func f ) + { + return base_class::find( val, [&f](node_type& item, Q& v) { f( item.m_val, v );}); + } + //@cond + template + bool find( Q const& val, Func f ) + { + return base_class::find( val, [&f](node_type& item, Q const& v) { f( item.m_val, v );}); + } + //@endcond + + /// Find the key \p val using \p pred predicate for comparing + /** + The function is an analog of \ref cds_nonintrusive_HopscotchHashset_find_func "find(Q&, Func)" + but \p pred is used for key comparison. + If you use ordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::less. + If you use unordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p pred must imply the same element order as the comparator used for building the set. + */ + template + bool find_with( Q& val, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( val, typename maker::template predicate_wrapper(), + [&f](node_type& item, Q& v) { f( item.m_val, v );}); + } + //@cond + template + bool find_with( Q const& val, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return base_class::find_with( val, typename maker::template predicate_wrapper(), + [&f](node_type& item, Q const& v) { f( item.m_val, v );}); + } + //@endcond + + /// Checks whether the set contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains( Q const& key ) + { + return base_class::find( key, [](node_type&, Q const&) {}); + } + //@cond + template + CDS_DEPRECATED("the function is deprecated, use contains()") + bool find( Q const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the set contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the set. + */ + template + bool contains( Q const& key, Predicate pred ) + { + CDS_UNUSED( pred ); + return base_class::find_with( key, typename maker::template predicate_wrapper(), [](node_type&, Q const&) {}); + } + //@cond + template + CDS_DEPRECATED("the function is deprecated, use contains()") + bool find_with( Q const& key, Predicate pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Clears the set + /** + The function erases all items from the set. + */ + void clear() + { + return base_class::clear_and_dispose( node_disposer()); + } + + /// Checks if the set is empty + /** + Emptiness is checked by item counting: if item count is zero then the set is empty. + */ + bool empty() const + { + return base_class::empty(); + } + + /// Returns item count in the set + size_t size() const + { + return base_class::size(); + } + + /// Returns the size of hash table + /** + The hash table size is non-constant and can be increased via resizing. + */ + size_t bucket_count() const + { + return base_class::bucket_count(); + } + + /// Returns lock array size + size_t lock_count() const + { + return base_class::lock_count(); + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + + /// Returns const reference to mutex policy internal statistics + typename mutex_policy::statistics_type const& mutex_policy_statistics() const + { + return base_class::mutex_policy_statistics(); + } + }; + +}} // namespace cds::container + +#endif //#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHSET_H diff --git a/cds/intrusive/hopscotch_hashset.h b/cds/intrusive/hopscotch_hashset.h new file mode 100644 index 000000000..35808d817 --- /dev/null +++ b/cds/intrusive/hopscotch_hashset.h @@ -0,0 +1,2829 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSLIB_INTRUSIVE_HOPSCOTCH_HASHSET_H +#define CDSLIB_INTRUSIVE_HOPSCOTCH_HASHSET_H + +#include +#include +#include +#include // ref +#include +#include +#include +#include +#include +#include + + +namespace cds { namespace intrusive { + + /// HopscotchHashset-related definitions + namespace hopscotch_hashset { + /// Option to define probeset type + /** + The option specifies probeset type for the HopscotchHashset. + Available values: + - \p cds::intrusive::hopscotch_hashset::list - the probeset is a single-linked list. + The node contains pointer to next node in probeset. + - \p cds::intrusive::hopscotch_hashset::vector - the probeset is a vector + with constant-size \p Capacity where \p Capacity is an unsigned int constant. + The node does not contain any auxiliary data. + */ + template + struct probeset_type + { + //@cond + template + struct pack: public Base { + typedef Type probeset_type; + }; + //@endcond + }; + + /// Option specifying whether to store hash values in the node + /** + This option reserves additional space in the hook to store the hash value of the object once it's introduced in the container. + When this option is used, the unordered container will store the calculated hash value in the hook and rehashing operations won't need + to recalculate the hash of the value. This option will improve the performance of unordered containers + when rehashing is frequent or hashing the value is a slow operation + + The \p Count template parameter defines the size of hash array. Remember that hopscotch hashing implies at least two + hash values per item. + + Possible values of \p Count: + - 0 - no hash storing in the node + - greater that 1 - store hash values. + + Value 1 is deprecated. + */ + template + struct store_hash + { + //@cond + template + struct pack: public Base { + static unsigned int const store_hash = Count; + }; + //@endcond + }; + + + //@cond + // Probeset type placeholders + struct list_probeset_class; + struct vector_probeset_class; + //@endcond + + //@cond + /// List probeset type + struct list; + //@endcond + + /// Vector probeset type + template + struct vector + { + /// Vector capacity + static unsigned int const c_nCapacity = Capacity; + }; + + /// HopscotchHashset node + /** + Template arguments: + - \p ProbesetType - type of probeset. Can be \p cds::intrusive::hopscotch_hashset::list + or \p cds::intrusive::hopscotch_hashset::vector. + - \p StoreHashCount - constant that defines whether to store node hash values. + See hopscotch_hashset::store_hash option for explanation + - \p Tag - a \ref cds_intrusive_hook_tag "tag" + */ + template + struct node +#ifdef CDS_DOXYGEN_INVOKED + { + typedef ProbesetType probeset_type ; ///< Probeset type + typedef Tag tag ; ///< Tag + static unsigned int const hash_array_size = StoreHashCount ; ///< The size of hash array + } +#endif +; + + //@cond + template + struct node< hopscotch_hashset::list, 0, Tag> + { + typedef list_probeset_class probeset_class; + typedef hopscotch_hashset::list probeset_type; + typedef Tag tag; + static unsigned int const hash_array_size = 0; + static unsigned int const probeset_size = 0; + + node * m_pNext; + + constexpr node() noexcept + : m_pNext( nullptr ) + {} + + void store_hash( size_t const* ) + {} + + size_t * get_hash() const + { + // This node type does not store hash values!!! + assert(false); + return nullptr; + } + + void clear() + { + m_pNext = nullptr; + } + }; + + template + struct node< hopscotch_hashset::list, StoreHashCount, Tag> + { + typedef list_probeset_class probeset_class; + typedef hopscotch_hashset::list probeset_type; + typedef Tag tag; + static unsigned int const hash_array_size = StoreHashCount; + static unsigned int const probeset_size = 0; + + node * m_pNext; + size_t m_arrHash[ hash_array_size ]; + + node() noexcept + : m_pNext( nullptr ) + { + memset( m_arrHash, 0, sizeof(m_arrHash)); + } + + void store_hash( size_t const* pHashes ) + { + memcpy( m_arrHash, pHashes, sizeof( m_arrHash )); + } + + size_t * get_hash() const + { + return const_cast( m_arrHash ); + } + + void clear() + { + m_pNext = nullptr; + } + }; + + template + struct node< hopscotch_hashset::vector, 0, Tag> + { + typedef vector_probeset_class probeset_class; + typedef hopscotch_hashset::vector probeset_type; + typedef Tag tag; + static unsigned int const hash_array_size = 0; + static unsigned int const probeset_size = probeset_type::c_nCapacity; + + node() noexcept + {} + + void store_hash( size_t const* ) + {} + + size_t * get_hash() const + { + // This node type does not store hash values!!! + assert(false); + return nullptr; + } + + void clear() + {} + }; + + template + struct node< hopscotch_hashset::vector, StoreHashCount, Tag> + { + typedef vector_probeset_class probeset_class; + typedef hopscotch_hashset::vector probeset_type; + typedef Tag tag; + static unsigned int const hash_array_size = StoreHashCount; + static unsigned int const probeset_size = probeset_type::c_nCapacity; + + size_t m_arrHash[ hash_array_size ]; + + node() noexcept + { + memset( m_arrHash, 0, sizeof(m_arrHash)); + } + + void store_hash( size_t const* pHashes ) + { + memcpy( m_arrHash, pHashes, sizeof( m_arrHash )); + } + + size_t * get_hash() const + { + return const_cast( m_arrHash ); + } + + void clear() + {} + }; + //@endcond + + + //@cond + struct default_hook { + typedef hopscotch_hashset::list probeset_type; + static unsigned int const store_hash = 0; + typedef opt::none tag; + }; + + template < typename HookType, typename... Options> + struct hook + { + typedef typename opt::make_options< default_hook, Options...>::type traits; + + typedef typename traits::probeset_type probeset_type; + typedef typename traits::tag tag; + static unsigned int const store_hash = traits::store_hash; + + typedef node node_type; + + typedef HookType hook_type; + }; + //@endcond + + /// Base hook + /** + \p Options are: + - \p hopscotch_hashset::probeset_type - probeset type. Defaul is \p hopscotch_hashset::list + - \p hopscotch_hashset::store_hash - store hash values in the node or not. Default is 0 (no storing) + - \p opt::tag - a \ref cds_intrusive_hook_tag "tag" + */ + template < typename... Options > + struct base_hook: public hook< opt::base_hook_tag, Options... > + {}; + + /// Member hook + /** + \p MemberOffset defines offset in bytes of \ref node member into your structure. + Use \p offsetof macro to define \p MemberOffset + + \p Options are: + - \p hopscotch_hashset::probeset_type - probeset type. Defaul is \p hopscotch_hashset::list + - \p hopscotch_hashset::store_hash - store hash values in the node or not. Default is 0 (no storing) + - \p opt::tag - a \ref cds_intrusive_hook_tag "tag" + */ + template < size_t MemberOffset, typename... Options > + struct member_hook: public hook< opt::member_hook_tag, Options... > + { + //@cond + static const size_t c_nMemberOffset = MemberOffset; + //@endcond + }; + + /// Traits hook + /** + \p NodeTraits defines type traits for node. + See \ref node_traits for \p NodeTraits interface description + + \p Options are: + - \p hopscotch_hashset::probeset_type - probeset type. Defaul is \p hopscotch_hashset::list + - \p hopscotch_hashset::store_hash - store hash values in the node or not. Default is 0 (no storing) + - \p opt::tag - a \ref cds_intrusive_hook_tag "tag" + */ + template + struct traits_hook: public hook< opt::traits_hook_tag, Options... > + { + //@cond + typedef NodeTraits node_traits; + //@endcond + }; + + /// Internal statistics for \ref striping mutex policy + struct striping_stat { + typedef cds::atomicity::event_counter counter_type; ///< Counter type + + counter_type m_nCellLockCount ; ///< Count of obtaining cell lock + counter_type m_nCellTryLockCount ; ///< Count of cell \p try_lock attempts + counter_type m_nFullLockCount ; ///< Count of obtaining full lock + counter_type m_nResizeLockCount ; ///< Count of obtaining resize lock + counter_type m_nResizeCount ; ///< Count of resize event + + //@cond + void onCellLock() { ++m_nCellLockCount; } + void onCellTryLock() { ++m_nCellTryLockCount; } + void onFullLock() { ++m_nFullLockCount; } + void onResizeLock() { ++m_nResizeLockCount; } + void onResize() { ++m_nResizeCount; } + //@endcond + }; + + /// Dummy internal statistics for \ref striping mutex policy + struct empty_striping_stat { + //@cond + void onCellLock() const {} + void onCellTryLock() const {} + void onFullLock() const {} + void onResizeLock() const {} + void onResize() const {} + //@endcond + }; + + /// Lock striping concurrent access policy + /** + This is one of available opt::mutex_policy option type for HopscotchHashset + + Lock striping is very simple technique. + The hopscotch set consists of the bucket tables and the array of locks. + There is single lock array for each bucket table, at least, the count of bucket table is 2. + Initially, the capacity of lock array and each bucket table is the same. + When set is resized, bucket table capacity will be doubled but lock array will not. + The lock \p i protects each bucket \p j, where j = i mod L , + where \p L - the size of lock array. + + The policy contains an internal array of \p RecursiveLock locks. + + Template arguments: + - \p RecursiveLock - the type of recursive mutex. The default is \p std::recursive_mutex. The mutex type should be default-constructible. + Note that a recursive spin-lock is not suitable for lock striping for performance reason. + - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the + count of lock arrays. Default value is 2. + - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR. + - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref HopscotchHashset + class according to its \p opt::stat option. + */ + template < + class RecursiveLock = std::recursive_mutex, + unsigned int Arity = 2, + class Alloc = CDS_DEFAULT_ALLOCATOR, + class Stat = empty_striping_stat + > + class striping + { + public: + typedef RecursiveLock lock_type ; ///< lock type + typedef Alloc allocator_type ; ///< allocator type + static unsigned int const c_nArity = Arity ; ///< the arity + typedef Stat statistics_type ; ///< Internal statistics type (\ref striping_stat or \ref empty_striping_stat) + + //@cond + typedef striping_stat real_stat; + typedef empty_striping_stat empty_stat; + + template + struct rebind_statistics { + typedef striping other; + }; + //@endcond + + typedef cds::sync::lock_array< lock_type, cds::sync::pow2_select_policy, allocator_type > lock_array_type ; ///< lock array type + + protected: + //@cond + class lock_array: public lock_array_type + { + public: + // placeholder ctor + lock_array(): lock_array_type( typename lock_array_type::select_cell_policy(2)) {} + + // real ctor + lock_array( size_t nCapacity ): lock_array_type( nCapacity, typename lock_array_type::select_cell_policy(nCapacity)) {} + }; + + class scoped_lock: public std::unique_lock< lock_array_type > + { + typedef std::unique_lock< lock_array_type > base_class; + public: + scoped_lock( lock_array& arrLock, size_t nHash ): base_class( arrLock, nHash ) {} + }; + //@endcond + + protected: + //@cond + lock_array m_Locks[c_nArity] ; ///< array of \p lock_array_type + statistics_type m_Stat ; ///< internal statistics + //@endcond + + public: + //@cond + class scoped_cell_lock { + lock_type * m_guard[c_nArity]; + + public: + scoped_cell_lock( striping& policy, size_t const* arrHash ) + { + for ( unsigned int i = 0; i < c_nArity; ++i ) { + m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] ))); + } + policy.m_Stat.onCellLock(); + } + + ~scoped_cell_lock() + { + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_guard[i]->unlock(); + } + }; + + class scoped_cell_trylock + { + typedef typename lock_array_type::lock_type lock_type; + + lock_type * m_guard[c_nArity]; + bool m_bLocked; + + public: + scoped_cell_trylock( striping& policy, size_t const* arrHash ) + { + size_t nCell = policy.m_Locks[0].try_lock( arrHash[0] ); + m_bLocked = nCell != lock_array_type::c_nUnspecifiedCell; + if ( m_bLocked ) { + m_guard[0] = &(policy.m_Locks[0].at(nCell)); + for ( unsigned int i = 1; i < c_nArity; ++i ) { + m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] ))); + } + } + else { + std::fill( m_guard, m_guard + c_nArity, nullptr ); + } + policy.m_Stat.onCellTryLock(); + } + ~scoped_cell_trylock() + { + if ( m_bLocked ) { + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_guard[i]->unlock(); + } + } + + bool locked() const + { + return m_bLocked; + } + }; + + class scoped_full_lock { + std::unique_lock< lock_array_type > m_guard; + public: + scoped_full_lock( striping& policy ) + : m_guard( policy.m_Locks[0] ) + { + policy.m_Stat.onFullLock(); + } + + /// Ctor for scoped_resize_lock - no statistics is incremented + scoped_full_lock( striping& policy, bool ) + : m_guard( policy.m_Locks[0] ) + {} + }; + + class scoped_resize_lock: public scoped_full_lock { + public: + scoped_resize_lock( striping& policy ) + : scoped_full_lock( policy, false ) + { + policy.m_Stat.onResizeLock(); + } + }; + //@endcond + + public: + /// Constructor + striping( + size_t nLockCount ///< The size of lock array. Must be power of two. + ) + { + // Trick: initialize the array of locks + for ( unsigned int i = 0; i < c_nArity; ++i ) { + lock_array * pArr = m_Locks + i; + pArr->lock_array::~lock_array(); + new ( pArr ) lock_array( nLockCount ); + } + } + + /// Returns lock array size + /** + Lock array size is unchanged during \p striping object lifetime + */ + size_t lock_count() const + { + return m_Locks[0].size(); + } + + //@cond + void resize( size_t ) + { + m_Stat.onResize(); + } + //@endcond + + /// Returns the arity of striping mutex policy + constexpr unsigned int arity() const noexcept + { + return c_nArity; + } + + /// Returns internal statistics + statistics_type const& statistics() const + { + return m_Stat; + } + }; + + /// Internal statistics for \ref refinable mutex policy + struct refinable_stat { + typedef cds::atomicity::event_counter counter_type ; ///< Counter type + + counter_type m_nCellLockCount ; ///< Count of obtaining cell lock + counter_type m_nCellLockWaitResizing ; ///< Count of loop iteration to wait for resizing + counter_type m_nCellLockArrayChanged ; ///< Count of event "Lock array has been changed when obtaining cell lock" + counter_type m_nCellLockFailed ; ///< Count of event "Cell lock failed because of the array is owned by other thread" + + counter_type m_nSecondCellLockCount ; ///< Count of obtaining cell lock when another cell is already locked + counter_type m_nSecondCellLockFailed ; ///< Count of unsuccess obtaining cell lock when another cell is already locked + + counter_type m_nFullLockCount ; ///< Count of obtaining full lock + counter_type m_nFullLockIter ; ///< Count of unsuccessfull iteration to obtain full lock + + counter_type m_nResizeLockCount ; ///< Count of obtaining resize lock + counter_type m_nResizeLockIter ; ///< Count of unsuccessfull iteration to obtain resize lock + counter_type m_nResizeLockArrayChanged; ///< Count of event "Lock array has been changed when obtaining resize lock" + counter_type m_nResizeCount ; ///< Count of resize event + + //@cond + void onCellLock() { ++m_nCellLockCount; } + void onCellWaitResizing() { ++m_nCellLockWaitResizing; } + void onCellArrayChanged() { ++m_nCellLockArrayChanged; } + void onCellLockFailed() { ++m_nCellLockFailed; } + void onSecondCellLock() { ++m_nSecondCellLockCount; } + void onSecondCellLockFailed() { ++m_nSecondCellLockFailed; } + void onFullLock() { ++m_nFullLockCount; } + void onFullLockIter() { ++m_nFullLockIter; } + void onResizeLock() { ++m_nResizeLockCount; } + void onResizeLockIter() { ++m_nResizeLockIter; } + void onResizeLockArrayChanged() { ++m_nResizeLockArrayChanged; } + void onResize() { ++m_nResizeCount; } + //@endcond + }; + + /// Dummy internal statistics for \ref refinable mutex policy + struct empty_refinable_stat { + //@cond + void onCellLock() const {} + void onCellWaitResizing() const {} + void onCellArrayChanged() const {} + void onCellLockFailed() const {} + void onSecondCellLock() const {} + void onSecondCellLockFailed() const {} + void onFullLock() const {} + void onFullLockIter() const {} + void onResizeLock() const {} + void onResizeLockIter() const {} + void onResizeLockArrayChanged() const {} + void onResize() const {} + //@endcond + }; + + /// Refinable concurrent access policy + /** + This is one of available \p opt::mutex_policy option type for \p HopscotchHashset + + Refining is like a striping technique (see \p hopscotch_hashset::striping) + but it allows growing the size of lock array when resizing the hash table. + So, the sizes of hash table and lock array are equal. + + Template arguments: + - \p RecursiveLock - the type of mutex. Reentrant (recursive) mutex is required. + The default is \p std::recursive_mutex. The mutex type should be default-constructible. + - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the + count of lock arrays. Default value is 2. + - \p BackOff - back-off strategy. Default is \p cds::backoff::Default + - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR. + - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref HopscotchHashset + class according to its \p opt::stat option. + */ + template < + class RecursiveLock = std::recursive_mutex, + unsigned int Arity = 2, + typename BackOff = cds::backoff::Default, + class Alloc = CDS_DEFAULT_ALLOCATOR, + class Stat = empty_refinable_stat + > + class refinable + { + public: + typedef RecursiveLock lock_type ; ///< lock type + typedef Alloc allocator_type ; ///< allocator type + typedef BackOff back_off ; ///< back-off strategy + typedef Stat statistics_type ; ///< internal statistics type + static unsigned int const c_nArity = Arity; ///< the arity + + //@cond + typedef refinable_stat real_stat; + typedef empty_refinable_stat empty_stat; + + template + struct rebind_statistics { + typedef refinable< lock_type, c_nArity, back_off, allocator_type, Stat2> other; + }; + //@endcond + + protected: + //@cond + typedef cds::sync::trivial_select_policy lock_selection_policy; + + class lock_array_type + : public cds::sync::lock_array< lock_type, lock_selection_policy, allocator_type > + , public std::enable_shared_from_this< lock_array_type > + { + typedef cds::sync::lock_array< lock_type, lock_selection_policy, allocator_type > lock_array_base; + public: + lock_array_type( size_t nCapacity ) + : lock_array_base( nCapacity ) + {} + }; + typedef std::shared_ptr< lock_array_type > lock_array_ptr; + typedef cds::details::Allocator< lock_array_type, allocator_type > lock_array_allocator; + + typedef unsigned long long owner_t; + typedef cds::OS::ThreadId threadId_t; + + typedef cds::sync::spin spinlock_type; + typedef std::unique_lock< spinlock_type > scoped_spinlock; + //@endcond + + protected: + //@cond + static owner_t const c_nOwnerMask = (((owner_t) 1) << (sizeof(owner_t) * 8 - 1)) - 1; + + atomics::atomic< owner_t > m_Owner ; ///< owner mark (thread id + boolean flag) + atomics::atomic m_nCapacity ; ///< lock array capacity + lock_array_ptr m_arrLocks[ c_nArity ] ; ///< Lock array. The capacity of array is specified in constructor. + spinlock_type m_access ; ///< access to m_arrLocks + statistics_type m_Stat ; ///< internal statistics + //@endcond + + protected: + //@cond + struct lock_array_disposer { + void operator()( lock_array_type * pArr ) + { + // Seems, there is a false positive in std::shared_ptr deallocation in uninstrumented libc++ + // see, for example, https://groups.google.com/forum/#!topic/thread-sanitizer/eHu4dE_z7Cc + // https://reviews.llvm.org/D21609 + CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN; + lock_array_allocator().Delete( pArr ); + CDS_TSAN_ANNOTATE_IGNORE_WRITES_END; + } + }; + + lock_array_ptr create_lock_array( size_t nCapacity ) + { + return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer()); + } + + void acquire( size_t const * arrHash, lock_array_ptr * pLockArr, lock_type ** parrLock ) + { + owner_t me = (owner_t) cds::OS::get_current_thread_id(); + owner_t who; + size_t cur_capacity; + + back_off bkoff; + while ( true ) { + + { + scoped_spinlock sl(m_access); + for ( unsigned int i = 0; i < c_nArity; ++i ) + pLockArr[i] = m_arrLocks[i]; + cur_capacity = m_nCapacity.load( atomics::memory_order_acquire ); + } + + // wait while resizing + while ( true ) { + who = m_Owner.load( atomics::memory_order_acquire ); + if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask)) + break; + bkoff(); + m_Stat.onCellWaitResizing(); + } + + if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) { + + size_t const nMask = pLockArr[0]->size() - 1; + assert( cds::beans::is_power2( nMask + 1 )); + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + parrLock[i] = &( pLockArr[i]->at( arrHash[i] & nMask )); + parrLock[i]->lock(); + } + + who = m_Owner.load( atomics::memory_order_acquire ); + if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask)) && cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) { + m_Stat.onCellLock(); + return; + } + + for ( unsigned int i = 0; i < c_nArity; ++i ) + parrLock[i]->unlock(); + + m_Stat.onCellLockFailed(); + } + else + m_Stat.onCellArrayChanged(); + + // clears pLockArr can lead to calling dtor for each item of pLockArr[i] that may be a heavy-weighted operation + // (each pLockArr[i] is a shared pointer to array of a ton of mutexes) + // It is better to do this before the next loop iteration where we will use spin-locked assignment to pLockArr + // However, destructing a lot of mutexes under spin-lock is a bad solution + for ( unsigned int i = 0; i < c_nArity; ++i ) + pLockArr[i].reset(); + } + } + + bool try_second_acquire( size_t const * arrHash, lock_type ** parrLock ) + { + // It is assumed that the current thread already has a lock + // and requires a second lock for other hash + + size_t const nMask = m_nCapacity.load(atomics::memory_order_acquire) - 1; + size_t nCell = m_arrLocks[0]->try_lock( arrHash[0] & nMask); + if ( nCell == lock_array_type::c_nUnspecifiedCell ) { + m_Stat.onSecondCellLockFailed(); + return false; + } + parrLock[0] = &(m_arrLocks[0]->at(nCell)); + + for ( unsigned int i = 1; i < c_nArity; ++i ) { + parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask))); + } + + m_Stat.onSecondCellLock(); + return true; + } + + void acquire_all() + { + owner_t me = (owner_t) cds::OS::get_current_thread_id(); + + back_off bkoff; + while ( true ) { + owner_t ownNull = 0; + if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) { + m_arrLocks[0]->lock_all(); + + m_Stat.onFullLock(); + return; + } + bkoff(); + m_Stat.onFullLockIter(); + } + } + + void release_all() + { + m_arrLocks[0]->unlock_all(); + m_Owner.store( 0, atomics::memory_order_release ); + } + + void acquire_resize( lock_array_ptr * pOldLocks ) + { + owner_t me = (owner_t) cds::OS::get_current_thread_id(); + size_t cur_capacity; + + while ( true ) { + { + scoped_spinlock sl(m_access); + for ( unsigned int i = 0; i < c_nArity; ++i ) + pOldLocks[i] = m_arrLocks[i]; + cur_capacity = m_nCapacity.load( atomics::memory_order_acquire ); + } + + // global lock + owner_t ownNull = 0; + if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) { + if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) { + pOldLocks[0]->lock_all(); + m_Stat.onResizeLock(); + return; + } + + m_Owner.store( 0, atomics::memory_order_release ); + m_Stat.onResizeLockArrayChanged(); + } + else + m_Stat.onResizeLockIter(); + + // clears pOldLocks can lead to calling dtor for each item of pOldLocks[i] that may be a heavy-weighted operation + // (each pOldLocks[i] is a shared pointer to array of a ton of mutexes) + // It is better to do this before the next loop iteration where we will use spin-locked assignment to pOldLocks + // However, destructing a lot of mutexes under spin-lock is a bad solution + for ( unsigned int i = 0; i < c_nArity; ++i ) + pOldLocks[i].reset(); + } + } + + void release_resize( lock_array_ptr * pOldLocks ) + { + m_Owner.store( 0, atomics::memory_order_release ); + pOldLocks[0]->unlock_all(); + } + //@endcond + + public: + //@cond + class scoped_cell_lock { + lock_type * m_arrLock[ c_nArity ]; + lock_array_ptr m_arrLockArr[ c_nArity ]; + + public: + scoped_cell_lock( refinable& policy, size_t const* arrHash ) + { + policy.acquire( arrHash, m_arrLockArr, m_arrLock ); + } + + ~scoped_cell_lock() + { + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_arrLock[i]->unlock(); + } + }; + + class scoped_cell_trylock { + lock_type * m_arrLock[ c_nArity ]; + bool m_bLocked; + + public: + scoped_cell_trylock( refinable& policy, size_t const* arrHash ) + { + m_bLocked = policy.try_second_acquire( arrHash, m_arrLock ); + } + + ~scoped_cell_trylock() + { + if ( m_bLocked ) { + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_arrLock[i]->unlock(); + } + } + + bool locked() const + { + return m_bLocked; + } + }; + + class scoped_full_lock { + refinable& m_policy; + public: + scoped_full_lock( refinable& policy ) + : m_policy( policy ) + { + policy.acquire_all(); + } + ~scoped_full_lock() + { + m_policy.release_all(); + } + }; + + class scoped_resize_lock + { + refinable& m_policy; + lock_array_ptr m_arrLocks[ c_nArity ]; + public: + scoped_resize_lock( refinable& policy ) + : m_policy(policy) + { + policy.acquire_resize( m_arrLocks ); + } + ~scoped_resize_lock() + { + m_policy.release_resize( m_arrLocks ); + } + }; + //@endcond + + public: + /// Constructor + refinable( + size_t nLockCount ///< The size of lock array. Must be power of two. + ) : m_Owner(0) + , m_nCapacity( nLockCount ) + { + assert( cds::beans::is_power2( nLockCount )); + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_arrLocks[i] = create_lock_array( nLockCount ); + } + + //@cond + void resize( size_t nCapacity ) + { + lock_array_ptr pNew[ c_nArity ]; + for ( unsigned int i = 0; i < c_nArity; ++i ) + pNew[i] = create_lock_array( nCapacity ); + + { + scoped_spinlock sl(m_access); + m_nCapacity.store( nCapacity, atomics::memory_order_release ); + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_arrLocks[i] = pNew[i]; + } + + m_Stat.onResize(); + } + //@endcond + + /// Returns lock array size + /** + Lock array size is not a constant for \p refinable policy and can be changed when the set is resized. + */ + size_t lock_count() const + { + return m_nCapacity.load(atomics::memory_order_relaxed); + } + + /// Returns the arity of \p refinable mutex policy + constexpr unsigned int arity() const noexcept + { + return c_nArity; + } + + /// Returns internal statistics + statistics_type const& statistics() const + { + return m_Stat; + } + }; + + /// \p HopscotchHashset internal statistics + struct stat { + typedef cds::atomicity::event_counter counter_type ; ///< Counter type + + counter_type m_nRelocateCallCount ; ///< Count of \p relocate() function call + counter_type m_nRelocateRoundCount ; ///< Count of attempts to relocate items + counter_type m_nFalseRelocateCount ; ///< Count of unneeded attempts of \p relocate call + counter_type m_nSuccessRelocateCount ; ///< Count of successful item relocating + counter_type m_nRelocateAboveThresholdCount; ///< Count of item relocating above probeset threshold + counter_type m_nFailedRelocateCount ; ///< Count of failed relocation attemp (when all probeset is full) + + counter_type m_nResizeCallCount ; ///< Count of \p resize() function call + counter_type m_nFalseResizeCount ; ///< Count of false \p resize() function call (when other thread has been resized the set) + counter_type m_nResizeSuccessNodeMove; ///< Count of successful node moving when resizing + counter_type m_nResizeRelocateCall ; ///< Count of \p relocate() function call from \p resize function + + counter_type m_nInsertSuccess ; ///< Count of successful \p insert() function call + counter_type m_nInsertFailed ; ///< Count of failed \p insert() function call + counter_type m_nInsertResizeCount ; ///< Count of \p resize() function call from \p insert() + counter_type m_nInsertRelocateCount ; ///< Count of \p relocate() function call from \p insert() + counter_type m_nInsertRelocateFault ; ///< Count of failed \p relocate() function call from \p insert() + + counter_type m_nUpdateExistCount ; ///< Count of call \p update() function for existing node + counter_type m_nUpdateSuccessCount ; ///< Count of successful \p insert() function call for new node + counter_type m_nUpdateResizeCount ; ///< Count of \p resize() function call from \p update() + counter_type m_nUpdateRelocateCount ; ///< Count of \p relocate() function call from \p update() + counter_type m_nUpdateRelocateFault ; ///< Count of failed \p relocate() function call from \p update() + + counter_type m_nUnlinkSuccess ; ///< Count of success \p unlink() function call + counter_type m_nUnlinkFailed ; ///< Count of failed \p unlink() function call + + counter_type m_nEraseSuccess ; ///< Count of success \p erase() function call + counter_type m_nEraseFailed ; ///< Count of failed \p erase() function call + + counter_type m_nFindSuccess ; ///< Count of success \p find() function call + counter_type m_nFindFailed ; ///< Count of failed \p find() function call + + counter_type m_nFindEqualSuccess ; ///< Count of success \p find_equal() function call + counter_type m_nFindEqualFailed ; ///< Count of failed \p find_equal() function call + + counter_type m_nFindWithSuccess ; ///< Count of success \p find_with() function call + counter_type m_nFindWithFailed ; ///< Count of failed \p find_with() function call + + //@cond + void onRelocateCall() { ++m_nRelocateCallCount; } + void onRelocateRound() { ++m_nRelocateRoundCount; } + void onFalseRelocateRound() { ++m_nFalseRelocateCount; } + void onSuccessRelocateRound(){ ++m_nSuccessRelocateCount; } + void onRelocateAboveThresholdRound() { ++m_nRelocateAboveThresholdCount; } + void onFailedRelocate() { ++m_nFailedRelocateCount; } + + void onResizeCall() { ++m_nResizeCallCount; } + void onFalseResizeCall() { ++m_nFalseResizeCount; } + void onResizeSuccessMove() { ++m_nResizeSuccessNodeMove; } + void onResizeRelocateCall() { ++m_nResizeRelocateCall; } + + void onInsertSuccess() { ++m_nInsertSuccess; } + void onInsertFailed() { ++m_nInsertFailed; } + void onInsertResize() { ++m_nInsertResizeCount; } + void onInsertRelocate() { ++m_nInsertRelocateCount; } + void onInsertRelocateFault() { ++m_nInsertRelocateFault; } + + void onUpdateExist() { ++m_nUpdateExistCount; } + void onUpdateSuccess() { ++m_nUpdateSuccessCount; } + void onUpdateResize() { ++m_nUpdateResizeCount; } + void onUpdateRelocate() { ++m_nUpdateRelocateCount; } + void onUpdateRelocateFault() { ++m_nUpdateRelocateFault; } + + void onUnlinkSuccess() { ++m_nUnlinkSuccess; } + void onUnlinkFailed() { ++m_nUnlinkFailed; } + + void onEraseSuccess() { ++m_nEraseSuccess; } + void onEraseFailed() { ++m_nEraseFailed; } + + void onFindSuccess() { ++m_nFindSuccess; } + void onFindFailed() { ++m_nFindFailed; } + + void onFindWithSuccess() { ++m_nFindWithSuccess; } + void onFindWithFailed() { ++m_nFindWithFailed; } + //@endcond + }; + + /// HopscotchHashset empty internal statistics + struct empty_stat { + //@cond + void onRelocateCall() const {} + void onRelocateRound() const {} + void onFalseRelocateRound() const {} + void onSuccessRelocateRound()const {} + void onRelocateAboveThresholdRound() const {} + void onFailedRelocate() const {} + + void onResizeCall() const {} + void onFalseResizeCall() const {} + void onResizeSuccessMove() const {} + void onResizeRelocateCall() const {} + + void onInsertSuccess() const {} + void onInsertFailed() const {} + void onInsertResize() const {} + void onInsertRelocate() const {} + void onInsertRelocateFault() const {} + + void onUpdateExist() const {} + void onUpdateSuccess() const {} + void onUpdateResize() const {} + void onUpdateRelocate() const {} + void onUpdateRelocateFault() const {} + + void onUnlinkSuccess() const {} + void onUnlinkFailed() const {} + + void onEraseSuccess() const {} + void onEraseFailed() const {} + + void onFindSuccess() const {} + void onFindFailed() const {} + + void onFindWithSuccess() const {} + void onFindWithFailed() const {} + //@endcond + }; + + /// Type traits for HopscotchHashset class + struct traits + { + /// Hook used + /** + Possible values are: hopscotch_hashset::base_hook, hopscotch_hashset::member_hook, hopscotch_hashset::traits_hook. + */ + typedef base_hook<> hook; + + /// Hash functors tuple + /** + This is mandatory type and has no predefined one. + + At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are defined as std::tuple< H1, H2, ... Hn > : + \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode + The number of hash functors specifies the number \p k - the count of hash tables in hopscotch hashing. + + To specify hash tuple in traits you should use \p cds::opt::hash_tuple: + \code + struct my_traits: public cds::intrusive::hopscotch_hashset::traits { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + }; + \endcode + */ + typedef cds::opt::none hash; + + /// Concurrent access policy + /** + Available opt::mutex_policy types: + - \p hopscotch_hashset::striping - simple, but the lock array is not resizable + - \p hopscotch_hashset::refinable - resizable lock array, but more complex access to set data. + + Default is \p hopscotch_hashset::striping. + */ + typedef hopscotch_hashset::striping<> mutex_policy; + + /// Key equality functor + /** + Default is std::equal_to + */ + typedef opt::none equal_to; + + /// Key comparing functor + /** + No default functor is provided. If the option is not specified, the \p less is used. + */ + typedef opt::none compare; + + /// specifies binary predicate used for key comparison. + /** + Default is \p std::less. + */ + typedef opt::none less; + + /// Item counter + /** + The type for item counting feature. + Default is \p cds::atomicity::item_counter + + Only atomic item counter type is allowed. + */ + typedef atomicity::item_counter item_counter; + + /// Allocator type + /** + The allocator type for allocating bucket tables. + */ + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Disposer + /** + The disposer functor is used in \p HopscotchHashset::clear() member function + to free set's node. + */ + typedef intrusive::opt::v::empty_disposer disposer; + + /// Internal statistics. Available statistics: \p hopscotch_hashset::stat, \p hopscotch_hashset::empty_stat + typedef empty_stat stat; + }; + + /// Metafunction converting option list to \p HopscotchHashset traits + /** + Template argument list \p Options... are: + - \p intrusive::opt::hook - hook used. Possible values are: \p hopscotch_hashset::base_hook, \p hopscotch_hashset::member_hook, + \p hopscotch_hashset::traits_hook. + If the option is not specified, %hopscotch_hashset::base_hook<> is used. + - \p opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor + should be orthogonal (different): for each i,j: i != j => h[i](x) != h[j](x) . + The hash functors are passed as std::tuple< H1, H2, ... Hn > . The number of hash functors specifies + the number \p k - the count of hash tables in hopscotch hashing. + - \p opt::mutex_policy - concurrent access policy. + Available policies: \p hopscotch_hashset::striping, \p hopscotch_hashset::refinable. + Default is \p %hopscotch_hashset::striping. + - \p opt::equal_to - key equality functor like \p std::equal_to. + If this functor is defined then the probe-set will be unordered. + If \p %opt::compare or \p %opt::less option is specified too, then the probe-set will be ordered + and \p %opt::equal_to will be ignored. + - \p opt::compare - key comparison functor. No default functor is provided. + If the option is not specified, the \p %opt::less is used. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less. + If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered. + - \p opt::item_counter - the type of item counting feature. Default is \p atomicity::item_counter + The item counter should be atomic. + - \p opt::allocator - the allocator type using for allocating bucket tables. + Default is \ref CDS_DEFAULT_ALLOCATOR + - \p intrusive::opt::disposer - the disposer type used in \p clear() member function for + freeing nodes. Default is \p intrusive::opt::v::empty_disposer + - \p opt::stat - internal statistics. Possibly types: \p hopscotch_hashset::stat, \p hopscotch_hashset::empty_stat. + Default is \p %hopscotch_hashset::empty_stat + + The probe set traits \p hopscotch_hashset::probeset_type and \p hopscotch_hashset::store_hash are taken from \p node type + specified by \p opt::hook option. + */ + template + struct make_traits { + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< hopscotch_hashset::traits, Options... >::type + ,Options... + >::type type ; ///< Result of metafunction + }; + + //@cond + namespace details { + template + class bucket_entry; + + template + class bucket_entry + { + public: + typedef Node node_type; + typedef hopscotch_hashset::list_probeset_class probeset_class; + typedef hopscotch_hashset::list probeset_type; + + protected: + node_type * pHead; + unsigned int nSize; + + public: + class iterator + { + node_type * pNode; + friend class bucket_entry; + + public: + iterator() + : pNode( nullptr ) + {} + iterator( node_type * p ) + : pNode( p ) + {} + iterator( iterator const& it) + : pNode( it.pNode ) + {} + + iterator& operator=( iterator const& it ) + { + pNode = it.pNode; + return *this; + } + + iterator& operator=( node_type * p ) + { + pNode = p; + return *this; + } + + node_type * operator->() + { + return pNode; + } + node_type& operator*() + { + assert( pNode != nullptr ); + return *pNode; + } + + // preinc + iterator& operator ++() + { + if ( pNode ) + pNode = pNode->m_pNext; + return *this; + } + + bool operator==(iterator const& it ) const + { + return pNode == it.pNode; + } + bool operator!=(iterator const& it ) const + { + return !( *this == it ); + } + }; + + public: + bucket_entry() + : pHead( nullptr ) + , nSize(0) + { + static_assert(( std::is_same::value ), "Incompatible node type" ); + } + + iterator begin() + { + return iterator(pHead); + } + iterator end() + { + return iterator(); + } + + void insert_after( iterator it, node_type * p ) + { + node_type * pPrev = it.pNode; + if ( pPrev ) { + p->m_pNext = pPrev->m_pNext; + pPrev->m_pNext = p; + } + else { + // insert as head + p->m_pNext = pHead; + pHead = p; + } + ++nSize; + } + + void remove( iterator itPrev, iterator itWhat ) + { + node_type * pPrev = itPrev.pNode; + node_type * pWhat = itWhat.pNode; + assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat)); + + if ( pPrev ) + pPrev->m_pNext = pWhat->m_pNext; + else { + assert( pWhat == pHead ); + pHead = pHead->m_pNext; + } + pWhat->clear(); + --nSize; + } + + void clear() + { + node_type * pNext; + for ( node_type * pNode = pHead; pNode; pNode = pNext ) { + pNext = pNode->m_pNext; + pNode->clear(); + } + + nSize = 0; + pHead = nullptr; + } + + template + void clear( Disposer disp ) + { + node_type * pNext; + for ( node_type * pNode = pHead; pNode; pNode = pNext ) { + pNext = pNode->m_pNext; + pNode->clear(); + disp( pNode ); + } + + nSize = 0; + pHead = nullptr; + } + + unsigned int size() const + { + return nSize; + } + }; + + template + class bucket_entry> + { + public: + typedef Node node_type; + typedef hopscotch_hashset::vector_probeset_class probeset_class; + typedef hopscotch_hashset::vector probeset_type; + + static unsigned int const c_nCapacity = probeset_type::c_nCapacity; + + protected: + node_type * m_arrNode[c_nCapacity]; + unsigned int m_nSize; + + void shift_up( unsigned int nFrom ) + { + assert( m_nSize < c_nCapacity ); + + if ( nFrom < m_nSize ) + std::copy_backward( m_arrNode + nFrom, m_arrNode + m_nSize, m_arrNode + m_nSize + 1 ); + } + + void shift_down( node_type ** pFrom ) + { + assert( m_arrNode <= pFrom && pFrom < m_arrNode + m_nSize); + std::copy( pFrom + 1, m_arrNode + m_nSize, pFrom ); + } + public: + class iterator + { + node_type ** pArr; + friend class bucket_entry; + + public: + iterator() + : pArr( nullptr ) + {} + iterator( node_type ** p ) + : pArr(p) + {} + iterator( iterator const& it) + : pArr( it.pArr ) + {} + + iterator& operator=( iterator const& it ) + { + pArr = it.pArr; + return *this; + } + + node_type * operator->() + { + assert( pArr != nullptr ); + return *pArr; + } + node_type& operator*() + { + assert( pArr != nullptr ); + assert( *pArr != nullptr ); + return *(*pArr); + } + + // preinc + iterator& operator ++() + { + ++pArr; + return *this; + } + + bool operator==(iterator const& it ) const + { + return pArr == it.pArr; + } + bool operator!=(iterator const& it ) const + { + return !( *this == it ); + } + }; + + public: + bucket_entry() + : m_nSize(0) + { + memset( m_arrNode, 0, sizeof(m_arrNode)); + static_assert(( std::is_same::value ), "Incompatible node type" ); + } + + iterator begin() + { + return iterator(m_arrNode); + } + iterator end() + { + return iterator(m_arrNode + size()); + } + + void insert_after( iterator it, node_type * p ) + { + assert( m_nSize < c_nCapacity ); + assert( !it.pArr || (m_arrNode <= it.pArr && it.pArr <= m_arrNode + m_nSize)); + + if ( it.pArr ) { + shift_up( static_cast(it.pArr - m_arrNode) + 1 ); + it.pArr[1] = p; + } + else { + shift_up(0); + m_arrNode[0] = p; + } + ++m_nSize; + } + + void remove( iterator /*itPrev*/, iterator itWhat ) + { + itWhat->clear(); + shift_down( itWhat.pArr ); + --m_nSize; + } + + void clear() + { + m_nSize = 0; + } + + template + void clear( Disposer disp ) + { + for ( unsigned int i = 0; i < m_nSize; ++i ) { + disp( m_arrNode[i] ); + } + m_nSize = 0; + } + + unsigned int size() const + { + return m_nSize; + } + }; + + template + struct hash_ops { + static void store( Node * pNode, size_t const* pHashes ) + { + memcpy( pNode->m_arrHash, pHashes, sizeof(pHashes[0]) * ArraySize ); + } + static bool equal_to( Node& node, unsigned int nTable, size_t nHash ) + { + return node.m_arrHash[nTable] == nHash; + } + }; + template + struct hash_ops + { + static void store( Node * /*pNode*/, size_t * /*pHashes*/ ) + {} + static bool equal_to( Node& /*node*/, unsigned int /*nTable*/, size_t /*nHash*/ ) + { + return true; + } + }; + + template + struct contains; + + template + struct contains + { + template + static bool find( BucketEntry& probeset, Position& pos, unsigned int /*nTable*/, size_t /*nHash*/, Q const& val, Compare cmp ) + { + // Ordered version + typedef typename BucketEntry::iterator bucket_iterator; + + bucket_iterator itPrev; + + for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) { + int cmpRes = cmp( *NodeTraits::to_value_ptr(*it), val ); + if ( cmpRes >= 0 ) { + pos.itFound = it; + pos.itPrev = itPrev; + return cmpRes == 0; + } + + itPrev = it; + } + + pos.itPrev = itPrev; + pos.itFound = probeset.end(); + return false; + } + }; + + template + struct contains + { + template + static bool find( BucketEntry& probeset, Position& pos, unsigned int nTable, size_t nHash, Q const& val, EqualTo eq ) + { + // Unordered version + typedef typename BucketEntry::iterator bucket_iterator; + typedef typename BucketEntry::node_type node_type; + + bucket_iterator itPrev; + + for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) { + if ( hash_ops::equal_to( *it, nTable, nHash ) && eq( *NodeTraits::to_value_ptr(*it), val )) { + pos.itFound = it; + pos.itPrev = itPrev; + return true; + } + itPrev = it; + } + + pos.itPrev = itPrev; + pos.itFound = probeset.end(); + return false; + } + }; + + } // namespace details + //@endcond + + } // namespace hopscotch_hashset + + /// Hopscotch hash set + /** @ingroup cds_intrusive_map + + Source + - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Hopscotch Hashing. Technical report" + - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming" + + About Hopscotch hashing + + [From "The Art of Multiprocessor Programming"] + Hopscotch hashing is a hashing algorithm in which a newly added item displaces any earlier item + occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set of size + N = 2k we use a two-entry array of tables, and two independent hash functions, + h0, h1: KeyRange -> 0,...,k-1 + mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set, + find(x) tests whether either table[0][h0(x)] or table[1][h1(x)] is + equal to \p x. Similarly, erase(x)checks whether \p x is in either table[0][h0(x)] + or table[1][h1(x)], ad removes it if found. + + The insert(x) successively "kicks out" conflicting items until every key has a slot. + To add \p x, the method swaps \p x with \p y, the current occupant of table[0][h0(x)]. + If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y + for the current occupant of table[1][h1(y)] in the same way. As before, if the prior value + was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables) + until it finds an empty slot. We might not find an empty slot, either because the table is full, + or because the sequence of displacement forms a cycle. We therefore need an upper limit on the + number of successive displacements we are willing to undertake. When this limit is exceeded, + we resize the hash table, choose new hash functions and start over. + + For concurrent hopscotch hashing, rather than organizing the set as a two-dimensional table of + items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set + of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm + tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set + holds no more than THRESHOLD < PROBE_SET items. While method calls are in-flight, a probe + set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items. + + In current implementation, a probe set can be defined either as a (single-linked) list + or as a fixed-sized vector, optionally ordered. + + In description above two-table hopscotch hashing (k = 2) has been considered. + We can generalize this approach for k >= 2 when we have \p k hash functions + h[0], ... h[k-1] and \p k tables table[0], ... table[k-1]. + + The search in probe set is linear, the complexity is O(PROBE_SET) . + The probe set may be ordered or not. Ordered probe set can be more efficient since + the average search complexity is O(PROBE_SET/2). + However, the overhead of sorting can eliminate a gain of ordered search. + + The probe set is ordered if \p compare or \p less is specified in \p Traits template + parameter. Otherwise, the probe set is unordered and \p Traits should provide + \p equal_to predicate. + + The \p cds::intrusive::hopscotch_hashset namespace contains \p %HopscotchHashset-related declarations. + + Template arguments: + - \p T - the type stored in the set. The type must be based on \p hopscotch_hashset::node (for \p hopscotch_hashset::base_hook) + or it must have a member of type %hopscotch_hashset::node (for \p hopscotch_hashset::member_hook), + or it must be convertible to \p %hopscotch_hashset::node (for \p hopscotch_hashset::traits_hook) + - \p Traits - type traits, default is \p hopscotch_hashset::traits. It is possible to declare option-based + set with \p hopscotch_hashset::make_traits metafunction result as \p Traits template argument. + + How to use + + You should incorporate \p hopscotch_hashset::node into your struct \p T and provide + appropriate \p hopscotch_hashset::traits::hook in your \p Traits template parameters. + Usually, for \p Traits you define a struct based on \p hopscotch_hashset::traits. + + Example for base hook and list-based probe-set: + \code + #include + + // Data stored in hopscotch set + // We use list as probe-set container and store hash values in the node + // (since we use two hash functions we should store 2 hash values per node) + struct my_data: public cds::intrusive::hopscotch_hashset::node< cds::intrusive::hopscotch_hashset::list, 2 > + { + // key field + std::string strKey; + + // other data + // ... + }; + + // Provide equal_to functor for my_data since we will use unordered probe-set + struct my_data_equal_to { + bool operator()( const my_data& d1, const my_data& d2 ) const + { + return d1.strKey.compare( d2.strKey ) == 0; + } + + bool operator()( const my_data& d, const std::string& s ) const + { + return d.strKey.compare(s) == 0; + } + + bool operator()( const std::string& s, const my_data& d ) const + { + return s.compare( d.strKey ) == 0; + } + }; + + // Provide two hash functor for my_data + struct hash1 { + size_t operator()(std::string const& s) const + { + return cds::opt::v::hash( s ); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + struct hash2: private hash1 { + size_t operator()(std::string const& s) const + { + size_t h = ~( hash1::operator()(s)); + return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + // Declare type traits + struct my_traits: public cds::intrusive::hopscotch_hashset::traits + { + typedef cds::intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< my_data::probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< my_data::hash_array_size > + > hook; + typedef my_data_equa_to equal_to; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + }; + + // Declare HopscotchHashset type + typedef cds::intrusive::HopscotchHashset< my_data, my_traits > my_hopscotch_hashset; + + // Equal option-based declaration + typedef cds::intrusive::HopscotchHashset< my_data, + cds::intrusive::hopscotch_hashset::make_traits< + cds::intrusive::opt::hook< cds::intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< my_data::probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< my_data::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::equal_to< my_data_equal_to > + >::type + > opt_hopscotch_hashset_set; + \endcode + + If we provide \p compare function instead of \p equal_to for \p my_data + we get as a result a hopscotch set with ordered probe set that may improve + performance. + Example for base hook and ordered vector-based probe-set: + + \code + #include + + // Data stored in hopscotch set + // We use a vector of capacity 4 as probe-set container and store hash values in the node + // (since we use two hash functions we should store 2 hash values per node) + struct my_data: public cds::intrusive::hopscotch_hashset::node< cds::intrusive::hopscotch_hashset::vector<4>, 2 > + { + // key field + std::string strKey; + + // other data + // ... + }; + + // Provide compare functor for my_data since we want to use ordered probe-set + struct my_data_compare { + int operator()( const my_data& d1, const my_data& d2 ) const + { + return d1.strKey.compare( d2.strKey ); + } + + int operator()( const my_data& d, const std::string& s ) const + { + return d.strKey.compare(s); + } + + int operator()( const std::string& s, const my_data& d ) const + { + return s.compare( d.strKey ); + } + }; + + // Provide two hash functor for my_data + struct hash1 { + size_t operator()(std::string const& s) const + { + return cds::opt::v::hash( s ); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + struct hash2: private hash1 { + size_t operator()(std::string const& s) const + { + size_t h = ~( hash1::operator()(s)); + return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); + } + size_t operator()( my_data const& d ) const + { + return (*this)( d.strKey ); + } + }; + + // Declare type traits + struct my_traits: public cds::intrusive::hopscotch_hashset::traits + { + typedef cds::intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< my_data::probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< my_data::hash_array_size > + > hook; + typedef my_data_compare compare; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + }; + + // Declare HopscotchHashset type + typedef cds::intrusive::HopscotchHashset< my_data, my_traits > my_hopscotch_hashset; + + // Equal option-based declaration + typedef cds::intrusive::HopscotchHashset< my_data, + cds::intrusive::hopscotch_hashset::make_traits< + cds::intrusive::opt::hook< cds::intrusive::hopscotch_hashset::base_hook< + cds::intrusive::hopscotch_hashset::probeset_type< my_data::probeset_type > + ,cds::intrusive::hopscotch_hashset::store_hash< my_data::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< my_data_compare > + >::type + > opt_hopscotch_hashset_set; + \endcode + + */ + template + class HopscotchHashset + { + public: + typedef T value_type; ///< The value type stored in the set + typedef Traits traits; ///< Set traits + + typedef typename traits::hook hook; ///< hook type + typedef typename hook::node_type node_type; ///< node type + typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits + + typedef typename traits::hash hash; ///< hash functor tuple wrapped for internal use + typedef typename hash::hash_tuple_type hash_tuple_type; ///< Type of hash tuple + + typedef typename traits::stat stat; ///< internal statistics type + + typedef typename traits::mutex_policy original_mutex_policy; ///< Concurrent access policy, see \p hopscotch_hashset::traits::mutex_policy + + //@cond + typedef typename original_mutex_policy::template rebind_statistics< + typename std::conditional< + std::is_same< stat, hopscotch_hashset::empty_stat >::value + ,typename original_mutex_policy::empty_stat + ,typename original_mutex_policy::real_stat + >::type + >::other mutex_policy; + //@endcond + + /// Probe set should be ordered or not + /** + If \p Traits specifies \p cmpare or \p less functor then the set is ordered. + Otherwise, it is unordered and \p Traits should provide \p equal_to functor. + */ + static bool const c_isSorted = !( std::is_same< typename traits::compare, opt::none >::value + && std::is_same< typename traits::less, opt::none >::value ); + static size_t const c_nArity = hash::size ; ///< the arity of hopscotch_hashset hashing: the number of hash functors provided; minimum 2. + + /// Key equality functor; used only for unordered probe-set + typedef typename opt::details::make_equal_to< value_type, traits, !c_isSorted>::type key_equal_to; + + /// key comparing functor based on \p opt::compare and \p opt::less option setter. Used only for ordered probe set + typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator; + + /// allocator type + typedef typename traits::allocator allocator; + + /// item counter type + typedef typename traits::item_counter item_counter; + + /// node disposer + typedef typename traits::disposer disposer; + + protected: + //@cond + typedef typename node_type::probeset_class probeset_class; + typedef typename node_type::probeset_type probeset_type; + static unsigned int const c_nNodeHashArraySize = node_type::hash_array_size; + + typedef typename mutex_policy::scoped_cell_lock scoped_cell_lock; + typedef typename mutex_policy::scoped_cell_trylock scoped_cell_trylock; + typedef typename mutex_policy::scoped_full_lock scoped_full_lock; + typedef typename mutex_policy::scoped_resize_lock scoped_resize_lock; + + typedef hopscotch_hashset::details::bucket_entry< node_type, probeset_type > bucket_entry; + typedef typename bucket_entry::iterator bucket_iterator; + typedef cds::details::Allocator< bucket_entry, allocator > bucket_table_allocator; + + typedef size_t hash_array[c_nArity] ; ///< hash array + + struct position { + bucket_iterator itPrev; + bucket_iterator itFound; + }; + + typedef hopscotch_hashset::details::contains< node_traits, c_isSorted > contains_action; + + template + struct predicate_wrapper { + typedef typename std::conditional< c_isSorted, cds::opt::details::make_comparator_from_less, Predicate>::type type; + }; + + typedef typename std::conditional< c_isSorted, key_comparator, key_equal_to >::type key_predicate; + //@endcond + + public: + static unsigned int const c_nDefaultProbesetSize = 4; ///< default probeset size + static size_t const c_nDefaultInitialSize = 16; ///< default initial size + static unsigned int const c_nRelocateLimit = c_nArity * 2 - 1; ///< Count of attempts to relocate before giving up + + protected: + bucket_entry * m_BucketTable[ c_nArity ] ; ///< Bucket tables + + atomics::atomic m_nBucketMask ; ///< Hash bitmask; bucket table size minus 1. + unsigned int const m_nProbesetSize ; ///< Probe set size + unsigned int const m_nProbesetThreshold ; ///< Probe set threshold + + hash m_Hash ; ///< Hash functor tuple + mutex_policy m_MutexPolicy ; ///< concurrent access policy + item_counter m_ItemCounter ; ///< item counter + mutable stat m_Stat ; ///< internal statistics + + protected: + //@cond + static void check_common_constraints() + { + static_assert( (c_nArity == mutex_policy::c_nArity), "The count of hash functors must be equal to mutex_policy arity" ); + } + + void check_probeset_properties() const + { + assert( m_nProbesetThreshold < m_nProbesetSize ); + + // if probe set type is hopscotch_hashset::vector then m_nProbesetSize == N + assert( node_type::probeset_size == 0 || node_type::probeset_size == m_nProbesetSize ); + } + + template + void hashing( size_t * pHashes, Q const& v ) const + { + m_Hash( pHashes, v ); + } + + void copy_hash( size_t * pHashes, value_type const& v ) const + { + constexpr_if ( c_nNodeHashArraySize != 0 ) + memcpy( pHashes, node_traits::to_node_ptr( v )->get_hash(), sizeof( pHashes[0] ) * c_nNodeHashArraySize ); + else + hashing( pHashes, v ); + } + + bucket_entry& bucket( unsigned int nTable, size_t nHash ) + { + assert( nTable < c_nArity ); + return m_BucketTable[nTable][nHash & m_nBucketMask.load( atomics::memory_order_relaxed ) ]; + } + + static void store_hash( node_type * pNode, size_t * pHashes ) + { + hopscotch_hashset::details::hash_ops< node_type, c_nNodeHashArraySize >::store( pNode, pHashes ); + } + + static bool equal_hash( node_type& node, unsigned int nTable, size_t nHash ) + { + return hopscotch_hashset::details::hash_ops< node_type, c_nNodeHashArraySize >::equal_to( node, nTable, nHash ); + } + + void allocate_bucket_tables( size_t nSize ) + { + assert( cds::beans::is_power2( nSize )); + + m_nBucketMask.store( nSize - 1, atomics::memory_order_release ); + bucket_table_allocator alloc; + for ( unsigned int i = 0; i < c_nArity; ++i ) + m_BucketTable[i] = alloc.NewArray( nSize ); + } + + static void free_bucket_tables( bucket_entry ** pTable, size_t nCapacity ) + { + bucket_table_allocator alloc; + for ( unsigned int i = 0; i < c_nArity; ++i ) { + alloc.Delete( pTable[i], nCapacity ); + pTable[i] = nullptr; + } + } + void free_bucket_tables() + { + free_bucket_tables( m_BucketTable, m_nBucketMask.load( atomics::memory_order_relaxed ) + 1 ); + } + + static constexpr unsigned int const c_nUndefTable = (unsigned int) -1; + template + unsigned int contains( position * arrPos, size_t * arrHash, Q const& val, Predicate pred ) + { + // Buckets must be locked + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& probeset = bucket( i, arrHash[i] ); + if ( contains_action::find( probeset, arrPos[i], i, arrHash[i], val, pred )) + return i; + } + return c_nUndefTable; + } + + template + value_type * erase_( Q const& val, Predicate pred, Func f ) + { + hash_array arrHash; + hashing( arrHash, val ); + position arrPos[ c_nArity ]; + + { + scoped_cell_lock guard( m_MutexPolicy, arrHash ); + + unsigned int nTable = contains( arrPos, arrHash, val, pred ); + if ( nTable != c_nUndefTable ) { + node_type& node = *arrPos[nTable].itFound; + f( *node_traits::to_value_ptr(node)); + bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound ); + --m_ItemCounter; + m_Stat.onEraseSuccess(); + return node_traits::to_value_ptr( node ); + } + } + + m_Stat.onEraseFailed(); + return nullptr; + } + + template + bool find_( Q& val, Predicate pred, Func f ) + { + hash_array arrHash; + position arrPos[ c_nArity ]; + hashing( arrHash, val ); + scoped_cell_lock sl( m_MutexPolicy, arrHash ); + + unsigned int nTable = contains( arrPos, arrHash, val, pred ); + if ( nTable != c_nUndefTable ) { + f( *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val ); + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; + } + + bool relocate( unsigned int nTable, size_t * arrGoalHash ) + { + // arrGoalHash contains hash values for relocating element + // Relocating element is first one from bucket( nTable, arrGoalHash[nTable] ) probeset + + m_Stat.onRelocateCall(); + + hash_array arrHash; + value_type * pVal; + for ( unsigned int nRound = 0; nRound < c_nRelocateLimit; ++nRound ) { + m_Stat.onRelocateRound(); + + while ( true ) { + scoped_cell_lock guard( m_MutexPolicy, arrGoalHash ); + + bucket_entry& refBucket = bucket( nTable, arrGoalHash[nTable] ); + if ( refBucket.size() < m_nProbesetThreshold ) { + // probeset is not above the threshold + m_Stat.onFalseRelocateRound(); + return true; + } + + pVal = node_traits::to_value_ptr( *refBucket.begin()); + copy_hash( arrHash, *pVal ); + + scoped_cell_trylock guard2( m_MutexPolicy, arrHash ); + if ( !guard2.locked()) + continue ; // try one more time + + refBucket.remove( typename bucket_entry::iterator(), refBucket.begin()); + + unsigned int i = (nTable + 1) % c_nArity; + + // try insert into free probeset + while ( i != nTable ) { + bucket_entry& bkt = bucket( i, arrHash[i] ); + if ( bkt.size() < m_nProbesetThreshold ) { + position pos; + contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false! + bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal )); + m_Stat.onSuccessRelocateRound(); + return true; + } + i = ( i + 1 ) % c_nArity; + } + + // try insert into partial probeset + i = (nTable + 1) % c_nArity; + while ( i != nTable ) { + bucket_entry& bkt = bucket( i, arrHash[i] ); + if ( bkt.size() < m_nProbesetSize ) { + position pos; + contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false! + bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal )); + nTable = i; + memcpy( arrGoalHash, arrHash, sizeof(arrHash)); + m_Stat.onRelocateAboveThresholdRound(); + goto next_iteration; + } + i = (i + 1) % c_nArity; + } + + // all probeset is full, relocating fault + refBucket.insert_after( typename bucket_entry::iterator(), node_traits::to_node_ptr( pVal )); + m_Stat.onFailedRelocate(); + return false; + } + + next_iteration:; + } + return false; + } + + void resize() + { + m_Stat.onResizeCall(); + + size_t nOldCapacity = bucket_count( atomics::memory_order_acquire ); + bucket_entry* pOldTable[ c_nArity ]; + { + scoped_resize_lock guard( m_MutexPolicy ); + + if ( nOldCapacity != bucket_count()) { + m_Stat.onFalseResizeCall(); + return; + } + + size_t nCapacity = nOldCapacity * 2; + + m_MutexPolicy.resize( nCapacity ); + memcpy( pOldTable, m_BucketTable, sizeof(pOldTable)); + allocate_bucket_tables( nCapacity ); + + hash_array arrHash; + position arrPos[ c_nArity ]; + + for ( unsigned int nTable = 0; nTable < c_nArity; ++nTable ) { + bucket_entry * pTable = pOldTable[nTable]; + for ( size_t k = 0; k < nOldCapacity; ++k ) { + bucket_iterator itNext; + for ( bucket_iterator it = pTable[k].begin(), itEnd = pTable[k].end(); it != itEnd; it = itNext ) { + itNext = it; + ++itNext; + + value_type& val = *node_traits::to_value_ptr( *it ); + copy_hash( arrHash, val ); + CDS_VERIFY_EQ( contains( arrPos, arrHash, val, key_predicate()), c_nUndefTable ); + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetThreshold ) { + refBucket.insert_after( arrPos[i].itPrev, &*it ); + m_Stat.onResizeSuccessMove(); + goto do_next; + } + } + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetSize ) { + refBucket.insert_after( arrPos[i].itPrev, &*it ); + assert( refBucket.size() > 1 ); + copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin())); + m_Stat.onResizeRelocateCall(); + relocate( i, arrHash ); + break; + } + } + do_next:; + } + } + } + } + free_bucket_tables( pOldTable, nOldCapacity ); + } + + constexpr static unsigned int calc_probeset_size( unsigned int nProbesetSize ) noexcept + { + return std::is_same< probeset_class, hopscotch_hashset::vector_probeset_class >::value + ? node_type::probeset_size + : (nProbesetSize + ? nProbesetSize + : ( node_type::probeset_size ? node_type::probeset_size : c_nDefaultProbesetSize )); + } + //@endcond + + public: + /// Default constructor + /** + Initial size = \ref c_nDefaultInitialSize + + Probe set size: + - \p c_nDefaultProbesetSize if \p probeset_type is \p hopscotch_hashset::list + - \p Capacity if \p probeset_type is hopscotch_hashset::vector + + Probe set threshold = probe set size - 1 + */ + HopscotchHashset() + : m_nProbesetSize( calc_probeset_size(0)) + , m_nProbesetThreshold( m_nProbesetSize - 1 ) + , m_MutexPolicy( c_nDefaultInitialSize ) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( c_nDefaultInitialSize ); + } + + /// Constructs the set object with given probe set size and threshold + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize is ignored since it should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size + , unsigned int nProbesetThreshold = 0 ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + ) + : m_nProbesetSize( calc_probeset_size(nProbesetSize)) + , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1 ) + , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize )) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize ); + } + + /// Constructs the set object with given hash functor tuple + /** + The probe set size and threshold are set as default, see \p HopscotchHashset() + */ + HopscotchHashset( + hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : m_nProbesetSize( calc_probeset_size(0)) + , m_nProbesetThreshold( m_nProbesetSize -1 ) + , m_Hash( h ) + , m_MutexPolicy( c_nDefaultInitialSize ) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( c_nDefaultInitialSize ); + } + + /// Constructs the set object with given probe set properties and hash functor tuple + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size, positive integer + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : m_nProbesetSize( calc_probeset_size(nProbesetSize)) + , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1) + , m_Hash( h ) + , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize )) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize ); + } + + /// Constructs the set object with given hash functor tuple (move semantics) + /** + The probe set size and threshold are set as default, see \p HopscotchHashset() + */ + HopscotchHashset( + hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : m_nProbesetSize( calc_probeset_size(0)) + , m_nProbesetThreshold( m_nProbesetSize / 2 ) + , m_Hash( std::forward(h)) + , m_MutexPolicy( c_nDefaultInitialSize ) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( c_nDefaultInitialSize ); + } + + /// Constructs the set object with given probe set properties and hash functor tuple (move semantics) + /** + If probe set type is hopscotch_hashset::vector vector + then \p nProbesetSize should be equal to vector's \p Capacity. + */ + HopscotchHashset( + size_t nInitialSize ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize + , unsigned int nProbesetSize ///< probe set size, positive integer + , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 + , hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity + ) + : m_nProbesetSize( calc_probeset_size(nProbesetSize)) + , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1) + , m_Hash( std::forward(h)) + , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize )) + { + check_common_constraints(); + check_probeset_properties(); + + allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize ); + } + + /// Destructor + ~HopscotchHashset() + { + free_bucket_tables(); + } + + public: + /// Inserts new node + /** + The function inserts \p val in the set if it does not contain an item with key equal to \p val. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + bool insert( value_type& val ) + { + return insert( val, []( value_type& ) {} ); + } + + /// Inserts new node + /** + The function allows to split creating of new item into two part: + - create item with key only + - insert new item into the set + - if inserting is success, calls \p f functor to initialize value-field of \p val. + + The functor signature is: + \code + void func( value_type& val ); + \endcode + where \p val is the item inserted. + + The user-defined functor is called only if the inserting is success. + */ + template + bool insert( value_type& val, Func f ) + { + hash_array arrHash; + position arrPos[ c_nArity ]; + unsigned int nGoalTable; + + hashing( arrHash, val ); + node_type * pNode = node_traits::to_node_ptr( val ); + store_hash( pNode, arrHash ); + + while (true) { + { + scoped_cell_lock guard( m_MutexPolicy, arrHash ); + + if ( contains( arrPos, arrHash, val, key_predicate()) != c_nUndefTable ) { + m_Stat.onInsertFailed(); + return false; + } + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetThreshold ) { + refBucket.insert_after( arrPos[i].itPrev, pNode ); + f( val ); + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } + } + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetSize ) { + refBucket.insert_after( arrPos[i].itPrev, pNode ); + f( val ); + ++m_ItemCounter; + nGoalTable = i; + assert( refBucket.size() > 1 ); + copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin())); + goto do_relocate; + } + } + } + + m_Stat.onInsertResize(); + resize(); + } + + do_relocate: + m_Stat.onInsertRelocate(); + if ( !relocate( nGoalTable, arrHash )) { + m_Stat.onInsertRelocateFault(); + m_Stat.onInsertResize(); + resize(); + } + + m_Stat.onInsertSuccess(); + return true; + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the set, then \p val is inserted into the set + iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + void func( bool bNew, value_type& item, value_type& val ); + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the set + - \p val - argument \p val passed into the \p %update() function + If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments + refer to the same thing. + + The functor may change non-key fields of the \p item. + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update( value_type& val, Func func, bool bAllowInsert = true ) + { + hash_array arrHash; + position arrPos[ c_nArity ]; + unsigned int nGoalTable; + + hashing( arrHash, val ); + node_type * pNode = node_traits::to_node_ptr( val ); + store_hash( pNode, arrHash ); + + while (true) { + { + scoped_cell_lock guard( m_MutexPolicy, arrHash ); + + unsigned int nTable = contains( arrPos, arrHash, val, key_predicate()); + if ( nTable != c_nUndefTable ) { + func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val ); + m_Stat.onUpdateExist(); + return std::make_pair( true, false ); + } + + if ( !bAllowInsert ) + return std::make_pair( false, false ); + + //node_type * pNode = node_traits::to_node_ptr( val ); + //store_hash( pNode, arrHash ); + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetThreshold ) { + refBucket.insert_after( arrPos[i].itPrev, pNode ); + func( true, val, val ); + ++m_ItemCounter; + m_Stat.onUpdateSuccess(); + return std::make_pair( true, true ); + } + } + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry& refBucket = bucket( i, arrHash[i] ); + if ( refBucket.size() < m_nProbesetSize ) { + refBucket.insert_after( arrPos[i].itPrev, pNode ); + func( true, val, val ); + ++m_ItemCounter; + nGoalTable = i; + assert( refBucket.size() > 1 ); + copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin())); + goto do_relocate; + } + } + } + + m_Stat.onUpdateResize(); + resize(); + } + + do_relocate: + m_Stat.onUpdateRelocate(); + if ( !relocate( nGoalTable, arrHash )) { + m_Stat.onUpdateRelocateFault(); + m_Stat.onUpdateResize(); + resize(); + } + + m_Stat.onUpdateSuccess(); + return std::make_pair( true, true ); + } + //@cond + template + CDS_DEPRECATED("ensure() is deprecated, use update()") + std::pair ensure( value_type& val, Func func ) + { + return update( val, func, true ); + } + //@endcond + + /// Unlink the item \p val from the set + /** + The function searches the item \p val in the set and unlink it + if it is found and is equal to \p val (here, the equality means that + \p val belongs to the set: if \p item is an item found then + unlink is successful iif &val == &item) + + The function returns \p true if success and \p false otherwise. + */ + bool unlink( value_type& val ) + { + hash_array arrHash; + hashing( arrHash, val ); + position arrPos[ c_nArity ]; + + { + scoped_cell_lock guard( m_MutexPolicy, arrHash ); + + unsigned int nTable = contains( arrPos, arrHash, val, key_predicate()); + if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) { + bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound ); + --m_ItemCounter; + m_Stat.onUnlinkSuccess(); + return true; + } + } + + m_Stat.onUnlinkFailed(); + return false; + } + + /// Deletes the item from the set + /** \anchor cds_intrusive_HopscotchHashset_erase + The function searches an item with key equal to \p val in the set, + unlinks it from the set, and returns a pointer to unlinked item. + + If the item with key equal to \p val is not found the function return \p nullptr. + + Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type. + */ + template + value_type * erase( Q const& val ) + { + return erase( val, [](value_type const&) {} ); + } + + /// Deletes the item from the set using \p pred predicate for searching + /** + The function is an analog of \ref cds_intrusive_HopscotchHashset_erase "erase(Q const&)" + but \p pred is used for key comparing. + If hopscotch set is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If hopscotch set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the set. + */ + template + value_type * erase_with( Q const& val, Predicate pred ) + { + CDS_UNUSED( pred ); + return erase_( val, typename predicate_wrapper::type(), [](value_type const&) {} ); + } + + /// Delete the item from the set + /** \anchor cds_intrusive_HopscotchHashset_erase_func + The function searches an item with key equal to \p val in the set, + call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item. + + The \p Func interface is + \code + struct functor { + void operator()( value_type const& item ); + }; + \endcode + + If the item with key equal to \p val is not found the function return \p nullptr. + + Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type. + */ + template + value_type * erase( Q const& val, Func f ) + { + return erase_( val, key_predicate(), f ); + } + + /// Deletes the item from the set using \p pred predicate for searching + /** + The function is an analog of \ref cds_intrusive_HopscotchHashset_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + If you use ordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::less. + If you use unordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the set. + */ + template + value_type * erase_with( Q const& val, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return erase_( val, typename predicate_wrapper::type(), f ); + } + + /// Find the key \p val + /** \anchor cds_intrusive_HopscotchHashset_find_func + The function searches the item with key equal to \p val and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& val ); + }; + \endcode + where \p item is the item found, \p val is the find function argument. + + The functor may change non-key fields of \p item. + + The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor + may modify both arguments. + + Note the hash functor specified for class \p Traits template parameter + should accept a parameter of type \p Q that can be not the same as \p value_type. + + The function returns \p true if \p val is found, \p false otherwise. + */ + template + bool find( Q& val, Func f ) + { + return find_( val, key_predicate(), f ); + } + //@cond + template + bool find( Q const& val, Func f ) + { + return find_( val, key_predicate(), f ); + } + //@endcond + + /// Find the key \p val using \p pred predicate for comparing + /** + The function is an analog of \ref cds_intrusive_HopscotchHashset_find_func "find(Q&, Func)" + but \p pred is used for key comparison. + If you use ordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::less. + If you use unordered hopscotch set, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p pred must imply the same element order as the comparator used for building the set. + */ + template + bool find_with( Q& val, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return find_( val, typename predicate_wrapper::type(), f ); + } + //@cond + template + bool find_with( Q const& val, Predicate pred, Func f ) + { + CDS_UNUSED( pred ); + return find_( val, typename predicate_wrapper::type(), f ); + } + //@endcond + + /// Checks whether the set contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains( Q const& key ) + { + return find( key, [](value_type&, Q const& ) {} ); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find( Q const& key ) + { + return contains( key ); + } + //@endcond + + /// Checks whether the set contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + If the set is unordered, \p Predicate has semantics like \p std::equal_to. + For ordered set \p Predicate has \p std::less semantics. In that case \p pred + must imply the same element order as the comparator used for building the set. + */ + template + bool contains( Q const& key, Predicate pred ) + { + CDS_UNUSED( pred ); + return find_with( key, typename predicate_wrapper::type(), [](value_type& , Q const& ) {} ); + } + //@cond + template + CDS_DEPRECATED("deprecated, use contains()") + bool find_with( Q const& key, Predicate pred ) + { + return contains( key, pred ); + } + //@endcond + + /// Clears the set + /** + The function unlinks all items from the set. + For any item \p Traits::disposer is called + */ + void clear() + { + clear_and_dispose( disposer()); + } + + /// Clears the set and calls \p disposer for each item + /** + The function unlinks all items from the set calling \p oDisposer for each item. + \p Disposer functor interface is: + \code + struct Disposer{ + void operator()( value_type * p ); + }; + \endcode + + The \p Traits::disposer is not called. + */ + template + void clear_and_dispose( Disposer oDisposer ) + { + // locks entire array + scoped_full_lock sl( m_MutexPolicy ); + + for ( unsigned int i = 0; i < c_nArity; ++i ) { + bucket_entry * pEntry = m_BucketTable[i]; + bucket_entry * pEnd = pEntry + m_nBucketMask.load( atomics::memory_order_relaxed ) + 1; + for ( ; pEntry != pEnd ; ++pEntry ) { + pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } ); + } + } + m_ItemCounter.reset(); + } + + /// Checks if the set is empty + /** + Emptiness is checked by item counting: if item count is zero then the set is empty. + */ + bool empty() const + { + return size() == 0; + } + + /// Returns item count in the set + size_t size() const + { + return m_ItemCounter; + } + + /// Returns the size of hash table + /** + The hash table size is non-constant and can be increased via resizing. + */ + size_t bucket_count() const + { + return m_nBucketMask.load( atomics::memory_order_relaxed ) + 1; + } + //@cond + size_t bucket_count( atomics::memory_order load_mo ) const + { + return m_nBucketMask.load( load_mo ) + 1; + } + //@endcond + + /// Returns lock array size + size_t lock_count() const + { + return m_MutexPolicy.lock_count(); + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + /// Returns const reference to mutex policy internal statistics + typename mutex_policy::statistics_type const& mutex_policy_statistics() const + { + return m_MutexPolicy.statistics(); + } + }; +}} // namespace cds::intrusive + +#endif // #ifndef CDSLIB_INTRUSIVE_HOPSCOTCH_HASHSET_H diff --git a/projects/Win/vc141/cds.sln b/projects/Win/vc141/cds.sln index 4b61eb8d4..0ebd4ca16 100644 --- a/projects/Win/vc141/cds.sln +++ b/projects/Win/vc141/cds.sln @@ -29,6 +29,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cds_test", "cds_test", "{3A ..\..\..\test\include\cds_test\stat_ellenbintree_out.h = ..\..\..\test\include\cds_test\stat_ellenbintree_out.h ..\..\..\test\include\cds_test\stat_feldman_hashset_out.h = ..\..\..\test\include\cds_test\stat_feldman_hashset_out.h ..\..\..\test\include\cds_test\stat_flat_combining_out.h = ..\..\..\test\include\cds_test\stat_flat_combining_out.h + ..\..\..\test\include\cds_test\stat_hopscotch_out.h = ..\..\..\test\include\cds_test\stat_hopscotch_out.h ..\..\..\test\include\cds_test\stat_hp_out.h = ..\..\..\test\include\cds_test\stat_hp_out.h ..\..\..\test\include\cds_test\stat_iterable_list_out.h = ..\..\..\test\include\cds_test\stat_iterable_list_out.h ..\..\..\test\include\cds_test\stat_lazy_list_out.h = ..\..\..\test\include\cds_test\stat_lazy_list_out.h @@ -127,6 +128,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Header Files", "Header File ..\..\..\test\stress\map\map_type_cuckoo.h = ..\..\..\test\stress\map\map_type_cuckoo.h ..\..\..\test\stress\map\map_type_ellen_bintree.h = ..\..\..\test\stress\map\map_type_ellen_bintree.h ..\..\..\test\stress\map\map_type_feldman_hashmap.h = ..\..\..\test\stress\map\map_type_feldman_hashmap.h + ..\..\..\test\stress\map\map_type_hopscotch.h = ..\..\..\test\stress\map\map_type_hopscotch.h ..\..\..\test\stress\map\map_type_iterable_list.h = ..\..\..\test\stress\map\map_type_iterable_list.h ..\..\..\test\stress\map\map_type_lazy_list.h = ..\..\..\test\stress\map\map_type_lazy_list.h ..\..\..\test\stress\map\map_type_michael.h = ..\..\..\test\stress\map\map_type_michael.h @@ -459,7 +461,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress-spsc-queue", "stress {408FE9BC-44F0-4E6A-89FA-D6F952584239} = {408FE9BC-44F0-4E6A-89FA-D6F952584239} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-map-hopscotch", "gtest-striped-map-hopscotch.vcxproj", "{78FF8F0D-9394-41CF-B895-BDC8538CDB1D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-map-hopscotch", "gtest-striped-hashmap-hopscotch.vcxproj", "{BADD532E-7C37-4877-AAA9-34DB4298C200}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-set-hopscotch", "gtest-striped-set-hopscotch.vcxproj", "{045D3046-9609-4B5B-81C3-17F5A0FEC48E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1875,26 +1879,46 @@ Global {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.Build.0 = vc14-Release|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|Win32.ActiveCfg = Debug|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|Win32.Build.0 = Debug|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|x64.ActiveCfg = Debug|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Debug|x64.Build.0 = Debug|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.DebugVLD|x64.Build.0 = DebugVLD|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|Win32.ActiveCfg = Release|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|Win32.Build.0 = Release|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|x64.ActiveCfg = Release|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.Release|x64.Build.0 = Release|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D}.vc14-Release|x64.Build.0 = vc14-Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.ActiveCfg = Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.Build.0 = Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.ActiveCfg = Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.Build.0 = Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.Build.0 = DebugVLD|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.ActiveCfg = Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.Build.0 = Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.ActiveCfg = Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.Build.0 = Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.Build.0 = vc14-Release|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|Win32.ActiveCfg = Debug|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|Win32.Build.0 = Debug|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|x64.ActiveCfg = Debug|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|x64.Build.0 = Debug|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.DebugVLD|x64.Build.0 = DebugVLD|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Release|Win32.ActiveCfg = Release|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Release|Win32.Build.0 = Release|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Release|x64.ActiveCfg = Release|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Release|x64.Build.0 = Release|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 + {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|x64.Build.0 = vc14-Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1982,7 +2006,8 @@ Global {EACBF8EC-C58F-4CC8-9435-21BE9812287A} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {47B9F1F9-C9F1-4F25-842B-EE8E6447FE50} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {CDC54B7B-B882-4CC8-87A2-E237D0D945B2} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D} = {DE3B8DBB-5B4B-40DC-80DB-F0C15BC21E0F} + {BADD532E-7C37-4877-AAA9-34DB4298C200} = {DE3B8DBB-5B4B-40DC-80DB-F0C15BC21E0F} + {045D3046-9609-4B5B-81C3-17F5A0FEC48E} = {E0C742CB-A36A-4617-8A06-DD4820FEB8ED} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3673AF8B-E81E-49E1-8241-B5FC94791FB8} diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index 0b2f2bea3..750134704 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -238,7 +238,7 @@ /wd4512 /wd4127 /Zc:inline /permissive- %(AdditionalOptions) Disabled - $(GTEST_ROOT);$(SolutionDir)..\..\..\;$(BOOST_PATH);%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\..\;$(BOOST_PATH);%(AdditionalIncludeDirectories) _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0500;CDS_BUILD_LIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -251,7 +251,7 @@ $(OutDir)$(TargetName).dll - $(BOOST_PATH)/stage/lib;%(AdditionalLibraryDirectories) + %(AdditionalLibraryDirectories) true $(TargetDir)$(TargetName).pdb Console @@ -659,7 +659,6 @@ - @@ -667,6 +666,8 @@ + + @@ -690,6 +691,8 @@ + + @@ -787,6 +790,7 @@ + diff --git a/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj index a64c6d4e8..869a47bae 100644 --- a/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj +++ b/projects/Win/vc141/gtest-striped-map-hopscotch.vcxproj @@ -52,10 +52,11 @@ - {78FF8F0D-9394-41CF-B895-BDC8538CDB1D} + {BADD532E-7C37-4877-AAA9-34DB4298C200} Win32Proj striped-map 10.0.14393.0 + gtest-striped-map-hopscotch @@ -220,7 +221,7 @@ Level3 Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - $(SolutionDir)..\..\..;$(GTEST_ROOT)\include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) /bigobj %(AdditionalOptions) /Zc:inline /permissive- diff --git a/projects/Win/vc141/gtest-map-hopscotch.vcxproj b/projects/Win/vc141/gtest-striped-set-hopscotch.vcxproj similarity index 76% rename from projects/Win/vc141/gtest-map-hopscotch.vcxproj rename to projects/Win/vc141/gtest-striped-set-hopscotch.vcxproj index 0886c986c..4dbc54cc4 100644 --- a/projects/Win/vc141/gtest-map-hopscotch.vcxproj +++ b/projects/Win/vc141/gtest-striped-set-hopscotch.vcxproj @@ -44,52 +44,19 @@ - - - - - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - - - - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - 4503 - - + + - - - - - - - - - - + + + + - {C6099C17-DF98-45AF-92F6-6B40169D39FA} + {045D3046-9609-4B5B-81C3-17F5A0FEC48E} Win32Proj - map_skip + striped-set 10.0.14393.0 @@ -254,10 +221,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -271,10 +237,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -288,10 +253,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -305,10 +269,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -322,10 +285,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -339,10 +301,9 @@ NotUsing Level3 Disabled - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -358,10 +319,9 @@ MaxSpeed true true - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -379,10 +339,9 @@ MaxSpeed true true - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -400,10 +359,9 @@ MaxSpeed true true - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console @@ -421,10 +379,9 @@ MaxSpeed true true - _ENABLE_ATOMIC_ALIGNMENT_FIX;CDS_ENABLE_HPSTAT;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _ENABLE_ATOMIC_ALIGNMENT_FIX;CDSUNIT_ENABLE_BOOST_CONTAINER;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) - /bigobj /Zc:inline /permissive- %(AdditionalOptions) - 4503 + /bigobj %(AdditionalOptions) /Zc:inline /permissive- Console diff --git a/projects/Win/vc141/stress-map-del3.vcxproj b/projects/Win/vc141/stress-map-del3.vcxproj index 0ca18cdf3..3bc9aa311 100644 --- a/projects/Win/vc141/stress-map-del3.vcxproj +++ b/projects/Win/vc141/stress-map-del3.vcxproj @@ -60,6 +60,7 @@ + diff --git a/test/include/cds_test/stat_hopscotch_out.h b/test/include/cds_test/stat_hopscotch_out.h new file mode 100644 index 000000000..3b73803a2 --- /dev/null +++ b/test/include/cds_test/stat_hopscotch_out.h @@ -0,0 +1,116 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSTEST_STAT_HOPSCOTCH_OUT_H +#define CDSTEST_STAT_HOPSCOTCH_OUT_H + +#include + +namespace cds_test { + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::striping_stat const& s ) + { + return o + << CDSSTRESS_STAT_OUT( s, m_nCellLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nCellLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nCellTryLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nFullLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nResizeLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nResizeCount ); + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::empty_striping_stat const& /*s*/ ) + { + return o; + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::refinable_stat const& s ) + { + return o + << CDSSTRESS_STAT_OUT( s, m_nCellLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nCellLockWaitResizing ) + << CDSSTRESS_STAT_OUT( s, m_nCellLockArrayChanged ) + << CDSSTRESS_STAT_OUT( s, m_nCellLockFailed ) + << CDSSTRESS_STAT_OUT( s, m_nSecondCellLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nSecondCellLockFailed ) + << CDSSTRESS_STAT_OUT( s, m_nFullLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nFullLockIter ) + << CDSSTRESS_STAT_OUT( s, m_nResizeLockCount ) + << CDSSTRESS_STAT_OUT( s, m_nResizeLockIter ) + << CDSSTRESS_STAT_OUT( s, m_nResizeLockArrayChanged ) + << CDSSTRESS_STAT_OUT( s, m_nResizeCount ); + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::empty_refinable_stat const& /*s*/ ) + { + return o; + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::stat const& s ) + { + return o + << CDSSTRESS_STAT_OUT( s, m_nRelocateCallCount ) + << CDSSTRESS_STAT_OUT( s, m_nRelocateRoundCount ) + << CDSSTRESS_STAT_OUT( s, m_nFalseRelocateCount ) + << CDSSTRESS_STAT_OUT( s, m_nSuccessRelocateCount ) + << CDSSTRESS_STAT_OUT( s, m_nRelocateAboveThresholdCount ) + << CDSSTRESS_STAT_OUT( s, m_nFailedRelocateCount ) + << CDSSTRESS_STAT_OUT( s, m_nResizeCallCount ) + << CDSSTRESS_STAT_OUT( s, m_nFalseResizeCount ) + << CDSSTRESS_STAT_OUT( s, m_nResizeSuccessNodeMove ) + << CDSSTRESS_STAT_OUT( s, m_nResizeRelocateCall ) + << CDSSTRESS_STAT_OUT( s, m_nInsertSuccess ) + << CDSSTRESS_STAT_OUT( s, m_nInsertFailed ) + << CDSSTRESS_STAT_OUT( s, m_nInsertResizeCount ) + << CDSSTRESS_STAT_OUT( s, m_nInsertRelocateCount ) + << CDSSTRESS_STAT_OUT( s, m_nInsertRelocateFault ) + << CDSSTRESS_STAT_OUT( s, m_nUpdateExistCount ) + << CDSSTRESS_STAT_OUT( s, m_nUpdateSuccessCount ) + << CDSSTRESS_STAT_OUT( s, m_nUpdateResizeCount ) + << CDSSTRESS_STAT_OUT( s, m_nUpdateRelocateCount ) + << CDSSTRESS_STAT_OUT( s, m_nUpdateRelocateFault ) + << CDSSTRESS_STAT_OUT( s, m_nUnlinkSuccess ) + << CDSSTRESS_STAT_OUT( s, m_nUnlinkFailed ) + << CDSSTRESS_STAT_OUT( s, m_nEraseSuccess ) + << CDSSTRESS_STAT_OUT( s, m_nEraseFailed ) + << CDSSTRESS_STAT_OUT( s, m_nFindSuccess ) + << CDSSTRESS_STAT_OUT( s, m_nFindFailed ) + << CDSSTRESS_STAT_OUT( s, m_nFindWithSuccess ) + << CDSSTRESS_STAT_OUT( s, m_nFindWithFailed ); + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::hopscotch_hashset::empty_stat const& /*s*/ ) + { + return o; + } + +} // namespace cds_test + +#endif // #ifndef CDSTEST_STAT_HOPSCOTCH_OUT_H diff --git a/test/stress/map/del3/map_del3_hopscotch.cpp b/test/stress/map/del3/map_del3_hopscotch.cpp new file mode 100644 index 000000000..3effa1a2c --- /dev/null +++ b/test/stress/map/del3/map_del3_hopscotch.cpp @@ -0,0 +1,38 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "map_del3.h" +#include "map_type_hopscotch.h" + +namespace map { + + CDSSTRESS_HopscotchHashmap( Map_Del3, run_test, key_thread, size_t ) + +} // namespace map diff --git a/test/stress/map/map_type_hopscotch.h b/test/stress/map/map_type_hopscotch.h new file mode 100644 index 000000000..e74797ea8 --- /dev/null +++ b/test/stress/map/map_type_hopscotch.h @@ -0,0 +1,364 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CDSUNIT_MAP_TYPE_HOPSCOTCH_H +#define CDSUNIT_MAP_TYPE_HOPSCOTCH_H + +#include "map_type.h" +#include +#include +#include + +namespace map { + + template + class HopscotchHashmap: public cc::HopscotchHashmap< K, V, Traits > + { + public: + typedef cc::HopscotchHashmap< K, V, Traits > base_class; + + public: + template + HopscotchHashmap( Config const& cfg ) + : base_class( + cfg.s_nHopscotchInitialSize, + static_cast( cfg.s_nHopscotchProbesetSize ), + static_cast( cfg.s_nHopscotchProbesetThreshold ) + ) + {} + + template + bool erase_with( Q const& key, Pred /*pred*/ ) + { + return base_class::erase_with( key, typename std::conditional< base_class::c_isSorted, Pred, typename Pred::equal_to>::type()); + } + + // for testing + static constexpr bool const c_bExtractSupported = false; + static constexpr bool const c_bLoadFactorDepended = false; + static constexpr bool const c_bEraseExactKey = false; + }; + + struct tag_HopscotchHashmap; + + template + struct map_type< tag_HopscotchHashmap, Key, Value >: public map_type_base< Key, Value > + { + typedef map_type_base< Key, Value > base_class; + typedef typename base_class::key_compare compare; + typedef typename base_class::key_less less; + typedef typename base_class::equal_to equal_to; + typedef typename base_class::key_hash hash; + typedef typename base_class::hash2 hash2; + + template + struct traits_HopscotchStripedMap: public Traits + { + typedef cc::hopscotch_hashmap::striping<> mutex_policy; + }; + template + struct traits_HopscotchRefinableMap : public Traits + { + typedef cc::hopscotch_hashmap::refinable<> mutex_policy; + }; + + struct traits_HopscotchHashmap_list_unord : + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + , co::equal_to< equal_to > + , co::hash< std::tuple< hash, hash2 > > + > ::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord; + + struct traits_HopscotchHashmap_list_unord_stat : public traits_HopscotchHashmap_list_unord + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord_stat; + + struct traits_HopscotchHashmap_list_unord_storehash : public traits_HopscotchHashmap_list_unord + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord_storehash; + + struct traits_HopscotchHashmap_list_ord : + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + , co::compare< compare > + , co::hash< std::tuple< hash, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord; + + struct traits_HopscotchHashmap_list_ord_stat : public traits_HopscotchHashmap_list_ord + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord_stat; + + struct traits_HopscotchHashmap_list_ord_storehash : public traits_HopscotchHashmap_list_ord + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord_storehash; + + struct traits_HopscotchHashmap_vector_unord : + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<4> > + , co::equal_to< equal_to > + , co::hash< std::tuple< hash, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord; + + struct traits_HopscotchHashmap_vector_unord_stat : public traits_HopscotchHashmap_vector_unord + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord_stat; + + struct traits_HopscotchHashmap_vector_unord_storehash : public traits_HopscotchHashmap_vector_unord + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord_storehash; + + struct traits_HopscotchHashmap_vector_ord : + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<4> > + , co::compare< compare > + , co::hash< std::tuple< hash, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord; + + struct traits_HopscotchHashmap_vector_ord_stat : public traits_HopscotchHashmap_vector_ord + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord_stat; + + struct traits_HopscotchHashmap_vector_ord_storehash : public traits_HopscotchHashmap_vector_ord + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord_storehash; + +#if CDS_BUILD_BITS == 64 + + struct traits_HopscotchHashmap_list_unord_city64: + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + , co::equal_to< equal_to > + , co::hash< std::tuple< cds_test::city64, hash2 > > + > ::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord_city64; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord_city64; + + struct traits_HopscotchHashmap_list_unord_city64_stat: public traits_HopscotchHashmap_list_unord_city64 + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord_city64_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord_city64_stat; + + struct traits_HopscotchHashmap_list_unord_city64_storehash: public traits_HopscotchHashmap_list_unord_city64 + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_unord_city64_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_unord_city64_storehash; + + struct traits_HopscotchHashmap_list_ord_city64: + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + , co::compare< compare > + , co::hash< std::tuple< cds_test::city64, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord_city64; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord_city64; + + struct traits_HopscotchHashmap_list_ord_city64_stat: public traits_HopscotchHashmap_list_ord_city64 + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord_city64_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord_city64_stat; + + struct traits_HopscotchHashmap_list_ord_city64_storehash: public traits_HopscotchHashmap_list_ord_city64 + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_list_ord_city64_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_list_ord_city64_storehash; + + struct traits_HopscotchHashmap_vector_unord_city64: + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<4> > + , co::equal_to< equal_to > + , co::hash< std::tuple< cds_test::city64, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord_city64; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord_city64; + + struct traits_HopscotchHashmap_vector_unord_city64_stat: public traits_HopscotchHashmap_vector_unord_city64 + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord_city64_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord_city64_stat; + + struct traits_HopscotchHashmap_vector_unord_city64_storehash: public traits_HopscotchHashmap_vector_unord_city64 + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_unord_city64_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_unord_city64_storehash; + + struct traits_HopscotchHashmap_vector_ord_city64: + public cc::hopscotch_hashmap::make_traits < + cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<4> > + , co::compare< compare > + , co::hash< std::tuple< cds_test::city64, hash2 > > + >::type + {}; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord_city64; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord_city64; + + struct traits_HopscotchHashmap_vector_ord_city64_stat: public traits_HopscotchHashmap_vector_ord_city64 + { + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord_city64_stat; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord_city64_stat; + + struct traits_HopscotchHashmap_vector_ord_city64_storehash: public traits_HopscotchHashmap_vector_ord_city64 + { + static constexpr const bool store_hash = true; + }; + typedef HopscotchHashmap< Key, Value, traits_HopscotchStripedMap> HopscotchStripedMap_vector_ord_city64_storehash; + typedef HopscotchHashmap< Key, Value, traits_HopscotchRefinableMap> HopscotchRefinableMap_vector_ord_city64_storehash; +#endif // CDS_BUILD_BITS == 64 + }; + + template + static inline void print_stat( cds_test::property_stream& o, cc::HopscotchHashmap< Key, T, Traits > const& s ) + { + o << s.statistics() << s.mutex_policy_statistics(); + } + + template + static inline void print_stat( cds_test::property_stream& o, HopscotchHashmap< Key, V, Traits > const& s ) + { + typedef HopscotchHashmap< Key, V, Traits > map_type; + print_stat( o, static_cast(s)); + } + +} // namespace map + + +#define CDSSTRESS_HopscotchHashmap_case( fixture, test_case, hopscotch_hashmap_type, key_type, value_type ) \ + TEST_F( fixture, hopscotch_map_type ) \ + { \ + typedef map::map_type< tag_HopscotchHashmap, key_type, value_type >::hopscotch_hashmap_type map_type; \ + test_case(); \ + } + +#define CDSSTRESS_HopscotchHashmap( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord_storehash, key_type, value_type ) + +#if CDS_BUILD_BITS == 64 +# define CDSSTRESS_HopscotchHashmap_city64( fixture, test_case, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_unord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_unord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_list_ord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_list_ord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_unord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_unord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord_city64, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord_city64_stat, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchStripedMap_vector_ord_city64_storehash, key_type, value_type ) \ + CDSSTRESS_HopscotchHashmap_case( fixture, test_case, HopscotchRefinableMap_vector_ord_city64_storehash, key_type, value_type ) +#endif +#endif // ifndef CDSUNIT_MAP_TYPE_HOPSCOTCH_H diff --git a/test/unit/main.cpp b/test/unit/main.cpp index cd33dd4ba..973007c7c 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -30,19 +30,17 @@ #include #include + #include -#include namespace cds_test { /*static*/ std::random_device fixture::random_dev_; /*static*/ std::mt19937 fixture::random_gen_( random_dev_() ); } // namespace cds_test -using namespace std; - int main( int argc, char **argv ) { - int result = 0; + int result; cds::Initialize(); { ::testing::InitGoogleTest( &argc, argv ); diff --git a/test/unit/striped-map/hopscotch_hashmap.cpp b/test/unit/striped-map/hopscotch_hashmap.cpp index bf0e23e43..734fe91dc 100644 --- a/test/unit/striped-map/hopscotch_hashmap.cpp +++ b/test/unit/striped-map/hopscotch_hashmap.cpp @@ -35,18 +35,18 @@ namespace { namespace cc = cds::container; - class HopscotchMap : public cds_test::container_map + class HopscotchHashmap : public cds_test::container_map { protected: typedef cds_test::container_map base_class; - template - void test(Map& m ) + template + void test( Set& m ) { - // Precondition: map is empty - // Postcondition: map is empty + // Precondition: set is empty + // Postcondition: set is empty - base_class::test_< Map::c_isSorted >( m ); + base_class::test_< Set::c_isSorted>( m ); } //void SetUp() @@ -56,16 +56,462 @@ namespace { //{} }; + struct store_hash_traits: public cc::hopscotch_hashmap::traits + { + static bool const store_hash = true; + }; + //************************************************************ // striped set - TEST_F(HopscotchMap, striped_list_unordered ) + TEST_F( HopscotchHashmap, striped_list_unordered ) + { + struct map_traits: public cc::hopscotch_hashmap::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::list probeset_type; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_unordered ) + { + struct map_traits: public cc::hopscotch_hashmap::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::vector<4> probeset_type; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m( 32, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_ordered_cmp ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + map_type m( 32, 6, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_ordered_cmp ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<8>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( ht ); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_ordered_less ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 4, ht ); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_ordered_less ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_ordered_cmpmix ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 0, std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_ordered_cmpmix ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_ordered_stat ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashmap::stat > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_ordered_stat ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashmap::stat > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<8>> + >::type + > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_unordered_storehash ) + { + struct map_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::list probeset_type; + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_unordered_storehash ) + { + struct map_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::stat stat; + typedef cc::hopscotch_hashmap::vector<4> probeset_type; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m( 32, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_list_ordered_storehash ) + { + typedef cc::HopscotchHashmap< key_type, value_type + ,cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + ,cc::hopscotch_hashmap::store_hash< true > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 0, std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, striped_vector_ordered_storehash ) + { + typedef cc::HopscotchHashmap< key_type, value_type + ,cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + ,cc::hopscotch_hashmap::store_hash< true > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + +//************************************************************ +// refinable set + + TEST_F( HopscotchHashmap, refinable_list_unordered ) { - typedef cc::hopscotch_hashmap< key_type, value_type > map_type; + struct map_traits: public cc::hopscotch_hashmap::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::list probeset_type; + typedef cc::hopscotch_hashmap::refinable<> mutex_policy; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; map_type m; test( m ); } - + + TEST_F( HopscotchHashmap, refinable_vector_unordered ) + { + struct map_traits: public cc::hopscotch_hashmap::traits + { + typedef cc::hopscotch_hashmap::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::vector<4> probeset_type; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m( 32, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_ordered_cmp ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + map_type m( 32, 6, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_ordered_cmp ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 >> + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<8>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( ht ); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_ordered_less ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 4, ht ); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_ordered_less ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_ordered_cmpmix ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 0, std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_ordered_cmpmix ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_ordered_stat ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashmap::stat > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + >::type + > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_ordered_stat ) + { + typedef cc::HopscotchHashmap< key_type, value_type + , cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashmap::stat > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<8>> + >::type + > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_unordered_storehash ) + { + struct map_traits: public store_hash_traits + { + typedef cc::hopscotch_hashmap::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::list probeset_type; + typedef cc::hopscotch_hashmap::stat stat; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m; + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_unordered_storehash ) + { + struct map_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef cc::hopscotch_hashmap::refinable<> mutex_policy; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashmap::stat stat; + typedef cc::hopscotch_hashmap::vector<4> probeset_type; + }; + typedef cc::HopscotchHashmap< key_type, value_type, map_traits > map_type; + + map_type m( 32, 4 ); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_list_ordered_storehash ) + { + typedef cc::HopscotchHashmap< key_type, value_type + ,cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::list > + ,cc::hopscotch_hashmap::store_hash< true > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( 32, 6, 0, std::move( ht )); + test( m ); + } + + TEST_F( HopscotchHashmap, refinable_vector_ordered_storehash ) + { + typedef cc::HopscotchHashmap< key_type, value_type + ,cc::hopscotch_hashmap::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashmap::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashmap::probeset_type< cc::hopscotch_hashmap::vector<6>> + ,cc::hopscotch_hashmap::store_hash< true > + >::type + > map_type; + + typename map_type::hash_tuple_type ht; + map_type m( std::move( ht )); + test( m ); + } + + } // namespace diff --git a/test/unit/striped-set/hopscotch_hashset.cpp b/test/unit/striped-set/hopscotch_hashset.cpp new file mode 100644 index 000000000..885b6c02e --- /dev/null +++ b/test/unit/striped-set/hopscotch_hashset.cpp @@ -0,0 +1,517 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "test_set.h" + +#include + +namespace { + namespace cc = cds::container; + + class HopscotchHashset : public cds_test::container_set + { + protected: + typedef cds_test::container_set base_class; + + template + void test( Set& s ) + { + // Precondition: set is empty + // Postcondition: set is empty + + base_class::test_< Set::c_isSorted>( s ); + } + + //void SetUp() + //{} + + //void TearDown() + //{} + }; + + struct store_hash_traits: public cc::hopscotch_hashset::traits + { + static bool const store_hash = true; + }; + + +//************************************************************ +// striped set + + TEST_F( HopscotchHashset, striped_list_unordered ) + { + struct set_traits: public cc::hopscotch_hashset::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::list probeset_type; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_unordered ) + { + struct set_traits: public cc::hopscotch_hashset::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::vector<4> probeset_type; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s( 32, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_ordered_cmp ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + set_type s( 32, 6, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_ordered_cmp ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<8>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_ordered_less ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_ordered_less ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_ordered_cmpmix ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_ordered_cmpmix ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_ordered_stat ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashset::stat > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_ordered_stat ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashset::stat > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<8>> + >::type + > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_unordered_storehash ) + { + struct set_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::list probeset_type; + typedef cc::hopscotch_hashset::stat stat; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_unordered_storehash ) + { + struct set_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::stat stat; + typedef cc::hopscotch_hashset::vector<4> probeset_type; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s( 32, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, striped_list_ordered_storehash ) + { + typedef cc::HopscotchHashset< int_item + ,cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + ,cc::hopscotch_hashset::store_hash< true > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, striped_vector_ordered_storehash ) + { + typedef cc::HopscotchHashset< int_item + ,cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + ,cc::hopscotch_hashset::store_hash< true > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + +//************************************************************ +// refinable set + + TEST_F( HopscotchHashset, refinable_list_unordered ) + { + struct set_traits: public cc::hopscotch_hashset::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::list probeset_type; + typedef cc::hopscotch_hashset::refinable<> mutex_policy; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_unordered ) + { + struct set_traits: public cc::hopscotch_hashset::traits + { + typedef cc::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::vector<4> probeset_type; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s( 32, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_ordered_cmp ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + set_type s( 32, 6, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_ordered_cmp ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 >> + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<8>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_ordered_less ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_ordered_less ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_ordered_cmpmix ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_ordered_cmpmix ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_ordered_stat ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashset::stat > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + >::type + > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_ordered_stat ) + { + typedef cc::HopscotchHashset< int_item + , cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cds::opt::stat< cc::hopscotch_hashset::stat > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<8>> + >::type + > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_unordered_storehash ) + { + struct set_traits: public store_hash_traits + { + typedef cc::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::list probeset_type; + typedef cc::hopscotch_hashset::stat stat; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s; + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_unordered_storehash ) + { + struct set_traits: public store_hash_traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef cc::hopscotch_hashset::refinable<> mutex_policy; + typedef base_class::equal_to equal_to; + typedef cc::hopscotch_hashset::stat stat; + typedef cc::hopscotch_hashset::vector<4> probeset_type; + }; + typedef cc::HopscotchHashset< int_item, set_traits > set_type; + + set_type s( 32, 4 ); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_list_ordered_storehash ) + { + typedef cc::HopscotchHashset< int_item + ,cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::list > + ,cc::hopscotch_hashset::store_hash< true > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s ); + } + + TEST_F( HopscotchHashset, refinable_vector_ordered_storehash ) + { + typedef cc::HopscotchHashset< int_item + ,cc::hopscotch_hashset::make_traits< + cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::mutex_policy< cc::hopscotch_hashset::refinable<>> + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,cc::hopscotch_hashset::probeset_type< cc::hopscotch_hashset::vector<6>> + ,cc::hopscotch_hashset::store_hash< true > + >::type + > set_type; + + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s ); + } + + +} // namespace diff --git a/test/unit/striped-set/intrusive_hopscotch_hashset.cpp b/test/unit/striped-set/intrusive_hopscotch_hashset.cpp new file mode 100644 index 000000000..5eda37a8d --- /dev/null +++ b/test/unit/striped-set/intrusive_hopscotch_hashset.cpp @@ -0,0 +1,1406 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (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/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "test_intrusive_set.h" + +#include + +namespace { + namespace ci = cds::intrusive; + + class IntrusiveHopscotchHashset : public cds_test::intrusive_set + { + protected: + typedef cds_test::intrusive_set base_class; + + typedef base_class::hash_int hash1; + + + template + void test( Set& s, std::vector< typename Set::value_type >& data ) + { + // Precondition: set is empty + // Postcondition: set is empty + + base_class::test_< Set::c_isSorted>( s, data ); + + size_t const nSetSize = base_class::kSize; + + // clear + for ( auto& i : data ) { + i.clear_stat(); + ASSERT_TRUE( s.insert( i )); + } + ASSERT_FALSE( s.empty()); + ASSERT_CONTAINER_SIZE( s, nSetSize ); + + s.clear(); + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + for ( auto& i : data ) { + EXPECT_EQ( i.nDisposeCount, 1u ); + } + + } + + //void SetUp() + //{} + + //void TearDown() + //{} + }; + + +//************************************************************ +// striped base hook + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_unordered ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 > > item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_unordered ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 0 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::base_hook< ci::hopscotch_hashset::probeset_type< item_type::probeset_type >> hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_ordered_cmp ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 6, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_ordered_cmp ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<8>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + , ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_ordered_less ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + , ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_ordered_less ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_ordered_cmpmix ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_ordered_cmpmix ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_ordered_stat ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_ordered_stat ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_unordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_unordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_basehook_ordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_basehook_ordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + +//************************************************************ +// striped member hook + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_unordered ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 > > item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember )> hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_unordered ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 0 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type >> hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_ordered_cmp ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 6, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_ordered_cmp ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<8>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + , ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_ordered_less ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + , ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_ordered_less ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_ordered_cmpmix ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_ordered_cmpmix ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_ordered_stat ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_ordered_stat ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_unordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_unordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_list_memberhook_ordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, striped_vector_memberhook_ordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > > + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + +//************************************************************ +// refinable base hook + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_unordered ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 > > item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_unordered ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 0 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef ci::hopscotch_hashset::base_hook< ci::hopscotch_hashset::probeset_type< item_type::probeset_type >> hook; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_ordered_cmp ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 6, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_ordered_cmp ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<8>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + , ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_ordered_less ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_ordered_less ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_ordered_cmpmix ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_ordered_cmpmix ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_ordered_stat ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_ordered_stat ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_unordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_unordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_basehook_ordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > > + ,ci::opt::mutex_policy> + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_basehook_ordered_storehash ) + { + typedef base_class::base_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::base_hook< + ci::hopscotch_hashset::probeset_type< item_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::hash_array_size > + > > + ,ci::opt::mutex_policy> + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + +//************************************************************ +// refinable member hook + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_unordered ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 > > item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember )> hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_unordered ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 0 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type >> hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_ordered_cmp ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 6, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_ordered_cmp ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<8>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_ordered_less ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 4, ht ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_ordered_less ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_ordered_cmpmix ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_ordered_cmpmix ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_ordered_stat ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_ordered_stat ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 0 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + > > + ,ci::opt::mutex_policy> + ,ci::opt::hash< std::tuple< hash1, hash2 > > + ,ci::opt::less< less > + ,ci::opt::compare< cmp > + ,ci::opt::stat< ci::hopscotch_hashset::stat > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_unordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s; + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_unordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<4>, 2 >> item_type; + struct set_traits: public ci::hopscotch_hashset::traits + { + typedef ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > hook; + typedef ci::hopscotch_hashset::refinable<> mutex_policy; + typedef cds::opt::hash_tuple< hash1, hash2 > hash; + typedef base_class::equal_to equal_to; + typedef mock_disposer disposer; + typedef ci::hopscotch_hashset::stat stat; + }; + typedef ci::HopscotchHashset< item_type, set_traits > set_type; + + std::vector< typename set_type::value_type > data; + { + set_type s( 32, 4 ); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_list_memberhook_ordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::list, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > > + ,ci::opt::mutex_policy> + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( 32, 6, 0, std::move( ht )); + test( s, data ); + } + } + + TEST_F( IntrusiveHopscotchHashset, refinable_vector_memberhook_ordered_storehash ) + { + typedef base_class::member_int_item< ci::hopscotch_hashset::node< ci::hopscotch_hashset::vector<6>, 2 >> item_type; + + typedef ci::HopscotchHashset< item_type + ,ci::hopscotch_hashset::make_traits< + ci::opt::hook< ci::hopscotch_hashset::member_hook< offsetof( item_type, hMember ), + ci::hopscotch_hashset::probeset_type< item_type::member_type::probeset_type > + ,ci::hopscotch_hashset::store_hash< item_type::member_type::hash_array_size > + > > + ,ci::opt::mutex_policy> + ,cds::opt::hash< std::tuple< hash1, hash2 > > + ,cds::opt::less< less > + ,cds::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > set_type; + + std::vector< typename set_type::value_type > data; + { + typename set_type::hash_tuple_type ht; + set_type s( std::move( ht )); + test( s, data ); + } + } + +} // namespace From 737548934159550ac819243f02555a2af1ab1cd3 Mon Sep 17 00:00:00 2001 From: Dron642 Date: Sun, 14 Jan 2018 21:41:50 +0300 Subject: [PATCH 19/19] some algorithm corrections --- cds/container/hopscotch_hashmap.h | 1397 +++++++++++++---------------- projects/Win/vc141/cds.sln | 48 +- 2 files changed, 652 insertions(+), 793 deletions(-) diff --git a/cds/container/hopscotch_hashmap.h b/cds/container/hopscotch_hashmap.h index f2bb9ac9c..6133d421c 100644 --- a/cds/container/hopscotch_hashmap.h +++ b/cds/container/hopscotch_hashmap.h @@ -1,772 +1,631 @@ /* - This file is a part of libcds - Concurrent Data Structures library - - (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/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* concurrent_hopscotch_hash_set.h +* +* Created on: 23.12.2017 +* Original idea by Maurice Herlihy, Nir Shavit, Moran Tzafrir: http://people.csail.mit.edu/shanir/publications/disc2008_submission_98.pdf +* Based on implementation by Sathya Hariesh Prakash, Royston Monteiro, Mohan Pandiyan: https://github.com/harieshsathya/Hopscotch-Hashing +* +* This version modifiers: Leonid Skorospelov, Andrey Chulanov, Roman Stetskevich */ -#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H -#define CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H - -#include -#include - -namespace cds { namespace container { - - //@cond - namespace details { - template - struct make_hopscotch_hashmap - { - typedef Key key_type; ///< key type - typedef T mapped_type; ///< type of value stored in the map - typedef std::pair value_type; ///< Pair type - - typedef Traits original_traits; - typedef typename original_traits::probeset_type probeset_type; - static bool const store_hash = original_traits::store_hash; - static unsigned int const store_hash_count = store_hash ? ((unsigned int) std::tuple_size< typename original_traits::hash::hash_tuple_type >::value) : 0; - - struct node_type: public intrusive::hopscotch_hashset::node - { - value_type m_val; - - template - node_type( K const& key ) - : m_val( std::make_pair( key_type(key), mapped_type())) - {} - - template - node_type( K const& key, Q const& v ) - : m_val( std::make_pair( key_type(key), mapped_type(v))) - {} - - template - node_type( K&& key, Args&&... args ) - : m_val( std::forward(key), std::move( mapped_type(std::forward(args)...))) - {} - }; - - struct key_accessor { - key_type const& operator()( node_type const& node ) const - { - return node.m_val.first; - } - }; - - struct intrusive_traits: public original_traits - { - typedef intrusive::hopscotch_hashset::base_hook< - cds::intrusive::hopscotch_hashset::probeset_type< probeset_type > - ,cds::intrusive::hopscotch_hashset::store_hash< store_hash_count > - > hook; - - typedef cds::intrusive::hopscotch_hashset::traits::disposer disposer; - - typedef typename std::conditional< - std::is_same< typename original_traits::equal_to, opt::none >::value - , opt::none - , cds::details::predicate_wrapper< node_type, typename original_traits::equal_to, key_accessor > - >::type equal_to; - - typedef typename std::conditional< - std::is_same< typename original_traits::compare, opt::none >::value - , opt::none - , cds::details::compare_wrapper< node_type, typename original_traits::compare, key_accessor > - >::type compare; - - typedef typename std::conditional< - std::is_same< typename original_traits::less, opt::none >::value - ,opt::none - ,cds::details::predicate_wrapper< node_type, typename original_traits::less, key_accessor > - >::type less; - - typedef opt::details::hash_list_wrapper< typename original_traits::hash, node_type, key_accessor > hash; - }; - - typedef intrusive::HopscotchHashset< node_type, intrusive_traits > type; - }; - } // namespace details - //@endcond - - /// Cuckoo hash map - /** @ingroup cds_nonintrusive_map - - Source - - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Cuckoo Hashing. Technical report" - - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming" - - About Cuckoo hashing - - [From "The Art of Multiprocessor Programming"] - Cuckoo hashing is a hashing algorithm in which a newly added item displaces any earlier item - occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set f size - N = 2k we use a two-entry array of tables, and two independent hash functions, - h0, h1: KeyRange -> 0,...,k-1 - mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set, - find(x) tests whether either table[0][h0(x)] or table[1][h1(x)] is - equal to \p x. Similarly, erase(x)checks whether \p x is in either table[0][h0(x)] - or table[1][h1(x)], ad removes it if found. - - The insert(x) successively "kicks out" conflicting items until every key has a slot. - To add \p x, the method swaps \p x with \p y, the current occupant of table[0][h0(x)]. - If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y - for the current occupant of table[1][h1(y)] in the same way. As before, if the prior value - was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables) - until it finds an empty slot. We might not find an empty slot, either because the table is full, - or because the sequence of displacement forms a cycle. We therefore need an upper limit on the - number of successive displacements we are willing to undertake. When this limit is exceeded, - we resize the hash table, choose new hash functions and start over. - - For concurrent hopscotch hashing, rather than organizing the set as a two-dimensional table of - items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set - of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm - tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set - holds no more than THRESHOLD < PROBE_SET items. While method calls are in-flight, a probe - set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items. - - In current implementation, a probe set can be defined either as a (single-linked) list - or as a fixed-sized vector, optionally ordered. - - In description above two-table hopscotch hashing (k = 2) has been considered. - We can generalize this approach for k >= 2 when we have \p k hash functions - h[0], ... h[k-1] and \p k tables table[0], ... table[k-1]. - - The search in probe set is linear, the complexity is O(PROBE_SET) . - The probe set may be ordered or not. Ordered probe set can be a little better since - the average search complexity is O(PROBE_SET/2). - However, the overhead of sorting can eliminate a gain of ordered search. - - The probe set is ordered if \p compare or \p less is specified in \p Traits - template parameter. Otherwise, the probe set is unordered and \p Traits must contain - \p equal_to predicate. - - Template arguments: - - \p Key - key type - - \p T - the type stored in the map. - - \p Traits - map traits, default is \p hopscotch_hashmap::traits. - It is possible to declare option-based set with \p hopscotch_hashmap::make_traits metafunction - result as \p Traits template argument. - - Examples - - Declares hopscotch mapping from \p std::string to struct \p foo. - For hopscotch hashing we should provide at least two hash functions: - \code - struct hash1 { - size_t operator()(std::string const& s) const - { - return cds::opt::v::hash( s ); - } - }; - - struct hash2: private hash1 { - size_t operator()(std::string const& s) const - { - size_t h = ~( hash1::operator()(s)); - return ~h + 0x9e3779b9 + (h << 6) + (h >> 2); - } - }; - \endcode - - Cuckoo-map with list-based unordered probe set and storing hash values - \code - #include - - // Declare type traits - struct my_traits: public cds::container::hopscotch_hashmap::traits - { - typedef std::equal_to< std::string > equal_to; - typedef std::tuple< hash1, hash2 > hash; - - static bool const store_hash = true; - }; - - // Declare HopscotchHashmap type - typedef cds::container::HopscotchHashmap< std::string, foo, my_traits > my_hopscotch_hashmap; - - // Equal option-based declaration - typedef cds::container::HopscotchHashmap< std::string, foo, - cds::container::hopscotch_hashmap::make_traits< - cds::opt::hash< std::tuple< hash1, hash2 > > - ,cds::opt::equal_to< std::equal_to< std::string > > - ,cds::container::hopscotch_hashmap::store_hash< true > - >::type - > opt_hopscotch_hashmap; - \endcode - - If we provide \p less functor instead of \p equal_to - we get as a result a hopscotch map with ordered probe set that may improve - performance. - Example for ordered vector-based probe-set: - - \code - #include - - // Declare type traits - // We use a vector of capacity 4 as probe-set container and store hash values in the node - struct my_traits: public cds::container::hopscotch_hashmap::traits - { - typedef std::less< std::string > less; - typedef std::tuple< hash1, hash2 > hash; - typedef cds::container::hopscotch_hashmap::vector<4> probeset_type; - - static bool const store_hash = true; - }; - - // Declare HopscotchHashmap type - typedef cds::container::HopscotchHashmap< std::string, foo, my_traits > my_hopscotch_hashmap; - - // Equal option-based declaration - typedef cds::container::HopscotchHashmap< std::string, foo, - cds::container::hopscotch_hashmap::make_traits< - cds::opt::hash< std::tuple< hash1, hash2 > > - ,cds::opt::less< std::less< std::string > > - ,cds::container::hopscotch_hashmap::probeset_type< cds::container::hopscotch_hashmap::vector<4> > - ,cds::container::hopscotch_hashmap::store_hash< true > - >::type - > opt_hopscotch_hashmap; - \endcode - - */ - template - class HopscotchHashmap: -#ifdef CDS_DOXYGEN_INVOKED - protected intrusive::HopscotchHashset< std::pair< Key const, T>, Traits> -#else - protected details::make_hopscotch_hashmap::type -#endif - { - //@cond - typedef details::make_hopscotch_hashmap maker; - typedef typename maker::type base_class; - //@endcond - public: - typedef Key key_type; ///< key type - typedef T mapped_type; ///< value type stored in the container - typedef std::pair value_type; ///< Key-value pair type stored in the map - typedef Traits traits; ///< Map traits - - typedef typename traits::hash hash; ///< hash functor tuple wrapped for internal use - typedef typename base_class::hash_tuple_type hash_tuple_type; ///< hash tuple type - - typedef typename base_class::mutex_policy mutex_policy; ///< Concurrent access policy, see \p hopscotch_hashmap::traits::mutex_policy - typedef typename base_class::stat stat; ///< internal statistics type - - static bool const c_isSorted = base_class::c_isSorted; ///< whether the probe set should be ordered - static size_t const c_nArity = base_class::c_nArity; ///< the arity of hopscotch_hashmap hashing: the number of hash functors provided; minimum 2. - - typedef typename base_class::key_equal_to key_equal_to; ///< Key equality functor; used only for unordered probe-set - - typedef typename base_class::key_comparator key_comparator; ///< key comparing functor based on opt::compare and opt::less option setter. Used only for ordered probe set - - typedef typename base_class::allocator allocator; ///< allocator type used for internal bucket table allocations - - /// Node allocator type - typedef typename std::conditional< - std::is_same< typename traits::node_allocator, opt::none >::value, - allocator, - typename traits::node_allocator - >::type node_allocator; - - /// item counter type - typedef typename traits::item_counter item_counter; - - protected: - //@cond - typedef typename base_class::scoped_cell_lock scoped_cell_lock; - typedef typename base_class::scoped_full_lock scoped_full_lock; - typedef typename base_class::scoped_resize_lock scoped_resize_lock; - typedef typename maker::key_accessor key_accessor; - - typedef typename base_class::value_type node_type; - typedef cds::details::Allocator< node_type, node_allocator > cxx_node_allocator; - //@endcond - - public: - static unsigned int const c_nDefaultProbesetSize = base_class::c_nDefaultProbesetSize; ///< default probeset size - static size_t const c_nDefaultInitialSize = base_class::c_nDefaultInitialSize; ///< default initial size - static unsigned int const c_nRelocateLimit = base_class::c_nRelocateLimit; ///< Count of attempts to relocate before giving up - - protected: - //@cond - template - static node_type * alloc_node( K const& key ) - { - return cxx_node_allocator().New( key ); - } - template - static node_type * alloc_node( K&& key, Args&&... args ) - { - return cxx_node_allocator().MoveNew( std::forward( key ), std::forward(args)... ); - } - - static void free_node( node_type * pNode ) - { - cxx_node_allocator().Delete( pNode ); - } - //@endcond - - protected: - //@cond - struct node_disposer { - void operator()( node_type * pNode ) - { - free_node( pNode ); - } - }; - - typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; - - //@endcond - - public: - /// Default constructor - /** - Initial size = \ref c_nDefaultInitialSize - - Probe set size: - - \ref c_nDefaultProbesetSize if \p probeset_type is \p hopscotch_hashmap::list - - \p Capacity if \p probeset_type is hopscotch_hashmap::vector - - Probe set threshold = probe set size - 1 - */ - HopscotchHashmap() - {} - - /// Constructs an object with given probe set size and threshold - /** - If probe set type is hopscotch_hashmap::vector vector - then \p nProbesetSize should be equal to vector's \p Capacity. - */ - HopscotchHashmap( - size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize - , unsigned int nProbesetSize ///< probe set size - , unsigned int nProbesetThreshold = 0 ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 - ) - : base_class( nInitialSize, nProbesetSize, nProbesetThreshold ) - {} - - /// Constructs an object with given hash functor tuple - /** - The probe set size and threshold are set as default, see HopscotchHashmap() - */ - HopscotchHashmap( - hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity - ) - : base_class( h ) - {} - - /// Constructs a map with given probe set properties and hash functor tuple - /** - If probe set type is hopscotch_hashmap::vector vector - then \p nProbesetSize should be equal to vector's \p Capacity. - */ - HopscotchHashmap( - size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize - , unsigned int nProbesetSize ///< probe set size - , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 - , hash_tuple_type const& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity - ) - : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, h ) - {} - - /// Constructs a map with given hash functor tuple (move semantics) - /** - The probe set size and threshold are set as default, see HopscotchHashmap() - */ - HopscotchHashmap( - hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity - ) - : base_class( std::forward(h)) - {} - - /// Constructs a map with given probe set properties and hash functor tuple (move semantics) - /** - If probe set type is hopscotch_hashmap::vector vector - then \p nProbesetSize should be equal to vector's \p Capacity. - */ - HopscotchHashmap( - size_t nInitialSize ///< Initial map size; if 0 - use default initial size \ref c_nDefaultInitialSize - , unsigned int nProbesetSize ///< probe set size - , unsigned int nProbesetThreshold ///< probe set threshold, nProbesetThreshold < nProbesetSize. If 0, nProbesetThreshold = nProbesetSize - 1 - , hash_tuple_type&& h ///< hash functor tuple of type std::tuple where n == \ref c_nArity - ) - : base_class( nInitialSize, nProbesetSize, nProbesetThreshold, std::forward(h)) - {} - - /// Destructor clears the map - ~HopscotchHashmap() - { - clear(); - } - - public: - /// Inserts new node with key and default value - /** - The function creates a node with \p key and default value, and then inserts the node created into the map. - - Preconditions: - - The \ref key_type should be constructible from a value of type \p K. - In trivial case, \p K is equal to \ref key_type. - - The \ref mapped_type should be default-constructible. - - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool insert( K const& key ) - { - return insert_with( key, [](value_type&){} ); - } - - /// Inserts new node - /** - The function creates a node with copy of \p val value - and then inserts the node created into the map. - - Preconditions: - - The \ref key_type should be constructible from \p key of type \p K. - - The \ref value_type should be constructible from \p val of type \p V. - - Returns \p true if \p val is inserted into the set, \p false otherwise. - */ - template - bool insert( K const& key, V const& val ) - { - return insert_with( key, [&val](value_type& item) { item.second = val ; } ); - } - - /// Inserts new node and initialize it by a functor - /** - This function inserts new node with key \p key and if inserting is successful then it calls - \p func functor with signature - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - - The argument \p item of user-defined functor \p func is the reference - to the map's item inserted: - - item.first is a const reference to item's key that cannot be changed. - - item.second is a reference to item's value that may be changed. - - The key_type should be constructible from value of type \p K. - - The function allows to split creating of new item into two part: - - create item from \p key; - - insert new item into the map; - - if inserting is successful, initialize the value of item by calling \p func functor - - This can be useful if complete initialization of object of \p value_type is heavyweight and - it is preferable that the initialization should be completed only if inserting is successful. - */ - template - bool insert_with( const K& key, Func func ) - { - scoped_node_ptr pNode( alloc_node( key )); - if ( base_class::insert( *pNode, [&func]( node_type& item ) { func( item.m_val ); } )) { - pNode.release(); - return true; - } - return false; - } - - /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... - /** - Returns \p true if inserting successful, \p false otherwise. - */ - template - bool emplace( K&& key, Args&&... args ) - { - scoped_node_ptr pNode( alloc_node( std::forward(key), std::forward(args)... )); - if ( base_class::insert( *pNode )) { - pNode.release(); - return true; - } - return false; - } - - /// Updates the node - /** - The operation performs inserting or changing data with lock-free manner. - - If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. - Otherwise, the functor \p func is called with item found. - The functor \p func signature is: - \code - struct my_functor { - void operator()( bool bNew, value_type& item ); - }; - \endcode - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - an item of the map for \p key - - Returns std::pair where \p first is \p true if operation is successful, - i.e. the node has been inserted or updated, - \p second is \p true if new item has been added or \p false if the item with \p key - already exists. - */ - template - std::pair update( K const& key, Func func, bool bAllowInsert = true ) - { - scoped_node_ptr pNode( alloc_node( key )); - std::pair res = base_class::update( *pNode, - [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_val ); }, - bAllowInsert - ); - if ( res.first && res.second ) - pNode.release(); - return res; - } - //@cond - template - CDS_DEPRECATED("ensure() is deprecated, use update()") - std::pair ensure( K const& key, Func func ) - { - return update( key, func, true ); - } - //@endcond - - /// Delete \p key from the map - /** \anchor cds_nonintrusive_HopscotchHashmap_erase_val - - Return \p true if \p key is found and deleted, \p false otherwise - */ - template - bool erase( K const& key ) - { - node_type * pNode = base_class::erase(key); - if ( pNode ) { - free_node( pNode ); - return true; - } - return false; - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_erase_val "erase(Q const&)" - but \p pred is used for key comparing. - If hopscotch map is ordered, then \p Predicate should have the interface and semantics like \p std::less. - If hopscotch map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. - \p Predicate must imply the same element order as the comparator used for building the map. - */ - template - bool erase_with( K const& key, Predicate pred ) - { - CDS_UNUSED( pred ); - node_type * pNode = base_class::erase_with(key, cds::details::predicate_wrapper()); - if ( pNode ) { - free_node( pNode ); - return true; - } - return false; - } - - /// Delete \p key from the map - /** \anchor cds_nonintrusive_HopscotchHashmap_erase_func - - The function searches an item with key \p key, calls \p f functor - and deletes the item. If \p key is not found, the functor is not called. - - The functor \p Func interface: - \code - struct extractor { - void operator()(value_type& item) { ... } - }; - \endcode - - Return \p true if key is found and deleted, \p false otherwise - - See also: \ref erase - */ - template - bool erase( K const& key, Func f ) - { - node_type * pNode = base_class::erase( key ); - if ( pNode ) { - f( pNode->m_val ); - free_node( pNode ); - return true; - } - return false; - } - - /// Deletes the item from the list using \p pred predicate for searching - /** - The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_erase_func "erase(Q const&, Func)" - but \p pred is used for key comparing. - If hopscotch map is ordered, then \p Predicate should have the interface and semantics like \p std::less. - If hopscotch map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. - \p Predicate must imply the same element order as the comparator used for building the map. - */ - template - bool erase_with( K const& key, Predicate pred, Func f ) - { - CDS_UNUSED( pred ); - node_type * pNode = base_class::erase_with( key, cds::details::predicate_wrapper()); - if ( pNode ) { - f( pNode->m_val ); - free_node( pNode ); - return true; - } - return false; - } - - /// Find the key \p key - /** \anchor cds_nonintrusive_HopscotchHashmap_find_func - - The function searches the item with key equal to \p key and calls the functor \p f for item found. - The interface of \p Func functor is: - \code - struct functor { - void operator()( value_type& item ); - }; - \endcode - where \p item is the item found. - - The functor may change \p item.second. - - The function returns \p true if \p key is found, \p false otherwise. - */ - template - bool find( K const& key, Func f ) - { - return base_class::find( key, [&f](node_type& item, K const& ) { f( item.m_val );}); - } - - /// Find the key \p val using \p pred predicate for comparing - /** - The function is an analog of \ref cds_nonintrusive_HopscotchHashmap_find_func "find(K const&, Func)" - but \p pred is used for key comparison. - If you use ordered hopscotch map, then \p Predicate should have the interface and semantics like \p std::less. - If you use unordered hopscotch map, then \p Predicate should have the interface and semantics like \p std::equal_to. - \p pred must imply the same element order as the comparator used for building the map. - */ - template - bool find_with( K const& key, Predicate pred, Func f ) - { - CDS_UNUSED( pred ); - return base_class::find_with( key, cds::details::predicate_wrapper(), - [&f](node_type& item, K const& ) { f( item.m_val );}); - } - - /// Checks whether the map contains \p key - /** - The function searches the item with key equal to \p key - and returns \p true if it is found, and \p false otherwise. - */ - template - bool contains( K const& key ) - { - return base_class::contains( key ); - } - //@cond - template - CDS_DEPRECATED("the function is deprecated, use contains()") - bool find( K const& key ) - { - return contains( key ); - } - //@endcond - - /// Checks whether the map contains \p key using \p pred predicate for searching - /** - The function is similar to contains( key ) but \p pred is used for key comparing. - \p Less functor has the interface like \p std::less. - \p Less must imply the same element order as the comparator used for building the map. - */ - template - bool contains( K const& key, Predicate pred ) - { - CDS_UNUSED( pred ); - return base_class::contains( key, cds::details::predicate_wrapper()); - } - //@cond - template - CDS_DEPRECATED("the function is deprecated, use contains()") - bool find_with( K const& key, Predicate pred ) - { - return contains( key, pred ); - } - //@endcond - - /// Clears the map - void clear() - { - base_class::clear_and_dispose( node_disposer()); - } - - /// Checks if the map is empty - /** - Emptiness is checked by item counting: if item count is zero then the map is empty. - */ - bool empty() const - { - return base_class::empty(); - } - - /// Returns item count in the map - size_t size() const - { - return base_class::size(); - } - - /// Returns the size of hash table - /** - The hash table size is non-constant and can be increased via resizing. - */ - size_t bucket_count() const - { - return base_class::bucket_count(); - } - - /// Returns lock array size - /** - The lock array size is constant. - */ - size_t lock_count() const - { - return base_class::lock_count(); - } - - /// Returns const reference to internal statistics - stat const& statistics() const - { - return base_class::statistics(); - } - - /// Returns const reference to mutex policy internal statistics - typename mutex_policy::statistics_type const& mutex_policy_statistics() const - { - return base_class::mutex_policy_statistics(); - } - }; -}} // namespace cds::container - -#endif //#ifndef CDSLIB_CONTAINER_HOPSCOTCH_HASHMAP_H +#ifndef HOPSCOTCHHASHMAP_H_ +#define HOPSCOTCHHASHMAP_H_ +#include +#include +#include +#include +#include + +namespace cds { + namespace container { + template + struct hopscotch_hashmap { + + typedef KEY key_type; ///< key type + typedef DATA mapped_type; ///< type of value stored in the map + typedef std::pair value_type; ///< Pair type + + typedef cds::atomicity::item_counter item_counter; + item_counter m_item_counter; + + private: + static const int HOP_RANGE = 32; + static const int ADD_RANGE = 256; + static const int MAX_SEGMENTS = 1048576; + //static const int MAX_TRIES = 2; + KEY* BUSY; + DATA* BUSYD; + struct Bucket { + + static const unsigned int _empty_hop_info = 0; + KEY* _empty_key = NULL; + DATA* _empty_data = NULL; + + std::atomic _hop_info; + KEY* _key; + DATA* _data; + std::atomic _lock; + std::atomic _timestamp; + std::mutex lock_mutex; + + Bucket() { + _hop_info = _empty_hop_info; + _lock = 0; + _key = _empty_key; + _data = _empty_data; + _timestamp = 0; + } + + void lock() { + lock_mutex.lock(); + while (1) { + if (_lock == 0) { + _lock = 1; + lock_mutex.unlock(); + break; + } + } + } + + void unlock() { + lock_mutex.lock(); + _lock = 0; + lock_mutex.unlock(); + } + + }; + + Bucket* segments_arys; + + template + std::size_t calc_hash(K const& key) { + std::hash hash_fn; + return hash_fn(key) % MAX_SEGMENTS; + } + + void resize() { + // TODO need to find out if we need one + } + + public: + static bool const c_isSorted = false; ///< whether the probe set should be ordered + + hopscotch_hashmap() { + segments_arys = new Bucket[MAX_SEGMENTS + ADD_RANGE]; + m_item_counter.reset(); + BUSY = (KEY*)std::malloc(sizeof(KEY)); + } + + ~hopscotch_hashmap() { + std::free(BUSY); + std::free(segments_arys); + } + + bool empty() { + return size() == 0; + } + + int size() { + return m_item_counter; + /* + unsigned int counter = 0; + const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); + for (unsigned int iElm = 0; iElm < num_elm; ++iElm) { + if (Bucket::_empty_hop_info != (segments_arys + iElm)->_hop_info) { + counter += __popcnt((segments_arys + iElm)->_hop_info); + } + } + return counter; + */ + } + + /// Checks whether the map contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains(K const& key) + { + cds_test::striped_map_fixture::cmp cmp = cds_test::striped_map_fixture::cmp(); + return find_with(key, [&](K const& one, K const& two) { return cmp(one,two); }, [](mapped_type&) {}); + } + + /// Checks whether the map contains \p key using \p pred predicate for searching + /** + The function is similar to contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the map. + */ + template + bool contains(K const& key, Predicate pred) + { + return find_with(key, pred, [](mapped_type&) {}); + } + + /// Find the key \p key + /** \anchor cds_nonintrusive_CuckooMap_find_func + + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + where \p item is the item found. + + The functor may change \p item.second. + + The function returns \p true if \p key is found, \p false otherwise. + */ + template + bool find_with(K const& key, Func f) + { + cds_test::striped_map_fixture::cmp cmp = cds_test::striped_map_fixture::cmp(); + return find_with(key, [&](K const& one, K const& two) { return cmp(one, two); }, f); + } + + template + bool find(K const& key, Predicate pred) + { + return find_with(key, pred, [](mapped_type&) {}); + } + + template + bool find(K const& key) + { + cds_test::striped_map_fixture::cmp cmp = cds_test::striped_map_fixture::cmp(); + return find_with(key, [&](K const& one, K const& two) { return cmp(one, two); }, [](mapped_type&) {}); + } + + template + bool find_with(K const& key, Predicate pred, Func f) + { + std::size_t hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + std::size_t timestamp; + do { + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + Bucket* check_bucket = start_bucket; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (pred(key, *(check_bucket->_key)) == 0) { + check_bucket->lock(); + if (pred(key, *(check_bucket->_key)) == 0) { + f(std::make_pair(*(check_bucket->_key), *(check_bucket->_data))); + check_bucket->unlock(); + return true; + } + else { + check_bucket->unlock(); + ++check_bucket; + continue; + } + + } + } + ++check_bucket; + } + } while (timestamp != start_bucket->_timestamp); + + return false; + } + + /// For key \p key inserts data of type \ref value_type constructed with std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace(K&& key, Args&&... args) + { + return insert(std::forward(key), mapped_type(std::forward(args)...)); + } + + /// Clears the map + void clear() + { + const unsigned int num_elm(MAX_SEGMENTS + ADD_RANGE); + Bucket *start = segments_arys; + for (unsigned int iElm = 0; iElm < num_elm; ++iElm, ++start) { + start->lock(); + } + m_item_counter.reset(); + segments_arys = (Bucket *)memset(segments_arys, 0, (MAX_SEGMENTS + ADD_RANGE) * sizeof(Bucket)); + } + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update(K const& key, Func func, bool bAllowInsert = true) + { + mapped_type def_val; + return update(key, def_val, func, bAllowInsert); + } + + template + std::pair update(K const& key, V const& val) + { + return update(key, val, [](V const&) {}); + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If \p key is not found in the map, then \p key is inserted iff \p bAllowInsert is \p true. + Otherwise, the functor \p func is called with item found. + The functor \p func signature is: + \code + struct my_functor { + void operator()( bool bNew, value_type& item ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - an item of the map for \p key + + Returns std::pair where \p first is \p true if operation is successful, + i.e. the node has been inserted or updated, + \p second is \p true if new item has been added or \p false if the item with \p key + already exists. + */ + template + std::pair update(K const& key, V const& val, Func func, bool bAllowInsert = true) + { + bool inserted = false; + + if (!contains(key)) { + if (bAllowInsert) { + if (insert(key, val)) { + inserted = true; + } + else { + return make_pair(false, false); + } + } + else { + return make_pair(false, false); + } + } + + std::size_t hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + std::size_t timestamp; + do { + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + Bucket* check_bucket = start_bucket; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (key == *(check_bucket->_key)) { + *(check_bucket->_data) = val; + func(*(check_bucket->_data)); + return make_pair(true, inserted); + } + } + ++check_bucket; + } + ++try_counter; + } while (timestamp != start_bucket->_timestamp); + + return make_pair(false, inserted); + } + + template + V get(K const& key) { + cds_test::striped_map_fixture::cmp cmp = cds_test::striped_map_fixture::cmp(); + return get(key, [&](K const& one, K const& two) { return cmp(one, two); }); + } + + template + V get(K const& key, Pred cmp) { + std::size_t hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + std::size_t timestamp; + V found_value; + do { + timestamp = start_bucket->_timestamp; + unsigned int hop_info = start_bucket->_hop_info; + Bucket* check_bucket = start_bucket; + unsigned int temp; + for (int i = 0; i < HOP_RANGE; i++) { + temp = hop_info; + temp = temp >> i; + + if (temp & 1) { + if (cmp(key, *(check_bucket->_key)) == 0) { + check_bucket->lock(); + if (cmp(key, *(check_bucket->_key)) == 0) { + found_value = *(check_bucket->_data); + check_bucket->unlock(); + return found_value; + } + else { + check_bucket->unlock(); + ++check_bucket; + continue; + } + } + } + ++check_bucket; + } + } while (timestamp != start_bucket->_timestamp); + + return NULL; + } + + /// Inserts new node and initialize it by a functor + /** + This function inserts new node with key \p key and if inserting is successful then it calls + \p func functor with signature + \code + struct functor { + void operator()( value_type& item ); + }; + \endcode + + The argument \p item of user-defined functor \p func is the reference + to the map's item inserted: + - item.first is a const reference to item's key that cannot be changed. + - item.second is a reference to item's value that may be changed. + + The key_type should be constructible from value of type \p K. + + The function allows to split creating of new item into two part: + - create item from \p key; + - insert new item into the map; + - if inserting is successful, initialize the value of item by calling \p func functor + + This can be useful if complete initialization of object of \p value_type is heavyweight and + it is preferable that the initialization should be completed only if inserting is successful. + */ + template + bool insert_with(const K& key, Func func) + { + mapped_type def_val; + return insert_with(key, def_val, func); + } + + /// Inserts new node + /** + The function creates a node with copy of \p val value + and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from \p key of type \p K. + - The \ref value_type should be constructible from \p val of type \p V. + + Returns \p true if \p val is inserted into the set, \p false otherwise. + */ + template + bool insert_with(K const& key, V const& val, Func func) + { + int tmp_val = 1; + std::size_t hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + if (contains(key)) { + start_bucket->unlock(); + return false; + } + + Bucket* free_bucket = start_bucket; + int free_distance = 0; + for (; free_distance < ADD_RANGE; ++free_distance) { + std::atomic _atomic = free_bucket->_key; + K* _null_key = Bucket::_empty_key; + if (_null_key == free_bucket->_key && _atomic.compare_exchange_strong(_null_key, BUSY)) { + break; + } + ++free_bucket; + } + + if (free_distance < ADD_RANGE) { + do { + if (free_distance < HOP_RANGE) { + start_bucket->_hop_info |= (1 << free_distance); + *(free_bucket->_data) = val; + *(free_bucket->_key) = key; + ++m_item_counter; + start_bucket->unlock(); + func(*(free_bucket->_data)); + return true; + } + find_closer_bucket(&free_bucket, &free_distance, tmp_val); + } while (0 != tmp_val); + } + start_bucket->unlock(); + + this->resize(); + + return false; + } + + /// Inserts new node with key and default value + /** + The function creates a node with \p key and default value, and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from a value of type \p K. + In trivial case, \p K is equal to \ref key_type. + - The \ref mapped_type should be default-constructible. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert(K const& key) + { + mapped_type def_data; + return insert_with(key, def_data, [](mapped_type&) {}); + } + + /// Inserts new node with key and default value + /** + The function creates a node with \p key and \p value, and then inserts the node created into the map. + + Preconditions: + - The \ref key_type should be constructible from a value of type \p K. + In trivial case, \p K is equal to \ref key_type. + + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool insert(K const& key, V const& val) + { + return insert_with(key, val, [](mapped_type&) {}); + } + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_CuckooMap_erase_val + + Return \p true if \p key is found and deleted, \p false otherwise + */ + template + bool erase(K const& key) + { + return erase(key, [](value_type&) {}); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_val "erase(Q const&)" + but \p pred is used for key comparing. + If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with(K const& key, Predicate pred) + { + return erase_with(key, pred, [](value_type&) {}); + } + + /// Delete \p key from the map + /** \anchor cds_nonintrusive_CuckooMap_erase_func + + The function searches an item with key \p key, calls \p f functor + and deletes the item. If \p key is not found, the functor is not called. + + The functor \p Func interface: + \code + struct extractor { + void operator()(value_type& item) { ... } + }; + \endcode + + Return \p true if key is found and deleted, \p false otherwise + + See also: \ref erase + */ + template + bool erase(K const& key, Func f) + { + cds_test::striped_map_fixture::equal_to cmp = cds_test::striped_map_fixture::equal_to(); + return erase_with(key, [&](K const& one, K const& two) { return cmp(one, two); }, [](value_type&) {}); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_nonintrusive_CuckooMap_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + If cuckoo map is ordered, then \p Predicate should have the interface and semantics like \p std::less. + If cuckoo map is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to. + \p Predicate must imply the same element order as the comparator used for building the map. + */ + template + bool erase_with(K const& key, Predicate pred, Func f) + { + size_t hash = calc_hash(key); + Bucket* start_bucket = segments_arys + hash; + start_bucket->lock(); + + unsigned int hop_info = start_bucket->_hop_info; + unsigned int mask = 1; + for (int i = 0; i < HOP_RANGE; ++i, mask <<= 1) { + if (mask & hop_info) { + Bucket* check_bucket = start_bucket + i; + if (pred(key, *(check_bucket->_key)) == 0) { + f(value_type(*(check_bucket->_key), *(check_bucket->_data))); + check_bucket->_key = NULL; + check_bucket->_data = NULL; + start_bucket->_hop_info &= ~(1 << i); + start_bucket->unlock(); + --m_item_counter; + return true; + } + } + } + start_bucket->unlock(); + return false; + } + + void find_closer_bucket(Bucket** free_bucket, int* free_distance, int &val) { + Bucket* move_bucket = *free_bucket - (HOP_RANGE - 1); + for (int free_dist = (HOP_RANGE - 1); free_dist > 0; --free_dist) { + unsigned int start_hop_info = move_bucket->_hop_info; + int move_free_distance = -1; + unsigned int mask = 1; + for (int i = 0; i < free_dist; ++i, mask <<= 1) { + if (mask & start_hop_info) { + move_free_distance = i; + break; + } + } + if (-1 != move_free_distance) { + move_bucket->lock(); + if (start_hop_info == move_bucket->_hop_info) { + Bucket* new_free_bucket = move_bucket + move_free_distance; + move_bucket->_hop_info |= (1 << free_dist); + (*free_bucket)->_data = new_free_bucket->_data; + (*free_bucket)->_key = new_free_bucket->_key; + ++(move_bucket->_timestamp); + new_free_bucket->_key = BUSY; + new_free_bucket->_data = BUSYD; + move_bucket->_hop_info &= ~(1 << move_free_distance); + *free_bucket = new_free_bucket; + *free_distance -= free_dist; + move_bucket->unlock(); + return; + } + move_bucket->unlock(); + } + ++move_bucket; + } + (*free_bucket)->_key = NULL; + val = 0; + *free_distance = 0; + } + + }; + } +} + +#endif /* HOPSCOTCHHASHMAP_H_ */ diff --git a/projects/Win/vc141/cds.sln b/projects/Win/vc141/cds.sln index 0ebd4ca16..c973efab2 100644 --- a/projects/Win/vc141/cds.sln +++ b/projects/Win/vc141/cds.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2010 +VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cds", "cds.vcxproj", "{408FE9BC-44F0-4E6A-89FA-D6F952584239}" EndProject @@ -461,10 +461,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stress-spsc-queue", "stress {408FE9BC-44F0-4E6A-89FA-D6F952584239} = {408FE9BC-44F0-4E6A-89FA-D6F952584239} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-map-hopscotch", "gtest-striped-hashmap-hopscotch.vcxproj", "{BADD532E-7C37-4877-AAA9-34DB4298C200}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-set-hopscotch", "gtest-striped-set-hopscotch.vcxproj", "{045D3046-9609-4B5B-81C3-17F5A0FEC48E}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-striped-map-hopscotch", "gtest-striped-map-hopscotch.vcxproj", "{BADD532E-7C37-4877-AAA9-34DB4298C200}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -1879,26 +1879,6 @@ Global {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 {CDC54B7B-B882-4CC8-87A2-E237D0D945B2}.vc14-Release|x64.Build.0 = vc14-Release|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.ActiveCfg = Debug|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.Build.0 = Debug|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.ActiveCfg = Debug|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.Build.0 = Debug|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.Build.0 = DebugVLD|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.ActiveCfg = Release|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.Build.0 = Release|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.ActiveCfg = Release|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.Build.0 = Release|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 - {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.Build.0 = vc14-Release|x64 {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|Win32.ActiveCfg = Debug|Win32 {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|Win32.Build.0 = Debug|Win32 {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.Debug|x64.ActiveCfg = Debug|x64 @@ -1919,6 +1899,26 @@ Global {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 {045D3046-9609-4B5B-81C3-17F5A0FEC48E}.vc14-Release|x64.Build.0 = vc14-Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.ActiveCfg = Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|Win32.Build.0 = Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.ActiveCfg = Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Debug|x64.Build.0 = Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.ActiveCfg = DebugVLD|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|Win32.Build.0 = DebugVLD|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.ActiveCfg = DebugVLD|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.DebugVLD|x64.Build.0 = DebugVLD|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.ActiveCfg = Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|Win32.Build.0 = Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.ActiveCfg = Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.Release|x64.Build.0 = Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.ActiveCfg = vc14-Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|Win32.Build.0 = vc14-Debug|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.ActiveCfg = vc14-Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Debug|x64.Build.0 = vc14-Debug|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.ActiveCfg = vc14-Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|Win32.Build.0 = vc14-Release|Win32 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.ActiveCfg = vc14-Release|x64 + {BADD532E-7C37-4877-AAA9-34DB4298C200}.vc14-Release|x64.Build.0 = vc14-Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2006,8 +2006,8 @@ Global {EACBF8EC-C58F-4CC8-9435-21BE9812287A} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {47B9F1F9-C9F1-4F25-842B-EE8E6447FE50} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} {CDC54B7B-B882-4CC8-87A2-E237D0D945B2} = {7DDE0A26-F02B-44FE-9206-552E39BC4A55} - {BADD532E-7C37-4877-AAA9-34DB4298C200} = {DE3B8DBB-5B4B-40DC-80DB-F0C15BC21E0F} {045D3046-9609-4B5B-81C3-17F5A0FEC48E} = {E0C742CB-A36A-4617-8A06-DD4820FEB8ED} + {BADD532E-7C37-4877-AAA9-34DB4298C200} = {DE3B8DBB-5B4B-40DC-80DB-F0C15BC21E0F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3673AF8B-E81E-49E1-8241-B5FC94791FB8}