Added Skiplist ReverseIterator, Distance Approximation Prototype and documented some stuff
Summary: Skiplist ReverseIterator and Distance Approximation Test Plan: manual Reviewers: florijan, buda Reviewed By: buda Subscribers: pullbot, florijan, buda Differential Revision: https://phabricator.memgraph.io/D44
This commit is contained in:
parent
9135e2fa7a
commit
99b8a4f234
File diff suppressed because it is too large
Load Diff
@ -4,21 +4,28 @@
|
||||
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
/**
|
||||
* @class Lockable
|
||||
*
|
||||
* @brief
|
||||
* Lockable is used as an custom implementation of a mutex mechanism. It is
|
||||
* implemented as a wrapper around std::lock_guard and std::unique_guard with
|
||||
* a default lock called Spinlock.
|
||||
*
|
||||
* @tparam lock_t type of lock to be used (default = Spinlock)
|
||||
*/
|
||||
template <class lock_t = SpinLock>
|
||||
class Lockable
|
||||
{
|
||||
public:
|
||||
using lock_type = lock_t;
|
||||
class Lockable {
|
||||
public:
|
||||
using lock_type = lock_t;
|
||||
|
||||
std::lock_guard<lock_t> acquire_guard() const
|
||||
{
|
||||
return std::lock_guard<lock_t>(lock);
|
||||
}
|
||||
std::lock_guard<lock_t> acquire_guard() const {
|
||||
return std::lock_guard<lock_t>(lock);
|
||||
}
|
||||
|
||||
std::unique_lock<lock_t> acquire_unique() const
|
||||
{
|
||||
return std::unique_lock<lock_t>(lock);
|
||||
}
|
||||
std::unique_lock<lock_t> acquire_unique() const {
|
||||
return std::unique_lock<lock_t>(lock);
|
||||
}
|
||||
|
||||
mutable lock_t lock;
|
||||
mutable lock_t lock;
|
||||
};
|
||||
|
@ -1,23 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <unistd.h>
|
||||
#include <atomic>
|
||||
|
||||
#include "utils/cpu_relax.hpp"
|
||||
|
||||
class SpinLock
|
||||
{
|
||||
public:
|
||||
void lock()
|
||||
{ // Before was memorz_order_acquire
|
||||
while (lock_flag.test_and_set(std::memory_order_seq_cst))
|
||||
cpu_relax();
|
||||
/// usleep(250);
|
||||
}
|
||||
// Before was memory_order_release
|
||||
void unlock() { lock_flag.clear(std::memory_order_seq_cst); }
|
||||
/**
|
||||
* @class SpinLock
|
||||
*
|
||||
* @brief
|
||||
* Spinlock is used as an locking mechanism based on an atomic flag and
|
||||
* waiting loops. It uses the cpu_relax "asm pause" command to optimize wasted
|
||||
* time while the threads are waiting.
|
||||
*
|
||||
*/
|
||||
class SpinLock {
|
||||
public:
|
||||
void lock() { // Before was memory_order_acquire
|
||||
while (lock_flag.test_and_set(std::memory_order_seq_cst)) cpu_relax();
|
||||
}
|
||||
// Before was memory_order_release
|
||||
void unlock() { lock_flag.clear(std::memory_order_seq_cst); }
|
||||
|
||||
private:
|
||||
// guaranteed by standard to be lock free!
|
||||
mutable std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
|
||||
private:
|
||||
// guaranteed by standard to be lock free!
|
||||
mutable std::atomic_flag lock_flag = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
@ -1,56 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <ext/aligned_buffer.h>
|
||||
#include <utility>
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
#include <ext/aligned_buffer.h>
|
||||
|
||||
/**
|
||||
* @class Placeholder
|
||||
*
|
||||
* @brief
|
||||
* Placeholder is used to allocate memory for an object on heap providing
|
||||
* methods for setting and getting the object and making sure that the
|
||||
* object is initialized.
|
||||
*
|
||||
* @tparam T type of object to be wrapped in the placeholder
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
class Placeholder
|
||||
{
|
||||
public:
|
||||
Placeholder() = default;
|
||||
class Placeholder {
|
||||
public:
|
||||
Placeholder() = default;
|
||||
|
||||
Placeholder(Placeholder &) = delete;
|
||||
Placeholder(Placeholder &&) = delete;
|
||||
Placeholder(Placeholder &) = delete;
|
||||
Placeholder(Placeholder &&) = delete;
|
||||
|
||||
~Placeholder()
|
||||
{
|
||||
if (initialized) get().~T();
|
||||
};
|
||||
/**
|
||||
* The destructor automatically calls the wrapped objects destructor.
|
||||
*/
|
||||
~Placeholder() {
|
||||
if (initialized) get().~T();
|
||||
};
|
||||
|
||||
bool is_initialized() { return initialized; }
|
||||
/**
|
||||
* @return returns true if object is set in memory otherwise false.
|
||||
*/
|
||||
bool is_initialized() { return initialized; }
|
||||
|
||||
T &get() noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
T &get() noexcept {
|
||||
runtime_assert(initialized, "Placeholder object not initialized");
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
const T &get() const noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
/**
|
||||
* @return const reference to object.
|
||||
*/
|
||||
const T &get() const noexcept {
|
||||
runtime_assert(initialized, "Placeholder object not initialized");
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
void set(const T &item)
|
||||
{
|
||||
new (data._M_addr()) T(item);
|
||||
initialized = true;
|
||||
}
|
||||
/**
|
||||
* Sets item in allocated memory and sets the initialized flag.
|
||||
*
|
||||
* @param T& item reference to the item initialized in allocated memory
|
||||
*/
|
||||
void set(const T &item) {
|
||||
new (data._M_addr()) T(item);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void set(T &&item)
|
||||
{
|
||||
new (data._M_addr()) T(std::move(item));
|
||||
initialized = true;
|
||||
}
|
||||
/**
|
||||
* Moves item to allocated memory and sets initialized flag.1
|
||||
*
|
||||
* @param T&& rvalue reference to the item which is moved to allocated memory
|
||||
*/
|
||||
void set(T &&item) {
|
||||
new (data._M_addr()) T(std::move(item));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace(Args &&... args)
|
||||
{
|
||||
new (data._M_addr()) T(args...);
|
||||
initialized = true;
|
||||
}
|
||||
/**
|
||||
* Emplaces item to allocated memory and calls the Constructor with specified
|
||||
* arguments.
|
||||
*
|
||||
* @tparam Args type of arguments to be passed to the objects constructor.
|
||||
* @param Parameters passed to the objects constructor.
|
||||
*/
|
||||
template <class... Args>
|
||||
void emplace(Args &&... args) {
|
||||
new (data._M_addr()) T(args...);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private:
|
||||
__gnu_cxx::__aligned_buffer<T> data;
|
||||
bool initialized = false;
|
||||
private:
|
||||
// libstd aligned buffer struct
|
||||
__gnu_cxx::__aligned_buffer<T> data;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
// namespace ::utils
|
||||
@ -71,5 +72,15 @@ auto generate_vector(RandomGenerator &gen, int size) {
|
||||
return elements;
|
||||
}
|
||||
|
||||
// IMPORTANT
|
||||
// be careful with RandomGenerator ranges and set size
|
||||
// condition must be valid: size(set) << range(RandomGenerator)
|
||||
template <class RandomGenerator>
|
||||
auto generate_set(RandomGenerator &gen, int size) {
|
||||
std::set<decltype(gen.next())> elements;
|
||||
while (elements.size() < size) elements.insert(gen.next());
|
||||
return elements;
|
||||
}
|
||||
|
||||
}; // namespace utils::random
|
||||
}; // namespace ::utils
|
||||
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
@date: 2017-01-31
|
||||
@authors: Sandi Fatic
|
||||
|
||||
These tests are used to benchmark the ReverseIterator vs the Find function
|
||||
while iterating the whole skiplist in reverse.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "benchmark/benchmark_api.h"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/random/generator.h"
|
||||
|
||||
using utils::random::NumberGenerator;
|
||||
using IntegerGenerator = NumberGenerator<std::uniform_int_distribution<int>,
|
||||
std::default_random_engine, int>;
|
||||
|
||||
void InsertSkiplist(SkipList<int> *skiplist, int start, int end) {
|
||||
auto accessor = skiplist->access();
|
||||
|
||||
for (int start = 0; start < end; start++) {
|
||||
accessor.insert(std::move(start));
|
||||
}
|
||||
}
|
||||
|
||||
void InsertConcurrentSkiplist(SkipList<int> *skiplist, int start, int end) {
|
||||
int number_od_threads = std::thread::hardware_concurrency();
|
||||
std::vector<std::thread> threads(number_od_threads);
|
||||
|
||||
for (int i = 0; i < number_od_threads; i++) {
|
||||
int part = (end - start) / number_od_threads;
|
||||
threads[i] = std::thread(InsertSkiplist, skiplist, start + i * part,
|
||||
start + (i + 1) * part);
|
||||
}
|
||||
|
||||
for (int i = 0; i < number_od_threads; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
static void ReverseFromRBegin(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
|
||||
SkipList<int> skiplist;
|
||||
InsertConcurrentSkiplist(&skiplist, 0, state.range(0));
|
||||
int counter = 10;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
state.ResumeTiming();
|
||||
|
||||
for (int i = 0; i < counter; i++) {
|
||||
if (rbegin != rend) {
|
||||
rbegin++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FindFromRBegin(benchmark::State &state) {
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
|
||||
SkipList<int> skiplist;
|
||||
InsertConcurrentSkiplist(&skiplist, 0, state.range(0));
|
||||
int counter = 10;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
state.ResumeTiming();
|
||||
|
||||
for (int i = 0; i < counter; i++) {
|
||||
accessor.find(*rbegin - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto BM_ReverseFromRBegin = [](benchmark::State &state) {
|
||||
ReverseFromRBegin(state);
|
||||
};
|
||||
|
||||
auto BM_FindFromRBegin = [](benchmark::State &state) { FindFromRBegin(state); };
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
logging::init_async();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
benchmark::RegisterBenchmark("ReverseFromRBegin", BM_ReverseFromRBegin)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16);
|
||||
|
||||
benchmark::RegisterBenchmark("FindFromRBegin", BM_FindFromRBegin)
|
||||
->RangeMultiplier(2)
|
||||
->Range(1 << 10, 1 << 16);
|
||||
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
|
||||
return 0;
|
||||
}
|
420
tests/unit/skiplist_reverse_iteration.cpp
Normal file
420
tests/unit/skiplist_reverse_iteration.cpp
Normal file
@ -0,0 +1,420 @@
|
||||
/**
|
||||
@date: 2017-01-2017
|
||||
@authors: Sandi Fatic
|
||||
|
||||
@brief
|
||||
These tests are used to test the functionality of the reverse() function in
|
||||
a single threaded scenario. For more concurrent tests look in the concurrent
|
||||
testing folder.
|
||||
|
||||
@todo
|
||||
Concurrent tests are missing for now.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "logging/default.cpp"
|
||||
#include "utils/random/generator.h"
|
||||
|
||||
using utils::random::NumberGenerator;
|
||||
using IntegerGenerator = NumberGenerator<std::uniform_int_distribution<int>,
|
||||
std::default_random_engine, int>;
|
||||
|
||||
/*
|
||||
Tests Skiplist rbegin() and rend() iterators on a sequential dataset.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsBeginToEnd) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
while (rbegin != rend) {
|
||||
ASSERT_EQ(number_of_elements - 1, *rbegin);
|
||||
rbegin++;
|
||||
number_of_elements--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist rbegin() and rend() iterators on a random dataset.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsBeginToEnd) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
int end = elems_set.size();
|
||||
std::vector<int> elems_descending(end);
|
||||
|
||||
for (auto el : elems_set) {
|
||||
end--;
|
||||
accessor.insert(std::move(el));
|
||||
elems_descending[end] = el;
|
||||
}
|
||||
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
while (rbegin != rend) {
|
||||
ASSERT_EQ(elems_descending[end], *rbegin);
|
||||
end++;
|
||||
rbegin++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists. The skiplist uses a sequential
|
||||
dataset and the element provided exists is in range of the dataset. The
|
||||
reverse function should return an std::pair<iterator_to_element_before, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsElementExists) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
int element = 1024 * 8;
|
||||
auto reverse_pair = accessor.reverse(element);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
while (rbegin != rend) {
|
||||
ASSERT_EQ(element - 1, *rbegin);
|
||||
rbegin++;
|
||||
element--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists. The skiplist uses a random
|
||||
dataset and the element provide exists in the random dataset. The reverse
|
||||
function should return an std::pair<iterator_to_element_before, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsElementExists) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
int end = elems_set.size();
|
||||
std::vector<int> elems_descending(end);
|
||||
|
||||
for (auto el : elems_set) {
|
||||
end--;
|
||||
accessor.insert(std::move(el));
|
||||
elems_descending[end] = el;
|
||||
}
|
||||
|
||||
int middle = end / 2;
|
||||
auto reverse_pair = accessor.reverse(elems_descending[middle]);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
while (rbegin != rend) {
|
||||
ASSERT_EQ(elems_descending[middle + 1], *rbegin);
|
||||
middle++;
|
||||
rbegin++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists and the element is the first one
|
||||
in the skiplist. The skiplist uses a sequential dataset. The reverse function
|
||||
should return an std::pair<rend, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsMinimumElement) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto reverse_pair = accessor.reverse(0);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rbegin, rend);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists and the element is the first one
|
||||
in the skiplist. The skiplist uses a random dataset. The reverse function
|
||||
should return an std::pair<rend, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsMinimumElement) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
auto min_el = std::min_element(elems_set.begin(), elems_set.end());
|
||||
for (auto el : elems_set) accessor.insert(std::move(el));
|
||||
|
||||
auto reverse_pair = accessor.reverse(*min_el);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rbegin, rend);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists and the element is the last one
|
||||
in the skiplist. The skiplist uses a sequential dataset. The reverse function
|
||||
should return an std::pair<iterator_to_smaller_element, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsMaximumElement) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto reverse_pair = accessor.reverse(number_of_elements - 1);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rbeing_real = accessor.rbegin();
|
||||
rbeing_real++;
|
||||
|
||||
ASSERT_EQ(rbegin, rbeing_real);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element exists and the element is the last one
|
||||
in the skiplist. the skiplist uses a random dataset. The reverse function
|
||||
should return and std::pair,iterator_to_smaller_element, true>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsMaximumElement) {
|
||||
SkipList<int> skiplist;
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
auto max_el = std::max_element(elems_set.begin(), elems_set.end());
|
||||
for (auto el : elems_set) accessor.insert(std::move(el));
|
||||
|
||||
auto reverse_pair = accessor.reverse(*max_el);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, true);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rbeing_real = accessor.rbegin();
|
||||
rbeing_real++;
|
||||
|
||||
ASSERT_EQ(rbegin, rbeing_real);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skipslist reverse() when element out of bounds. The skiplist uses a
|
||||
sequential dataset and the element provided is bigger then the last element
|
||||
in skiplist. Reverse function should return an std::pair<rend, false>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsElementBiggerThanLast) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto reverse_pair = accessor.reverse(number_of_elements + 1);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, false);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rend, rbegin);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skipslist reverse() when element out of bounds. The skiplist uses a
|
||||
random dataset and the element provide is bigger then the last element in the
|
||||
skiplist. Reverse function should return an std::pair<rend, false>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsElementBiggerThanLast) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
auto max_el = std::max_element(elems_set.begin(), elems_set.end());
|
||||
for (auto el : elems_set) accessor.insert(std::move(el));
|
||||
|
||||
auto reverse_pair = accessor.reverse(*max_el + 1);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, false);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rend, rbegin);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skipslist reverse() when element out of bounds.
|
||||
The skiplist uses a sequential dataset and the element provided is lower
|
||||
then the first element in skiplist. Reverse function should return an
|
||||
std::pair<rend, false>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, SequentialIteratorsElementLowerThanFirst) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto reverse_pair = accessor.reverse(-1);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, false);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rend, rbegin);
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Skiplist reverse() when element out of bounds. The skiplist uses a
|
||||
random dataset and the element provided is lower then the first element in
|
||||
skiplist. Reverse function should return an std::pair<rend, false>.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, RandomIteratorsElementLowerThanFirst) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
IntegerGenerator generator(0, 1000000000);
|
||||
std::set<int> elems_set =
|
||||
utils::random::generate_set(generator, number_of_elements);
|
||||
|
||||
auto min_el = std::min_element(elems_set.begin(), elems_set.end());
|
||||
for (auto el : elems_set) accessor.insert(std::move(el));
|
||||
|
||||
auto reverse_pair = accessor.reverse(*min_el - 1);
|
||||
|
||||
ASSERT_EQ(reverse_pair.second, false);
|
||||
|
||||
auto rbegin = reverse_pair.first;
|
||||
auto rend = accessor.rend();
|
||||
|
||||
ASSERT_EQ(rend, rbegin);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Tests Skiplist ReverseIterator when concurrently inserting an element while
|
||||
iterating. The inserted element should also be traversed.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, InsertWhileIteratingTest) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 1; i < number_of_elements; i+=2) accessor.insert(std::move(i));
|
||||
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
for (int i = 0; i < number_of_elements; i+=2) accessor.insert(std::move(i));
|
||||
|
||||
int element = number_of_elements - 1;
|
||||
while (rbegin != rend) {
|
||||
ASSERT_EQ(element, *rbegin);
|
||||
rbegin++;
|
||||
element--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Tests Skiplist ReverseIterator when concurrently deleting an element while
|
||||
iterating. The deleted element shouldn't be traversed except if the element
|
||||
is deleted while pointing to the element.
|
||||
*/
|
||||
TEST(SkipListReverseIteratorTest, DeleteWhileIteratingTest) {
|
||||
SkipList<int> skiplist;
|
||||
auto accessor = skiplist.access();
|
||||
int number_of_elements = 1024 * 16;
|
||||
|
||||
for (int i = 0; i < number_of_elements; i++) accessor.insert(std::move(i));
|
||||
|
||||
auto rbegin = accessor.rbegin();
|
||||
auto rend = accessor.rend();
|
||||
|
||||
int element = number_of_elements - 2;
|
||||
// check element which will be deleted
|
||||
rbegin++;
|
||||
ASSERT_EQ(element, *rbegin);
|
||||
|
||||
// delete elements
|
||||
for (int i = 0; i < number_of_elements; i+=2) accessor.remove(i);
|
||||
|
||||
// check if still points to the same after delete
|
||||
ASSERT_EQ(element, *rbegin);
|
||||
rbegin++;
|
||||
|
||||
// check all deleted elements after
|
||||
while (rbegin != rend && element > 0) {
|
||||
ASSERT_EQ(element-1, *rbegin);
|
||||
rbegin++;
|
||||
element-=2;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
logging::init_sync();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Reference in New Issue
Block a user