Added support for multiset.

Minor fixes to tests.

MultiIterator & MultiMap work.
There are some unfinished optimizations in the code commented.

All tests pass. Some common parts from tests have been moved.
This commit is contained in:
Kruno Tomola Fabro 2016-08-02 13:23:39 +01:00
parent bf174644de
commit e16b576a6e
28 changed files with 2080 additions and 960 deletions

View File

@ -54,7 +54,7 @@ IncludeCategories:
- Regex: '.*'
Priority: 1
IndentCaseLabels: false
IndentWidth: 2
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''

View File

@ -0,0 +1,94 @@
#pragma once
#include "data_structures/concurrent/skiplist.hpp"
#include "utils/total_ordering.hpp"
using std::pair;
template <typename K, typename T>
class Item : public TotalOrdering<Item<K, T>>,
public TotalOrdering<K, Item<K, T>>,
public TotalOrdering<Item<K, T>, K>,
public pair<const K, T>
{
public:
using pair<const K, T>::pair;
friend constexpr bool operator<(const Item &lhs, const Item &rhs)
{
std::pair<const K, T> *a;
return lhs.first < rhs.first;
}
friend constexpr bool operator==(const Item &lhs, const Item &rhs)
{
return lhs.first == rhs.first;
}
friend constexpr bool operator<(const K &lhs, const Item &rhs)
{
return lhs < rhs.first;
}
friend constexpr bool operator==(const K &lhs, const Item &rhs)
{
return lhs == rhs.first;
}
friend constexpr bool operator<(const Item &lhs, const K &rhs)
{
return lhs.first < rhs;
}
friend constexpr bool operator==(const Item &lhs, const K &rhs)
{
return lhs.first == rhs;
}
};
template <typename T>
class AccessorBase
{
typedef SkipList<T> list;
typedef typename SkipList<T>::Iterator list_it;
typedef typename SkipList<T>::ConstIterator list_it_con;
protected:
AccessorBase(list *skiplist) : accessor(skiplist->access()) {}
public:
AccessorBase(const AccessorBase &) = delete;
AccessorBase(AccessorBase &&other) : accessor(std::move(other.accessor)) {}
~AccessorBase() {}
list_it begin() { return accessor.begin(); }
list_it_con begin() const { return accessor.cbegin(); }
list_it_con cbegin() const { return accessor.cbegin(); }
list_it end() { return accessor.end(); }
list_it_con end() const { return accessor.cend(); }
list_it_con cend() const { return accessor.cend(); }
template <class K>
typename SkipList<T>::template MultiIterator<K> end(const K &data)
{
return accessor.template mend<K>(data);
}
template <class K>
typename SkipList<T>::template MultiIterator<K> mend(const K &data)
{
return accessor.template mend<K>(data);
}
size_t size() const { return accessor.size(); }
protected:
typename list::Accessor accessor;
};

View File

@ -1,100 +1,45 @@
#pragma once
#include "data_structures/concurrent/common.hpp"
#include "data_structures/concurrent/skiplist.hpp"
#include "utils/total_ordering.hpp"
using std::pair;
template <typename K, typename T>
class ConcurrentMap
{
class Item : public TotalOrdering<Item>,
public TotalOrdering<K, Item>,
public TotalOrdering<Item, K>,
public pair<const K, T>
{
public:
using pair<const K, T>::pair;
friend constexpr bool operator<(const Item &lhs, const Item &rhs)
{
std::pair<const K, T> *a;
return lhs.first < rhs.first;
}
friend constexpr bool operator==(const Item &lhs, const Item &rhs)
{
return lhs.first == rhs.first;
}
friend constexpr bool operator<(const K &lhs, const Item &rhs)
{
return lhs < rhs.first;
}
friend constexpr bool operator==(const K &lhs, const Item &rhs)
{
return lhs == rhs.first;
}
friend constexpr bool operator<(const Item &lhs, const K &rhs)
{
return lhs.first < rhs;
}
friend constexpr bool operator==(const Item &lhs, const K &rhs)
{
return lhs.first == rhs;
}
};
typedef SkipList<Item> list;
typedef typename SkipList<Item>::Iterator list_it;
typedef typename SkipList<Item>::ConstIterator list_it_con;
typedef Item<K, T> item_t;
typedef SkipList<item_t> list;
typedef typename SkipList<item_t>::Iterator list_it;
typedef typename SkipList<item_t>::ConstIterator list_it_con;
public:
ConcurrentMap() {}
friend class Accessor;
class Accessor
class Accessor : public AccessorBase<item_t>
{
friend class ConcurrentMap;
Accessor(list *skiplist) : accessor(skiplist->access()) {}
using AccessorBase<item_t>::AccessorBase;
private:
using AccessorBase<item_t>::accessor;
public:
Accessor(const Accessor &) = delete;
Accessor(Accessor &&other) : accessor(std::move(other.accessor)) {}
~Accessor() {}
list_it begin() { return accessor.begin(); }
list_it_con begin() const { return accessor.cbegin(); }
list_it_con cbegin() const { return accessor.cbegin(); }
list_it end() { return accessor.end(); }
list_it_con end() const { return accessor.cend(); }
list_it_con cend() const { return accessor.cend(); }
std::pair<list_it, bool> insert(const K &key, const T &data)
{
return accessor.insert(Item(key, data));
return accessor.insert(item_t(key, data));
}
std::pair<list_it, bool> insert(const K &key, T &&data)
{
return accessor.insert(Item(key, std::forward<T>(data)));
return accessor.insert(item_t(key, std::forward<T>(data)));
}
std::pair<list_it, bool> insert(K &&key, T &&data)
{
return accessor.insert(Item(std::forward<K>(key), std::forward<T>(data)));
return accessor.insert(
item_t(std::forward<K>(key), std::forward<T>(data)));
}
list_it_con find(const K &key) const { return accessor.find(key); }
@ -104,11 +49,6 @@ public:
bool contains(const K &key) const { return this->find(key) != this->end(); }
bool remove(const K &key) { return accessor.remove(key); }
size_t size() const { return accessor.size(); }
private:
typename list::Accessor accessor;
};
Accessor access() { return Accessor(&skiplist); }

View File

@ -0,0 +1,70 @@
#pragma once
#include "data_structures/concurrent/skiplist.hpp"
#include "utils/total_ordering.hpp"
using std::pair;
template <typename K, typename T>
class ConcurrentMultiMap
{
typedef Item<K, T> item_t;
typedef SkipList<item_t> list;
typedef typename SkipList<item_t>::Iterator list_it;
typedef typename SkipList<item_t>::ConstIterator list_it_con;
typedef typename SkipList<item_t>::template MultiIterator<K> list_it_multi;
public:
ConcurrentMultiMap() {}
class Accessor : public AccessorBase<item_t>
{
friend class ConcurrentMultiMap<K, T>;
using AccessorBase<item_t>::AccessorBase;
private:
using AccessorBase<item_t>::accessor;
public:
list_it insert(const K &key, const T &data)
{
return accessor.insert_non_unique(item_t(key, data));
}
list_it insert(const K &key, T &&data)
{
return accessor.insert_non_unique(
item_t(key, std::forward<T>(data)));
}
list_it insert(K &&key, T &&data)
{
return accessor.insert_non_unique(
item_t(std::forward<K>(key), std::forward<T>(data)));
}
list_it_multi find_multi(const K &key)
{
return accessor.find_multi(key);
}
list_it_con find(const K &key) const { return accessor.find(key); }
list_it find(const K &key) { return accessor.find(key); }
bool contains(const K &key) const
{
return this->find(key) != this->end();
}
bool remove(const K &key) { return accessor.remove(key); }
};
Accessor access() { return Accessor(&skiplist); }
const Accessor access() const { return Accessor(&skiplist); }
private:
list skiplist;
};

View File

@ -0,0 +1,50 @@
#pragma once
#include "data_structures/concurrent/skiplist.hpp"
template <class T>
class ConcurrentMultiSet
{
typedef SkipList<T> list;
typedef typename SkipList<T>::Iterator list_it;
typedef typename SkipList<T>::ConstIterator list_it_con;
public:
ConcurrentMultiSet() {}
class Accessor : public AccessorBase<T>
{
friend class ConcurrentMultiSet;
using AccessorBase<T>::AccessorBase;
private:
using AccessorBase<T>::accessor;
public:
list_it insert(const T &item) { return accessor.insert_non_unique(item); }
list_it insert(T &&item)
{
return accessor.insert_non_unique(std::forward<T>(item));
}
list_it_con find(const T &item) const { return accessor.find(item); }
list_it find(const T &item) { return accessor.find(item); }
bool contains(const T &item) const
{
return this->find(item) != this->end();
}
bool remove(const T &item) { return accessor.remove(item); }
};
Accessor access() { return Accessor(&skiplist); }
const Accessor access() const { return Accessor(&skiplist); }
private:
list skiplist;
};

View File

@ -1,8 +1,54 @@
#pragma once
#include "data_structures/concurrent/common.hpp"
#include "data_structures/concurrent/skiplist.hpp"
template<class T>
class ConcurrentSet : public SkipList<T>
template <class T>
class ConcurrentSet
{
typedef SkipList<T> list;
typedef typename SkipList<T>::Iterator list_it;
typedef typename SkipList<T>::ConstIterator list_it_con;
public:
ConcurrentSet() {}
class Accessor : public AccessorBase<T>
{
friend class ConcurrentSet;
using AccessorBase<T>::AccessorBase;
private:
using AccessorBase<T>::accessor;
public:
std::pair<list_it, bool> insert(const T &item)
{
return accessor.insert(item);
}
std::pair<list_it, bool> insert(T &&item)
{
return accessor.insert(std::forward<T>(item));
}
list_it_con find(const T &item) const { return accessor.find(item); }
list_it find(const T &item) { return accessor.find(item); }
bool contains(const T &item) const
{
return this->find(item) != this->end();
}
bool remove(const T &item) { return accessor.remove(item); }
};
Accessor access() { return Accessor(&skiplist); }
const Accessor access() const { return Accessor(&skiplist); }
private:
list skiplist;
};

File diff suppressed because it is too large Load Diff

View File

@ -10,45 +10,48 @@
template <class T, class lock_t = SpinLock>
class SkiplistGC : public LazyGC<SkiplistGC<T, lock_t>, lock_t>
{
public:
// release_ref method should be called by a thread
// when the thread finish it job over object
// which has to be lazy cleaned
// if thread counter becames zero, all objects in the local_freelist
// are going to be deleted
// the only problem with this approach is that
// GC may never be called, but for now we can deal with that
void release_ref()
{
std::vector<T *> local_freelist;
// take freelist if there is no more threads
public:
// release_ref method should be called by a thread
// when the thread finish it job over object
// which has to be lazy cleaned
// if thread counter becames zero, all objects in the local_freelist
// are going to be deleted
// the only problem with this approach is that
// GC may never be called, but for now we can deal with that
void release_ref()
{
auto lock = this->acquire_unique();
--this->count;
if (this->count == 0) {
freelist.swap(local_freelist);
}
}
std::vector<T *> local_freelist;
if (local_freelist.size() > 0) {
std::cout << "GC started" << std::endl;
std::cout << "Local list size: " << local_freelist.size() << std::endl;
long long counter = 0;
// destroy all elements from local_freelist
for (auto element : local_freelist) {
if (element->flags.is_marked()) {
T::destroy(element);
counter++;
// take freelist if there is no more threads
{
auto lock = this->acquire_unique();
assert(this->count > 0);
--this->count;
if (this->count == 0) {
freelist.swap(local_freelist);
}
}
if (local_freelist.size() > 0) {
std::cout << "GC started" << std::endl;
std::cout << "Local list size: " << local_freelist.size()
<< std::endl;
long long counter = 0;
// destroy all elements from local_freelist
for (auto element : local_freelist) {
if (element->flags.is_marked()) {
T::destroy(element);
counter++;
}
}
std::cout << "Number of destroyed elements " << counter
<< std::endl;
}
}
std::cout << "Number of destroyed elements " << counter << std::endl;
}
}
void collect(T *node) { freelist.add(node); }
void collect(T *node) { freelist.add(node); }
private:
FreeList<T> freelist;
private:
FreeList<T> freelist;
};

View File

@ -10,17 +10,17 @@
template <class Derived, class lock_t = SpinLock>
class LazyGC : public Crtp<Derived>, public Lockable<lock_t>
{
public:
// add_ref method should be called by a thread
// when the thread has to do something over
// object which has to be lazy cleaned when
// the thread finish it job
void add_ref()
{
auto lock = this->acquire_unique();
++count;
}
public:
// add_ref method should be called by a thread
// when the thread has to do something over
// object which has to be lazy cleaned when
// the thread finish it job
void add_ref()
{
auto lock = this->acquire_unique();
++count;
}
protected:
size_t count{0};
protected:
size_t count{0};
};

View File

@ -2,46 +2,46 @@
const Vertex::Accessor Vertices::find(tx::Transaction &t, const Id &id)
{
auto vertices_accessor = vertices.access();
auto vertices_iterator = vertices_accessor.find(id);
auto vertices_accessor = vertices.access();
auto vertices_iterator = vertices_accessor.find(id);
if (vertices_iterator == vertices_accessor.end()) return Vertex::Accessor();
if (vertices_iterator == vertices_accessor.end()) return Vertex::Accessor();
// find vertex
auto vertex = vertices_iterator->second.find(t);
// find vertex
auto vertex = vertices_iterator->second.find(t);
if (vertex == nullptr) return Vertex::Accessor();
if (vertex == nullptr) return Vertex::Accessor();
return Vertex::Accessor(vertex, &vertices_iterator->second, this);
return Vertex::Accessor(vertex, &vertices_iterator->second, this);
}
Vertex::Accessor Vertices::insert(tx::Transaction &t)
{
// get next vertex id
auto next = counter.next();
// get next vertex id
auto next = counter.next();
// create new vertex record
VertexRecord vertex_record(next);
// vertex_record.id(next);
// create new vertex record
VertexRecord vertex_record(next);
// vertex_record.id(next);
// insert the new vertex record into the vertex store
auto vertices_accessor = vertices.access();
auto result = vertices_accessor.insert(next, std::move(vertex_record));
// insert the new vertex record into the vertex store
auto vertices_accessor = vertices.access();
auto result = vertices_accessor.insert(next, std::move(vertex_record));
// create new vertex
auto inserted_vertex_record = result.first;
auto vertex = inserted_vertex_record->second.insert(t);
// create new vertex
auto inserted_vertex_record = result.first;
auto vertex = inserted_vertex_record->second.insert(t);
return Vertex::Accessor(vertex, &inserted_vertex_record->second, this);
return Vertex::Accessor(vertex, &inserted_vertex_record->second, this);
}
void Vertices::update_label_index(const Label &label,
VertexIndexRecord &&index_record)
{
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
}
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label)
{
return label_index.find(label);
return label_index.find(label);
}

View File

@ -8,18 +8,14 @@
class SpinLock
{
public:
void lock()
{
while(lock_flag.test_and_set(std::memory_order_acquire))
{ // Before was memorz_order_acquire
while (lock_flag.test_and_set(std::memory_order_seq_cst))
cpu_relax();
///usleep(250);
}
void unlock()
{
lock_flag.clear(std::memory_order_release);
/// usleep(250);
}
// Before was memory_order_release
void unlock() { lock_flag.clear(std::memory_order_seq_cst); }
private:
// guaranteed by standard to be lock free!

View File

@ -8,6 +8,9 @@
#include <thread>
#include "data_structures/concurrent/concurrent_map.hpp"
#include "data_structures/concurrent/concurrent_multimap.hpp"
#include "data_structures/concurrent/concurrent_multiset.hpp"
#include "data_structures/concurrent/concurrent_set.hpp"
#include "data_structures/concurrent/skiplist.hpp"
#include "data_structures/static_array.hpp"
#include "utils/assert.hpp"
@ -15,133 +18,203 @@
using std::cout;
using std::endl;
using skiplist_t = ConcurrentMap<int, int>;
using map_t = ConcurrentMap<int, int>;
using set_t = ConcurrentSet<int>;
using multiset_t = ConcurrentMultiSet<int>;
using multimap_t = ConcurrentMultiMap<int, int>;
using namespace std::chrono_literals;
// Returns uniform random size_t generator from range [0,n>
auto rand_gen(size_t n)
{
std::default_random_engine generator;
std::uniform_int_distribution<size_t> distribution(0, n - 1);
return std::bind(distribution, generator);
std::default_random_engine generator;
std::uniform_int_distribution<size_t> distribution(0, n - 1);
return std::bind(distribution, generator);
}
// Returns random bool generator with distribution of 1 true for n false.
auto rand_gen_bool(size_t n = 1)
{
auto gen = rand_gen(n + 1);
return [=]() mutable { return gen() == 0; };
auto gen = rand_gen(n + 1);
return [=]() mutable { return gen() == 0; };
}
// Checks for all owned keys if there data is data.
template <typename S>
void check_present_same(typename S::Accessor &acc, size_t data,
std::vector<size_t> &owned)
{
for (auto num : owned) {
permanent_assert(acc.find(num)->second == data,
"My data is present and my");
}
}
// Checks for all owned.second keys if there data is owned.first.
void check_present_same(skiplist_t::Accessor &acc,
template <typename S>
void check_present_same(typename S::Accessor &acc,
std::pair<size_t, std::vector<size_t>> &owned)
{
check_present_same(acc, owned.first, owned.second);
}
// Checks for all owned keys if there data is data.
void check_present_same(skiplist_t::Accessor &acc, size_t data,
std::vector<size_t> &owned)
{
for (auto num : owned) {
permanent_assert(acc.find(num)->second == data,
"My data is present and my");
}
check_present_same<S>(acc, owned.first, owned.second);
}
// Checks if reported size and traversed size are equal to given size.
void check_size(const skiplist_t::Accessor &acc, long long size)
template <typename S>
void check_size(typename S::Accessor &acc, long long size)
{
// check size
// check size
permanent_assert(acc.size() == size,
"Size should be " << size << ", but size is " << acc.size());
permanent_assert(acc.size() == size, "Size should be " << size
<< ", but size is "
<< acc.size());
// check count
// check count
size_t iterator_counter = 0;
size_t iterator_counter = 0;
for (auto elem : acc) {
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< acc.size());
for (auto elem : acc) {
++iterator_counter;
}
permanent_assert(iterator_counter == size, "Iterator count should be "
<< size << ", but size is "
<< iterator_counter);
}
// Checks if order in list is maintened. It expects map
template <typename S>
void check_order(typename S::Accessor &acc)
{
if (acc.begin() != acc.end()) {
auto last = acc.begin()->first;
for (auto elem : acc) {
if (!(last <= elem))
std::cout << "Order isn't maintained. Before was: " << last
<< " next is " << elem.first << "\n";
last = elem.first;
}
}
}
void check_zero(size_t key_range, long array[], const char *str)
{
for (int i = 0; i < key_range; i++) {
permanent_assert(array[i] == 0,
str << " doesn't hold it's guarantees. It has "
<< array[i] << " extra elements.");
}
}
// Checks multiIterator and iterator guarantees
void check_multi_iterator(multimap_t::Accessor &accessor, size_t key_range,
long set[])
{
for (int i = 0; i < key_range; i++) {
auto it = accessor.find(i);
auto it_m = accessor.find_multi(i);
permanent_assert(
!(it_m != accessor.end(i) && it == accessor.end()),
"MultiIterator ended before Iterator. Set: " << set[i]);
permanent_assert(
!(it_m == accessor.end(i) && it != accessor.end()),
"Iterator ended before MultiIterator. Set: " << set[i]);
permanent_assert((it_m == accessor.end(i) && it == accessor.end()) ||
it->second == it_m->second,
"MultiIterator didn't found the same "
"first element. Set: "
<< set[i]);
if (set[i] > 0) {
for (int j = 0; j < set[i]; j++) {
permanent_assert(
it->second == it_m->second,
"MultiIterator and iterator aren't on the same "
"element.");
permanent_assert(it_m->first == i,
"MultiIterator is showing illegal data") it++;
it_m++;
}
}
permanent_assert(
it_m == accessor.end(i),
"There is more data than it should be in MultiIterator. "
<< it_m->first << "\n");
}
}
// Runs given function in threads_no threads and returns vector of futures for
// there
// results.
template <class R>
template <class R, typename S>
std::vector<std::future<std::pair<size_t, R>>>
run(size_t threads_no, skiplist_t &skiplist,
std::function<R(skiplist_t::Accessor, size_t)> f)
run(size_t threads_no, S &skiplist,
std::function<R(typename S::Accessor, size_t)> f)
{
std::vector<std::future<std::pair<size_t, R>>> futures;
std::vector<std::future<std::pair<size_t, R>>> futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
std::packaged_task<std::pair<size_t, R>()> task([&skiplist, f, thread_i]() {
return std::pair<size_t, R>(thread_i, f(skiplist.access(), thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i) {
std::packaged_task<std::pair<size_t, R>()> task(
[&skiplist, f, thread_i]() {
return std::pair<size_t, R>(thread_i,
f(skiplist.access(), thread_i));
}); // wrap the function
futures.push_back(task.get_future()); // get a future
std::thread(std::move(task)).detach();
}
return futures;
}
// Collects all data from futures.
template <class R>
auto collect(std::vector<std::future<R>> &collect)
{
std::vector<R> collection;
for (auto &fut : collect) {
collection.push_back(fut.get());
}
return collection;
std::vector<R> collection;
for (auto &fut : collect) {
collection.push_back(fut.get());
}
return collection;
}
// Returns object which tracs in owned which (key,data) where added and
// downcounts.
template <class K, class D>
auto insert_try(skiplist_t::Accessor &acc, size_t &downcount,
template <class K, class D, class S>
auto insert_try(typename S::Accessor &acc, long long &downcount,
std::vector<K> &owned)
{
return [&](K key, D data) mutable {
if (acc.insert(key, data).second) {
downcount--;
owned.push_back(key);
}
};
return [&](K key, D data) mutable {
if (acc.insert(key, data).second) {
downcount--;
owned.push_back(key);
}
};
}
// Helper function.
int parseLine(char *line)
{
// This assumes that a digit will be found and the line ends in " Kb".
int i = strlen(line);
const char *p = line;
while (*p < '0' || *p > '9')
p++;
line[i - 3] = '\0';
i = atoi(p);
return i;
// This assumes that a digit will be found and the line ends in " Kb".
int i = strlen(line);
const char *p = line;
while (*p < '0' || *p > '9')
p++;
line[i - 3] = '\0';
i = atoi(p);
return i;
}
// Returns currentlz used memory in kB.
int currently_used_memory()
{ // Note: this value is in KB!
FILE *file = fopen("/proc/self/status", "r");
int result = -1;
char line[128];
FILE *file = fopen("/proc/self/status", "r");
int result = -1;
char line[128];
while (fgets(line, 128, file) != NULL) {
if (strncmp(line, "VmSize:", 7) == 0) {
result = parseLine(line);
break;
while (fgets(line, 128, file) != NULL) {
if (strncmp(line, "VmSize:", 7) == 0) {
result = parseLine(line);
break;
}
}
}
fclose(file);
return result;
fclose(file);
return result;
}
// Performs memory check to determine if memory usage before calling given
@ -150,11 +223,11 @@ int currently_used_memory()
// senstive so no_threads spawned in function is necessary.
void memory_check(size_t no_threads, std::function<void()> f)
{
long long start = currently_used_memory();
f();
long long leaked =
currently_used_memory() - start -
no_threads * 73732; // OS sensitive, 73732 size allocated for thread
std::cout << "leaked: " << leaked << "\n";
permanent_assert(leaked <= 0, "Memory leak check");
long long start = currently_used_memory();
f();
long long leaked =
currently_used_memory() - start -
no_threads * 73732; // OS sensitive, 73732 size allocated for thread
std::cout << "leaked: " << leaked << "\n";
permanent_assert(leaked <= 0, "Memory leak check");
}

View File

@ -1,70 +0,0 @@
#include "common.h"
#define THREADS_NO 1
constexpr size_t elems_per_thread = 16e5;
int main()
{
memory_check(THREADS_NO, [&] {
ds::static_array<std::thread, THREADS_NO> threads;
skiplist_t skiplist;
// put THREADS_NO * elems_per_thread items to the skiplist
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = 0; elem_i < elems_per_thread; ++elem_i) {
accessor.insert(elem_i, elem_i);
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// wait all threads
for (auto &thread : threads) {
thread.join();
}
// get skiplist size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == THREADS_NO * elems_per_thread,
"all elements in skiplist");
}
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = 0; elem_i < elems_per_thread; ++elem_i) {
permanent_assert(accessor.remove(elem_i) == true, "");
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// wait all threads
for (auto &thread : threads) {
thread.join();
}
// check size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == 0, "Size should be 0, but size is "
<< accessor.size());
}
// check count
{
size_t iterator_counter = 0;
auto accessor = skiplist.access();
for (auto elem : accessor) {
++iterator_counter;
cout << elem.first << " ";
}
permanent_assert(iterator_counter == 0, "deleted elements");
}
});
}

View File

@ -9,29 +9,31 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
// Test checks for missing data and changed/overwriten data.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
size_t downcount = elems_per_thread;
std::vector<size_t> owned;
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
long long downcount = elems_per_thread;
std::vector<size_t> owned;
auto inserter =
insert_try<size_t, size_t, map_t>(acc, downcount, owned);
do {
inserter(rand(), index);
} while (downcount > 0);
do {
inserter(rand(), index);
} while (downcount > 0);
check_present_same(acc, index, owned);
return owned;
});
check_present_same<map_t>(acc, index, owned);
return owned;
});
auto accessor = skiplist.access();
for (auto &owned : collect(futures)) {
check_present_same(accessor, owned);
}
auto accessor = skiplist.access();
for (auto &owned : collect(futures)) {
check_present_same<map_t>(accessor, owned);
}
check_size(accessor, THREADS_NO * elems_per_thread);
});
check_size<map_t>(accessor, THREADS_NO * elems_per_thread);
check_order<map_t>(accessor);
});
}

View File

@ -10,29 +10,31 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
// Test checks for missing data and changed/overwriten data.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
size_t downcount = elems_per_thread;
std::vector<size_t> owned;
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
long long downcount = elems_per_thread;
std::vector<size_t> owned;
auto inserter =
insert_try<size_t, size_t, map_t>(acc, downcount, owned);
for (int i = 0; downcount > 0; i++) {
inserter(i, index);
}
for (int i = 0; downcount > 0; i++) {
inserter(i, index);
}
check_present_same(acc, index, owned);
return owned;
});
check_present_same<map_t>(acc, index, owned);
return owned;
});
auto accessor = skiplist.access();
for (auto &owned : collect(futures)) {
check_present_same(accessor, owned);
}
auto accessor = skiplist.access();
for (auto &owned : collect(futures)) {
check_present_same<map_t>(accessor, owned);
}
check_size(accessor, THREADS_NO * elems_per_thread);
});
check_size<map_t>(accessor, THREADS_NO * elems_per_thread);
check_order<map_t>(accessor);
});
}

View File

@ -0,0 +1,76 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t elems_per_thread = 1e5;
int main()
{
memory_check(THREADS_NO, [&] {
ds::static_array<std::thread, THREADS_NO> threads;
map_t skiplist;
// put THREADS_NO * elems_per_thread items to the skiplist
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = start; elem_i < end; ++elem_i) {
accessor.insert(elem_i, elem_i);
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// wait all threads
for (auto &thread : threads) {
thread.join();
}
// get skiplist size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == THREADS_NO * elems_per_thread,
"all elements in skiplist");
}
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = start; elem_i < end; ++elem_i) {
permanent_assert(accessor.remove(elem_i) == true, "");
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// // wait all threads
for (auto &thread : threads) {
thread.join();
}
// check size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == 0,
"Size should be 0, but size is "
<< accessor.size());
}
// check count
{
size_t iterator_counter = 0;
auto accessor = skiplist.access();
for (auto elem : accessor) {
++iterator_counter;
cout << elem.first << " ";
}
permanent_assert(iterator_counter == 0, "deleted elements");
}
{
auto accessor = skiplist.access();
check_order<map_t>(accessor);
}
});
}

View File

@ -8,7 +8,7 @@ constexpr size_t elements = 2e6;
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
map_t skiplist;
auto futures = run<size_t>(THREADS_NO, skiplist, [](auto acc, auto index) {
for (size_t i = 0; i < elements; i++) {
@ -17,6 +17,8 @@ int main()
return index;
});
collect(futures);
check_size(skiplist.access(), elements);
auto accessor = skiplist.access();
check_size<map_t>(accessor, elements);
});
}

View File

@ -0,0 +1,72 @@
#include "common.h"
#define THREADS_NO 1
constexpr size_t elems_per_thread = 16e5;
// Known memory leak at 1,600,000 elements.
int main()
{
memory_check(THREADS_NO, [&] {
ds::static_array<std::thread, THREADS_NO> threads;
map_t skiplist;
// put THREADS_NO * elems_per_thread items to the skiplist
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = start; elem_i < end; ++elem_i) {
accessor.insert(elem_i, elem_i);
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// wait all threads
for (auto &thread : threads) {
thread.join();
}
// get skiplist size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == THREADS_NO * elems_per_thread,
"all elements in skiplist");
}
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
threads[thread_i] = std::thread(
[&skiplist](size_t start, size_t end) {
auto accessor = skiplist.access();
for (size_t elem_i = start; elem_i < end; ++elem_i) {
permanent_assert(accessor.remove(elem_i) == true, "");
}
},
thread_i * elems_per_thread,
thread_i * elems_per_thread + elems_per_thread);
}
// // wait all threads
for (auto &thread : threads) {
thread.join();
}
// check size
{
auto accessor = skiplist.access();
permanent_assert(accessor.size() == 0,
"Size should be 0, but size is "
<< accessor.size());
}
// check count
{
size_t iterator_counter = 0;
auto accessor = skiplist.access();
for (auto elem : accessor) {
++iterator_counter;
cout << elem.first << " ";
}
permanent_assert(iterator_counter == 0, "deleted elements");
}
});
}

View File

@ -0,0 +1,55 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
// Depending on value there is a possiblity of numerical overflow
constexpr size_t max_number = 10;
constexpr size_t no_insert_for_one_delete = 1;
// This test checks MultiIterator from multimap.
// Each thread removes random data. So removes are joint.
// Calls of remove method are interleaved with insert calls which always
// succeed.
int main()
{
memory_check(THREADS_NO, [] {
multimap_t skiplist;
auto futures = run<std::vector<long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long long> set(key_range, 0);
do {
size_t num = rand();
auto data = num % max_number;
if (rand_op()) {
if (acc.remove(num)) {
downcount--;
set[num]--;
}
} else {
acc.insert(num, data);
downcount--;
set[num]++;
}
} while (downcount > 0);
return set;
});
long set[key_range] = {0};
for (auto &data : collect(futures)) {
for (int i = 0; i < key_range; i++) {
set[i] += data.second[i];
}
}
auto accessor = skiplist.access();
check_multi_iterator(accessor, key_range, set);
check_order<multimap_t>(accessor);
});
}

View File

@ -0,0 +1,90 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
// Depending on value there is a possiblity of numerical overflow
constexpr size_t max_number = 10;
constexpr size_t no_insert_for_one_delete = 1;
// This test checks MultiIterator remove method.
// Each thread removes random data. So removes are joint and scattered on same
// key values.
// Calls of remove method are interleaved with insert calls which always
// succeed.
int main()
{
memory_check(THREADS_NO, [] {
multimap_t skiplist;
auto futures = run<std::pair<long long, std::vector<long long>>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long long> set(key_range, 0);
long long sum = 0;
do {
size_t num = rand();
auto data = rand() % max_number;
if (rand_op()) {
int len = 0;
for (auto it = acc.find_multi(num); it.has_value();
it++) {
len++;
}
if (len > 0) {
int pos = rand() % len;
for (auto it = acc.find_multi(num); it.has_value();
it++) {
if (pos == 0) {
auto data_r = it->second;
if (it.remove()) {
downcount--;
set[num]--;
sum -= data_r;
permanent_assert(
it.is_removed(),
"is_removed method doesn't work");
}
break;
}
pos--;
}
}
} else {
acc.insert(num, data);
downcount--;
set[num]++;
sum += data;
}
} while (downcount > 0);
return std::pair<long long, std::vector<long long>>(sum, set);
});
long set[key_range] = {0};
long long sums = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
for (int i = 0; i < key_range; i++) {
set[i] += data.second.second[i];
}
}
auto accessor = skiplist.access();
check_multi_iterator(accessor, key_range, set);
for (auto &e : accessor) {
set[e.first]--;
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_zero(key_range, set, "MultiMap");
check_order<multimap_t>(accessor);
});
}

View File

@ -0,0 +1,82 @@
#include "common.h"
#define THREADS_NO 4
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
// Depending on value there is a possiblity of numerical overflow
constexpr size_t max_number = 10;
constexpr size_t no_insert_for_one_delete = 2;
// This test checks MultiIterator remove method ].
// Each thread removes all duplicate data on random key. So removes are joint
// and scattered on same
// key values.
// Calls of remove method are interleaved with insert calls which always
// succeed.
int main()
{
memory_check(THREADS_NO, [] {
multimap_t skiplist;
auto futures = run<std::pair<long long, std::vector<long long>>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long long> set(key_range, 0);
long long sum = 0;
do {
size_t num = rand();
auto data = rand() % max_number;
if (rand_op()) {
auto it = acc.find_multi(num);
if (it.has_value()) {
it++;
while (it.has_value()) {
auto data_r = it->second;
if (it.remove()) {
downcount--;
set[num]--;
sum -= data_r;
permanent_assert(
it.is_removed(),
"is_removed method doesn't work");
}
it++;
}
}
} else {
acc.insert(num, data);
downcount--;
set[num]++;
sum += data;
}
} while (downcount > 0);
return std::pair<long long, std::vector<long long>>(sum, set);
});
long set[key_range] = {0};
long long sums = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
for (int i = 0; i < key_range; i++) {
set[i] += data.second.second[i];
}
}
auto accessor = skiplist.access();
check_multi_iterator(accessor, key_range, set);
for (auto &e : accessor) {
set[e.first]--;
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_zero(key_range, set, "MultiMap");
check_order<multimap_t>(accessor);
});
}

View File

@ -0,0 +1,120 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
// Depending on value there is a possiblity of numerical overflow
constexpr size_t max_number = 10;
constexpr size_t no_insert_for_one_delete = 1;
// This test checks multimap.
// Each thread removes random data. So removes are joint.
// Calls of remove method are interleaved with insert calls which always
// succeed.
int main()
{
memory_check(THREADS_NO, [] {
multimap_t skiplist;
std::atomic<long long> size(0);
auto futures = run<std::pair<long long, std::vector<long long>>>(
THREADS_NO, skiplist, [&size](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long long> set(key_range, 0);
long long sum = 0;
do {
size_t num = rand();
auto data = num % max_number;
if (rand_op()) {
if (acc.remove(num)) {
downcount--;
set[num]--;
sum -= data;
size--;
}
} else {
acc.insert(num, data);
downcount--;
set[num]++;
sum += data;
size++;
}
} while (downcount > 0);
return std::pair<long long, std::vector<long long>>(sum, set);
});
long set[key_range] = {0};
long long sums = 0;
long long size_calc = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
for (int i = 0; i < key_range; i++) {
set[i] += data.second.second[i];
size_calc += data.second.second[i];
}
}
auto accessor = skiplist.access();
permanent_assert(size == size_calc, "Set size isn't same as counted");
check_size<multimap_t>(accessor, size);
check_order<multimap_t>(accessor);
auto bef_it = accessor.end();
for (int i = 0; i < key_range; i++) {
auto it = accessor.find(i);
if (set[i] > 0) {
permanent_assert(it != accessor.end(),
"Multimap doesn't contain necessary element "
<< i);
if (bef_it == accessor.end()) bef_it = accessor.find(i);
for (int j = 0; j < set[i]; j++) {
permanent_assert(
bef_it != accessor.end(),
"Previous iterator doesn't iterate through same "
"key entrys. Expected "
<< i << " for " << set[i] - j
<< " more times but found null");
permanent_assert(
bef_it->first == i,
"Previous iterator doesn't iterate through same "
"key entrys. Expected "
<< i << " for " << set[i] - j
<< " more times but found " << bef_it->first
<< ". Occurances should be " << set[i]);
bef_it++;
}
for (int j = 0; j < set[i]; j++) {
permanent_assert(it != accessor.end(),
"Iterator doesn't iterate through same "
"key entrys. Expected "
<< i << " for " << set[i] - j
<< " more times but found null");
permanent_assert(
it->first == i,
"Iterator doesn't iterate through same "
"key entrys. Expected "
<< i << " for " << set[i] - j
<< " more times but found " << it->first
<< ". Occurances should be " << set[i]);
it++;
}
permanent_assert(it == accessor.end() || it->first != i,
"There is more data than it should be.");
bef_it = it;
}
}
for (auto &e : accessor) {
set[e.first]--;
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_zero(key_range, set, "MultiMap");
});
}

View File

@ -0,0 +1,70 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
constexpr size_t no_insert_for_one_delete = 1;
// This test checks multiset.
// Each thread removes random data. So removes are joint.
// Calls of remove method are interleaved with insert calls which always
// succeed.
int main()
{
memory_check(THREADS_NO, [] {
multiset_t skiplist;
auto futures = run<std::vector<long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long> set(key_range, 0);
do {
size_t num = rand();
if (rand_op()) {
if (acc.remove(num)) {
downcount--;
set[num]--;
}
} else {
acc.insert(num);
downcount--;
set[num]++;
}
} while (downcount > 0);
return set;
});
long set[key_range] = {0};
for (auto &data : collect(futures)) {
for (int i = 0; i < key_range; i++) {
set[i] += data.second[i];
}
}
auto accessor = skiplist.access();
for (int i = 0; i < key_range; i++) {
auto it = accessor.find(i);
if (set[i] > 0) {
for (int j = 0; j < set[i]; j++) {
permanent_assert(
it == i,
"Iterator doesn't iterate through same key entrys");
it++;
}
}
permanent_assert(it == accessor.end() || it != i,
"There is more data than it should be.");
}
for (auto &e : accessor) {
set[e]--;
}
check_zero(key_range, set, "MultiSet");
});
}

View File

@ -12,52 +12,53 @@ constexpr size_t no_insert_for_one_delete = 2;
// Calls of remove method are interleaved with insert calls.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
size_t downcount = op_per_thread;
long long sum = 0;
long long count = 0;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
long long sum = 0;
long long count = 0;
for (int i = 0; downcount > 0; i++) {
auto data = i % max_number;
if (rand_op()) {
auto t = i;
while (t > 0) {
if (acc.remove(t)) {
sum -= t % max_number;
downcount--;
count--;
break;
for (int i = 0; downcount > 0; i++) {
auto data = i % max_number;
if (rand_op()) {
auto t = i;
while (t > 0) {
if (acc.remove(t)) {
sum -= t % max_number;
downcount--;
count--;
break;
}
t--;
}
} else {
if (acc.insert(i, data).second) {
sum += data;
count++;
downcount--;
}
}
}
t--;
}
} else {
if (acc.insert(i, data).second) {
sum += data;
count++;
downcount--;
}
}
}
return std::pair<long long, long long>(sum, count);
});
return std::pair<long long, long long>(sum, count);
});
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_size(accessor, counters);
});
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_size<map_t>(accessor, counters);
check_order<map_t>(accessor);
});
}

View File

@ -10,38 +10,41 @@ constexpr size_t no_insert_for_one_delete = 1;
// Calls of remove method are interleaved with insert calls.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
size_t downcount = op_per_thread;
std::vector<size_t> owned;
auto inserter = insert_try<size_t, size_t>(acc, downcount, owned);
auto futures = run<std::vector<size_t>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<size_t> owned;
auto inserter =
insert_try<size_t, size_t, map_t>(acc, downcount, owned);
do {
if (owned.size() != 0 && rand_op()) {
auto rem = rand() % owned.size();
permanent_assert(acc.remove(owned[rem]), "Owned data removed");
owned.erase(owned.begin() + rem);
downcount--;
} else {
inserter(rand(), index);
}
} while (downcount > 0);
do {
if (owned.size() != 0 && rand_op()) {
auto rem = rand() % owned.size();
permanent_assert(acc.remove(owned[rem]),
"Owned data removed");
owned.erase(owned.begin() + rem);
downcount--;
} else {
inserter(rand(), index);
}
} while (downcount > 0);
check_present_same(acc, index, owned);
return owned;
});
check_present_same<map_t>(acc, index, owned);
return owned;
});
auto accessor = skiplist.access();
size_t count = 0;
for (auto &owned : collect(futures)) {
check_present_same(accessor, owned);
count += owned.second.size();
}
check_size(accessor, count);
});
auto accessor = skiplist.access();
size_t count = 0;
for (auto &owned : collect(futures)) {
check_present_same<map_t>(accessor, owned);
count += owned.second.size();
}
check_size<map_t>(accessor, count);
check_order<map_t>(accessor);
});
}

View File

@ -1,7 +1,7 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e5;
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
// Depending on value there is a possiblity of numerical overflow
constexpr size_t max_number = 10;
@ -12,50 +12,51 @@ constexpr size_t no_insert_for_one_delete = 2;
// Calls of remove method are interleaved with insert calls.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
size_t downcount = op_per_thread;
long long sum = 0;
long long count = 0;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
long long sum = 0;
long long count = 0;
do {
auto num = rand();
auto data = num % max_number;
if (rand_op()) {
if (acc.remove(num)) {
sum -= data;
downcount--;
count--;
}
} else {
if (acc.insert(num, data).second) {
sum += data;
downcount--;
count++;
}
}
} while (downcount > 0);
do {
auto num = rand();
auto data = num % max_number;
if (rand_op()) {
if (acc.remove(num)) {
sum -= data;
downcount--;
count--;
}
} else {
if (acc.insert(num, data).second) {
sum += data;
downcount--;
count++;
}
}
} while (downcount > 0);
return std::pair<long long, long long>(sum, count);
});
return std::pair<long long, long long>(sum, count);
});
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_size(accessor, counters);
});
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Aproximetly Same values are present");
check_size<map_t>(accessor, counters);
check_order<map_t>(accessor);
});
}

View File

@ -0,0 +1,61 @@
#include "common.h"
#define THREADS_NO 8
constexpr size_t key_range = 1e4;
constexpr size_t op_per_thread = 1e5;
constexpr size_t no_insert_for_one_delete = 2;
// This test checks set.
// Each thread removes random data. So removes are joint.
// Calls of remove method are interleaved with insert calls.
int main()
{
memory_check(THREADS_NO, [] {
set_t skiplist;
auto futures = run<std::vector<long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_op = rand_gen_bool(no_insert_for_one_delete);
long long downcount = op_per_thread;
std::vector<long> set(key_range);
do {
size_t num = rand();
if (rand_op()) {
if (acc.remove(num)) {
downcount--;
set[num]--;
}
} else {
if (acc.insert(num).second) {
downcount--;
set[num]++;
}
}
} while (downcount > 0);
return set;
});
long set[key_range] = {0};
for (auto &data : collect(futures)) {
for (int i = 0; i < key_range; i++) {
set[i] += data.second[i];
}
}
auto accessor = skiplist.access();
for (int i = 0; i < key_range; i++) {
permanent_assert(set[i] == 0 || set[i] == 1 ||
(set[i] == 1) ^ accessor.contains(i),
"Set doesn't hold it's guarantees.");
}
for (auto &e : accessor) {
set[e]--;
}
check_zero(key_range, set, "Set");
});
}

View File

@ -14,54 +14,56 @@ constexpr size_t no_insert_for_one_delete = 1;
// no_find_per_change and no_insert_for_one_delete.
int main()
{
memory_check(THREADS_NO, [] {
skiplist_t skiplist;
memory_check(THREADS_NO, [] {
map_t skiplist;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_change = rand_gen_bool(no_find_per_change);
auto rand_delete = rand_gen_bool(no_insert_for_one_delete);
long long sum = 0;
long long count = 0;
auto futures = run<std::pair<long long, long long>>(
THREADS_NO, skiplist, [](auto acc, auto index) {
auto rand = rand_gen(key_range);
auto rand_change = rand_gen_bool(no_find_per_change);
auto rand_delete = rand_gen_bool(no_insert_for_one_delete);
long long sum = 0;
long long count = 0;
for (int i = 0; i < op_per_thread; i++) {
auto num = rand();
auto data = num % max_number;
if (rand_change()) {
if (rand_delete()) {
if (acc.remove(num)) {
sum -= data;
count--;
for (int i = 0; i < op_per_thread; i++) {
auto num = rand();
auto data = num % max_number;
if (rand_change()) {
if (rand_delete()) {
if (acc.remove(num)) {
sum -= data;
count--;
}
} else {
if (acc.insert(num, data).second) {
sum += data;
count++;
}
}
} else {
auto value = acc.find(num);
permanent_assert(value == acc.end() ||
value->second == data,
"Data is invalid");
}
}
} else {
if (acc.insert(num, data).second) {
sum += data;
count++;
}
}
} else {
auto value = acc.find(num);
permanent_assert(value == acc.end() || value->second == data,
"Data is invalid");
}
}
return std::pair<long long, long long>(sum, count);
});
return std::pair<long long, long long>(sum, count);
});
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
auto accessor = skiplist.access();
long long sums = 0;
long long counters = 0;
for (auto &data : collect(futures)) {
sums += data.second.first;
counters += data.second.second;
}
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Same values aren't present");
check_size(accessor, counters);
});
for (auto &e : accessor) {
sums -= e.second;
}
permanent_assert(sums == 0, "Same values aren't present");
check_size<map_t>(accessor, counters);
check_order<map_t>(accessor);
});
}