2016-07-31 04:20:21 +08:00
|
|
|
#include <chrono>
|
|
|
|
#include <future>
|
|
|
|
#include <iostream>
|
|
|
|
#include <random>
|
|
|
|
#include <thread>
|
|
|
|
|
2016-08-03 00:05:10 +08:00
|
|
|
#include "data_structures/bitset/dynamic_bitset.hpp"
|
2016-10-11 08:43:41 +08:00
|
|
|
#include "data_structures/concurrent/concurrent_list.hpp"
|
2016-07-31 04:20:21 +08:00
|
|
|
#include "data_structures/concurrent/concurrent_map.hpp"
|
2016-08-02 20:23:39 +08:00
|
|
|
#include "data_structures/concurrent/concurrent_multimap.hpp"
|
|
|
|
#include "data_structures/concurrent/concurrent_multiset.hpp"
|
|
|
|
#include "data_structures/concurrent/concurrent_set.hpp"
|
2016-07-31 04:20:21 +08:00
|
|
|
#include "data_structures/concurrent/skiplist.hpp"
|
|
|
|
#include "data_structures/static_array.hpp"
|
2016-08-15 07:09:58 +08:00
|
|
|
#include "logging/default.hpp"
|
|
|
|
#include "logging/streams/stdout.hpp"
|
2016-10-11 08:43:41 +08:00
|
|
|
#include "utils/assert.hpp"
|
2016-07-31 04:20:21 +08:00
|
|
|
#include "utils/sysinfo/memory.hpp"
|
|
|
|
|
2016-10-11 08:43:41 +08:00
|
|
|
// NOTE: this file is highly coupled to data_structures
|
|
|
|
// TODO: REFACTOR
|
|
|
|
|
2016-09-14 01:08:34 +08:00
|
|
|
// Sets max number of threads that will be used in concurrent tests.
|
2016-10-11 08:43:41 +08:00
|
|
|
constexpr int max_no_threads = 8;
|
2016-09-14 01:08:34 +08:00
|
|
|
|
2016-07-31 04:20:21 +08:00
|
|
|
using std::cout;
|
|
|
|
using std::endl;
|
2016-12-22 04:33:58 +08:00
|
|
|
using map_t = ConcurrentMap<int, int>;
|
|
|
|
using set_t = ConcurrentSet<int>;
|
2016-08-02 20:23:39 +08:00
|
|
|
using multiset_t = ConcurrentMultiSet<int>;
|
|
|
|
using multimap_t = ConcurrentMultiMap<int, int>;
|
|
|
|
|
2016-07-31 04:20:21 +08:00
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Returns uniform random size_t generator from range [0,n>
|
2016-07-31 20:56:13 +08:00
|
|
|
auto rand_gen(size_t n)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
std::default_random_engine generator;
|
|
|
|
std::uniform_int_distribution<size_t> distribution(0, n - 1);
|
|
|
|
return std::bind(distribution, generator);
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns random bool generator with distribution of 1 true for n false.
|
2016-07-31 20:56:13 +08:00
|
|
|
auto rand_gen_bool(size_t n = 1)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
auto gen = rand_gen(n + 1);
|
|
|
|
return [=]() mutable { return gen() == 0; };
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Checks for all owned keys if there data is data.
|
2016-08-02 20:23:39 +08:00
|
|
|
template <typename S>
|
|
|
|
void check_present_same(typename S::Accessor &acc, size_t data,
|
2016-07-31 20:56:13 +08:00
|
|
|
std::vector<size_t> &owned)
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto num : owned)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
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.
|
|
|
|
template <typename S>
|
|
|
|
void check_present_same(typename S::Accessor &acc,
|
|
|
|
std::pair<size_t, std::vector<size_t>> &owned)
|
|
|
|
{
|
|
|
|
check_present_same<S>(acc, owned.first, owned.second);
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Checks if reported size and traversed size are equal to given size.
|
2016-08-02 20:23:39 +08:00
|
|
|
template <typename S>
|
2016-09-14 00:53:40 +08:00
|
|
|
void check_size_list(S &acc, long long size)
|
|
|
|
{
|
|
|
|
// check size
|
|
|
|
|
|
|
|
permanent_assert(acc.size() == size, "Size should be " << size
|
|
|
|
<< ", but size is "
|
|
|
|
<< acc.size());
|
|
|
|
|
|
|
|
// check count
|
|
|
|
|
|
|
|
size_t iterator_counter = 0;
|
|
|
|
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto elem : acc)
|
|
|
|
{
|
2016-09-14 00:53:40 +08:00
|
|
|
++iterator_counter;
|
|
|
|
}
|
|
|
|
permanent_assert(iterator_counter == size, "Iterator count should be "
|
|
|
|
<< size << ", but size is "
|
|
|
|
<< iterator_counter);
|
|
|
|
}
|
|
|
|
template <typename S>
|
2016-08-02 20:23:39 +08:00
|
|
|
void check_size(typename S::Accessor &acc, long long size)
|
2016-07-31 20:56:13 +08:00
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
// check size
|
2016-07-31 04:20:21 +08:00
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
permanent_assert(acc.size() == size, "Size should be " << size
|
|
|
|
<< ", but size is "
|
|
|
|
<< acc.size());
|
2016-07-31 04:20:21 +08:00
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
// check count
|
2016-07-31 04:20:21 +08:00
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
size_t iterator_counter = 0;
|
2016-07-31 04:20:21 +08:00
|
|
|
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto elem : acc)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
++iterator_counter;
|
|
|
|
}
|
|
|
|
permanent_assert(iterator_counter == size, "Iterator count should be "
|
|
|
|
<< size << ", but size is "
|
|
|
|
<< iterator_counter);
|
|
|
|
}
|
2016-09-14 00:53:40 +08:00
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
// Checks if order in list is maintened. It expects map
|
|
|
|
template <typename S>
|
|
|
|
void check_order(typename S::Accessor &acc)
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
if (acc.begin() != acc.end())
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
auto last = acc.begin()->first;
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto elem : acc)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
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)
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
for (int i = 0; i < key_range; i++)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
permanent_assert(array[i] == 0,
|
|
|
|
str << " doesn't hold it's guarantees. It has "
|
|
|
|
<< array[i] << " extra elements.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 00:05:10 +08:00
|
|
|
void check_set(DynamicBitset<> &db, std::vector<bool> &set)
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
for (int i = 0; i < set.size(); i++)
|
|
|
|
{
|
2016-08-03 00:05:10 +08:00
|
|
|
permanent_assert(!(set[i] ^ db.at(i)),
|
|
|
|
"Set constraints aren't fullfilled.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
// Checks multiIterator and iterator guarantees
|
|
|
|
void check_multi_iterator(multimap_t::Accessor &accessor, size_t key_range,
|
|
|
|
long set[])
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
for (int i = 0; i < key_range; i++)
|
|
|
|
{
|
|
|
|
auto it = accessor.find(i);
|
2016-08-02 20:23:39 +08:00
|
|
|
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]);
|
2016-12-22 04:33:58 +08:00
|
|
|
if (set[i] > 0)
|
|
|
|
{
|
|
|
|
for (int j = 0; j < set[i]; j++)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
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");
|
|
|
|
}
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Runs given function in threads_no threads and returns vector of futures for
|
|
|
|
// there
|
|
|
|
// results.
|
2016-08-02 20:23:39 +08:00
|
|
|
template <class R, typename S>
|
2016-07-31 04:20:21 +08:00
|
|
|
std::vector<std::future<std::pair<size_t, R>>>
|
2016-08-02 20:23:39 +08:00
|
|
|
run(size_t threads_no, S &skiplist,
|
|
|
|
std::function<R(typename S::Accessor, size_t)> f)
|
2016-07-31 20:56:13 +08:00
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
std::vector<std::future<std::pair<size_t, R>>> futures;
|
|
|
|
|
2016-12-22 04:33:58 +08:00
|
|
|
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
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;
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-08-03 00:05:10 +08:00
|
|
|
// Runs given function in threads_no threads and returns vector of futures for
|
|
|
|
// there
|
|
|
|
// results.
|
|
|
|
template <class R>
|
|
|
|
std::vector<std::future<std::pair<size_t, R>>> run(size_t threads_no,
|
|
|
|
std::function<R(size_t)> f)
|
|
|
|
{
|
|
|
|
std::vector<std::future<std::pair<size_t, R>>> futures;
|
|
|
|
|
2016-12-22 04:33:58 +08:00
|
|
|
for (size_t thread_i = 0; thread_i < threads_no; ++thread_i)
|
|
|
|
{
|
2016-08-03 00:05:10 +08:00
|
|
|
std::packaged_task<std::pair<size_t, R>()> task([f, thread_i]() {
|
|
|
|
return std::pair<size_t, R>(thread_i, f(thread_i));
|
|
|
|
}); // wrap the function
|
|
|
|
futures.push_back(task.get_future()); // get a future
|
|
|
|
std::thread(std::move(task)).detach();
|
|
|
|
}
|
|
|
|
return futures;
|
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Collects all data from futures.
|
2016-07-31 20:56:13 +08:00
|
|
|
template <class R>
|
|
|
|
auto collect(std::vector<std::future<R>> &collect)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
std::vector<R> collection;
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto &fut : collect)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
collection.push_back(fut.get());
|
|
|
|
}
|
|
|
|
return collection;
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-08-03 00:05:10 +08:00
|
|
|
std::vector<bool> collect_set(
|
|
|
|
std::vector<std::future<std::pair<size_t, std::vector<bool>>>> &&futures)
|
|
|
|
{
|
|
|
|
std::vector<bool> set;
|
2016-12-22 04:33:58 +08:00
|
|
|
for (auto &data : collect(futures))
|
|
|
|
{
|
2016-08-03 00:05:10 +08:00
|
|
|
set.resize(data.second.size());
|
2016-12-22 04:33:58 +08:00
|
|
|
for (int i = 0; i < data.second.size(); i++)
|
|
|
|
{
|
2016-08-03 00:05:10 +08:00
|
|
|
set[i] = set[i] | data.second[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return set;
|
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Returns object which tracs in owned which (key,data) where added and
|
|
|
|
// downcounts.
|
2016-08-02 20:23:39 +08:00
|
|
|
template <class K, class D, class S>
|
|
|
|
auto insert_try(typename S::Accessor &acc, long long &downcount,
|
2016-07-31 20:56:13 +08:00
|
|
|
std::vector<K> &owned)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
return [&](K key, D data) mutable {
|
2016-12-22 04:33:58 +08:00
|
|
|
if (acc.insert(key, data).second)
|
|
|
|
{
|
2016-08-02 20:23:39 +08:00
|
|
|
downcount--;
|
|
|
|
owned.push_back(key);
|
|
|
|
}
|
|
|
|
};
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
|
|
|
|
2016-07-31 18:40:38 +08:00
|
|
|
// Performs memory check to determine if memory usage before calling given
|
|
|
|
// function
|
|
|
|
// is aproximately equal to memory usage after function. Memory usage is thread
|
|
|
|
// senstive so no_threads spawned in function is necessary.
|
2016-07-31 20:56:13 +08:00
|
|
|
void memory_check(size_t no_threads, std::function<void()> f)
|
|
|
|
{
|
2016-12-22 04:33:58 +08:00
|
|
|
logging::info("Number of threads: {}", no_threads);
|
|
|
|
|
|
|
|
// TODO: replace vm_size with something more appropriate
|
|
|
|
// the past implementation was teribble wrong
|
|
|
|
// to that ASAP
|
|
|
|
// OR
|
|
|
|
// use custom allocation wrapper
|
|
|
|
// OR
|
|
|
|
// user Boost.Test
|
|
|
|
auto start = vm_size();
|
|
|
|
logging::info("Memory check (used memory at the beginning): {}", start);
|
|
|
|
|
2016-08-02 20:23:39 +08:00
|
|
|
f();
|
2016-12-22 04:33:58 +08:00
|
|
|
|
|
|
|
auto end = vm_size();
|
|
|
|
logging::info("Memory check (used memory at the end): {}", end);
|
|
|
|
|
|
|
|
long long delta = end - start;
|
|
|
|
logging::info("Delta: {}", delta);
|
|
|
|
|
|
|
|
// TODO: do memory check somehow
|
|
|
|
// the past implementation was wrong
|
|
|
|
permanent_assert(true, "Memory leak");
|
2016-07-31 04:20:21 +08:00
|
|
|
}
|
2016-08-15 07:09:58 +08:00
|
|
|
|
2016-12-22 22:51:16 +08:00
|
|
|
// TODO: move this inside logging/default
|
2016-10-11 08:43:41 +08:00
|
|
|
// Initializes loging faccilityes
|
|
|
|
void init_log()
|
|
|
|
{
|
2016-08-15 07:09:58 +08:00
|
|
|
logging::init_async();
|
|
|
|
logging::log->pipe(std::make_unique<Stdout>());
|
|
|
|
}
|