Remove unused datastructures
Reviewers: teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2639
This commit is contained in:
parent
c425624ba6
commit
0c7313bb5f
@ -1,99 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
/**
|
||||
* Bitset data structure with a number of bits provided in constructor.
|
||||
* @tparam TStore type of underlying bit storage: int32, int64, char, etc.
|
||||
*/
|
||||
template <typename TStore>
|
||||
class Bitset {
|
||||
public:
|
||||
/**
|
||||
* Create bitset.
|
||||
* @param sz size of bitset
|
||||
*/
|
||||
Bitset(size_t sz) : block_size_(8 * sizeof(TStore)) {
|
||||
if (sz % block_size_ != 0) sz += block_size_;
|
||||
blocks_.resize(sz / block_size_);
|
||||
}
|
||||
/**
|
||||
* Set bit to one.
|
||||
* @param idx position of bit.
|
||||
*/
|
||||
void Set(int idx) {
|
||||
DCHECK(idx >= 0) << "Invalid bit location.";
|
||||
DCHECK(idx < static_cast<int64_t>(blocks_.size()) * block_size_)
|
||||
<< "Invalid bit location.";
|
||||
int bucket = idx / block_size_;
|
||||
blocks_[bucket] |= TStore(1) << idx % block_size_;
|
||||
}
|
||||
/**
|
||||
* Return bit at position.
|
||||
* @param idx position of bit.
|
||||
* @return 1/0.
|
||||
*/
|
||||
bool At(int idx) const {
|
||||
DCHECK(idx >= 0) << "Invalid bit location.";
|
||||
DCHECK(idx < static_cast<int64_t>(blocks_.size()) * block_size_)
|
||||
<< "Invalid bit location.";
|
||||
int bucket = idx / block_size_;
|
||||
return (blocks_[bucket] >> (idx % block_size_)) & 1;
|
||||
}
|
||||
/**
|
||||
* Intersect two bitsets
|
||||
* @param other bitset.
|
||||
* @return intersection.
|
||||
*/
|
||||
Bitset<TStore> Intersect(const Bitset<TStore> &other) const {
|
||||
DCHECK(this->blocks_.size() == other.blocks_.size())
|
||||
<< "Bitsets are not of equal size.";
|
||||
Bitset<TStore> ret(this->blocks_.size() * this->block_size_);
|
||||
for (int i = 0; i < (int)this->blocks_.size(); ++i) {
|
||||
ret.blocks_[i] = this->blocks_[i] & other.blocks_[i];
|
||||
continue;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Positions of bits set to 1.
|
||||
* @return positions of bits set to 1.
|
||||
*/
|
||||
std::vector<int> Ones() const {
|
||||
std::vector<int> ret;
|
||||
int ret_idx = 0;
|
||||
for (auto x : blocks_) {
|
||||
while (x) {
|
||||
auto pos = CountTrailingZeroes(x & -x);
|
||||
x -= x & -x;
|
||||
ret.push_back(ret_idx + pos);
|
||||
}
|
||||
ret_idx += block_size_;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calculate number of trailing zeroes in a binary number, usually a power
|
||||
* of two.
|
||||
* @return number of trailing zeroes.
|
||||
*/
|
||||
size_t CountTrailingZeroes(TStore v) const {
|
||||
size_t ret = 0;
|
||||
while (v >> 32) ret += 32, v >>= 32;
|
||||
if (v >> 16) ret += 16, v >>= 16;
|
||||
if (v >> 8) ret += 8, v >>= 8;
|
||||
if (v >> 4) ret += 4, v >>= 4;
|
||||
if (v >> 2) ret += 2, v >>= 2;
|
||||
if (v >> 1) ret += 1, v >>= 1;
|
||||
return ret;
|
||||
}
|
||||
std::vector<TStore> blocks_;
|
||||
const size_t block_size_;
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
#include "glog/logging.h"
|
||||
|
||||
// Thread safe queue. Probably doesn't perform very well, but it works.
|
||||
template <typename T>
|
||||
class Queue {
|
||||
public:
|
||||
Queue() = default;
|
||||
Queue(const Queue &) = delete;
|
||||
Queue &operator=(const Queue &) = delete;
|
||||
Queue(Queue &&) = delete;
|
||||
Queue &operator=(Queue &&) = delete;
|
||||
|
||||
void Push(T x) {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
queue_.emplace(std::move(x));
|
||||
guard.unlock();
|
||||
cvar_.notify_one();
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Emplace(Args &&... args) {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
queue_.emplace(std::forward<Args>(args)...);
|
||||
guard.unlock();
|
||||
cvar_.notify_one();
|
||||
}
|
||||
|
||||
int64_t size() const {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
return queue_.size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
return queue_.empty();
|
||||
}
|
||||
|
||||
// Block until there is an element in the queue and then pop it from the queue
|
||||
// and return it. Function can return nullopt if Queue is signaled via
|
||||
// Shutdown function or if there is no element to pop after timeout elapses.
|
||||
std::optional<T> AwaitPop(std::chrono::system_clock::duration timeout =
|
||||
std::chrono::system_clock::duration::max()) {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto until = std::chrono::system_clock::time_point::max() - timeout > now
|
||||
? now + timeout
|
||||
: std::chrono::system_clock::time_point::max();
|
||||
cvar_.wait_until(guard, until,
|
||||
[this] { return !queue_.empty() || !alive_; });
|
||||
if (queue_.empty() || !alive_) return std::nullopt;
|
||||
std::optional<T> x(std::move(queue_.front()));
|
||||
queue_.pop();
|
||||
return x;
|
||||
}
|
||||
|
||||
// Nonblocking version of above function.
|
||||
std::optional<T> MaybePop() {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
if (queue_.empty()) return std::nullopt;
|
||||
std::optional<T> x(std::move(queue_.front()));
|
||||
queue_.pop();
|
||||
return x;
|
||||
}
|
||||
|
||||
// Notify all threads waiting on conditional variable to stop waiting. New
|
||||
// threads that try to Await will not block.
|
||||
void Shutdown() {
|
||||
std::unique_lock<std::mutex> guard(mutex_);
|
||||
alive_ = false;
|
||||
guard.unlock();
|
||||
cvar_.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
bool alive_ = true;
|
||||
std::queue<T> queue_;
|
||||
std::condition_variable cvar_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
@ -1,90 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
template <class uintXX_t = uint32_t>
|
||||
/**
|
||||
* UnionFind data structure. Provides means of connectivity
|
||||
* setting and checking in O(alpha(n)) amortized complexity. Memory
|
||||
* complexity is linear.
|
||||
*/
|
||||
class UnionFind {
|
||||
public:
|
||||
/**
|
||||
* Constructor, creates a UnionFind structure of fixed size.
|
||||
*
|
||||
* @param n Number of elements in the data structure.
|
||||
*/
|
||||
explicit UnionFind(uintXX_t n) : set_count_(n), rank_(n), parent_(n) {
|
||||
for (auto i = 0; i < n; ++i) rank_[i] = 0, parent_[i] = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects two elements (and thereby the sets they belong
|
||||
* to). If they are already connected the function has no effect.
|
||||
*
|
||||
* Has O(alpha(n)) amortized time complexity.
|
||||
*
|
||||
* @param p First element.
|
||||
* @param q Second element.
|
||||
*/
|
||||
void Connect(uintXX_t p, uintXX_t q) {
|
||||
auto rp = Root(p);
|
||||
auto rq = Root(q);
|
||||
|
||||
// if roots are equal, we don't have to do anything
|
||||
if (rp == rq) return;
|
||||
|
||||
// merge the subtree with the smaller rank to the root of the subtree with
|
||||
// the larger rank
|
||||
if (rank_[rp] < rank_[rq])
|
||||
parent_[rp] = rq;
|
||||
else if (rank_[rp] > rank_[rq])
|
||||
parent_[rq] = rp;
|
||||
else
|
||||
parent_[rq] = rp, rank_[rp] += 1;
|
||||
|
||||
// update the number of groups
|
||||
set_count_--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if two elements are connected. Has amortized O(alpha(n)) time
|
||||
* complexity.
|
||||
*
|
||||
* @param p First element.
|
||||
* @param q Second element.
|
||||
* @return See above.
|
||||
*/
|
||||
bool Find(uintXX_t p, uintXX_t q) { return Root(p) == Root(q); }
|
||||
|
||||
/**
|
||||
* Returns the number of disjoint sets in this UnionFind.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
uintXX_t Size() const { return set_count_; }
|
||||
|
||||
private:
|
||||
uintXX_t set_count_;
|
||||
|
||||
// array of subtree ranks
|
||||
std::vector<uintXX_t> rank_;
|
||||
|
||||
// array of tree indices
|
||||
std::vector<uintXX_t> parent_;
|
||||
|
||||
uintXX_t Root(uintXX_t p) {
|
||||
auto r = p;
|
||||
auto newp = p;
|
||||
|
||||
// find the node connected to itself, that's the root
|
||||
while (parent_[r] != r) r = parent_[r];
|
||||
|
||||
// do some path compression to enable faster searches
|
||||
while (p != r) newp = parent_[p], parent_[p] = r, p = newp;
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
@ -24,9 +24,6 @@ target_link_libraries(${test_prefix}bolt_encoder mg-communication mg-query)
|
||||
add_unit_test(commit_log_v2.cpp)
|
||||
target_link_libraries(${test_prefix}commit_log_v2 glog gflags)
|
||||
|
||||
add_unit_test(datastructure_union_find.cpp)
|
||||
target_link_libraries(${test_prefix}datastructure_union_find glog gflags)
|
||||
|
||||
add_unit_test(kvstore.cpp)
|
||||
target_link_libraries(${test_prefix}kvstore kvstore_lib glog)
|
||||
|
||||
@ -112,9 +109,6 @@ target_link_libraries(${test_prefix}typed_value mg-query)
|
||||
|
||||
# END mg-query
|
||||
|
||||
add_unit_test(queue.cpp)
|
||||
target_link_libraries(${test_prefix}queue glog gflags)
|
||||
|
||||
add_unit_test(skip_list.cpp)
|
||||
target_link_libraries(${test_prefix}skip_list mg-utils)
|
||||
|
||||
|
@ -1,80 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "data_structures/union_find.hpp"
|
||||
|
||||
void ExpectFully(UnionFind<> &uf, bool connected, int from = 0, int to = -1) {
|
||||
if (to == -1) to = uf.Size();
|
||||
|
||||
for (int i = from; i < to; i++)
|
||||
for (int j = from; j < to; j++)
|
||||
if (i != j) EXPECT_EQ(uf.Find(i, j), connected);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, InitialSizeTest) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
UnionFind<> uf(i);
|
||||
EXPECT_EQ(i, uf.Size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, ModifiedSizeTest) {
|
||||
UnionFind<> uf(10);
|
||||
EXPECT_EQ(10, uf.Size());
|
||||
|
||||
uf.Connect(0, 0);
|
||||
EXPECT_EQ(10, uf.Size());
|
||||
|
||||
uf.Connect(0, 1);
|
||||
EXPECT_EQ(9, uf.Size());
|
||||
|
||||
uf.Connect(2, 3);
|
||||
EXPECT_EQ(8, uf.Size());
|
||||
|
||||
uf.Connect(0, 2);
|
||||
EXPECT_EQ(7, uf.Size());
|
||||
|
||||
uf.Connect(1, 3);
|
||||
EXPECT_EQ(7, uf.Size());
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, Disconectivity) {
|
||||
UnionFind<> uf(10);
|
||||
ExpectFully(uf, false);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, ConnectivityAlongChain) {
|
||||
UnionFind<> uf(10);
|
||||
for (unsigned int i = 1; i < uf.Size(); i++) uf.Connect(i - 1, i);
|
||||
ExpectFully(uf, true);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, ConnectivityOnTree) {
|
||||
UnionFind<> uf(10);
|
||||
ExpectFully(uf, false);
|
||||
|
||||
uf.Connect(0, 1);
|
||||
uf.Connect(0, 2);
|
||||
ExpectFully(uf, true, 0, 3);
|
||||
ExpectFully(uf, false, 2);
|
||||
|
||||
uf.Connect(2, 3);
|
||||
ExpectFully(uf, true, 0, 4);
|
||||
ExpectFully(uf, false, 3);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, DisjointChains) {
|
||||
UnionFind<> uf(30);
|
||||
for (int i = 0; i < 30; i++) uf.Connect(i, i % 10 == 0 ? i : i - 1);
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
for (int j = 0; j < 30; j++)
|
||||
EXPECT_EQ(uf.Find(i, j), (j - (j % 10)) == (i - (i % 10)));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "data_structures/queue.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
TEST(Queue, PushMaybePop) {
|
||||
Queue<int> q;
|
||||
q.Push(1);
|
||||
EXPECT_EQ(*q.MaybePop(), 1);
|
||||
EXPECT_EQ(q.MaybePop(), std::nullopt);
|
||||
|
||||
q.Push(2);
|
||||
q.Push(3);
|
||||
EXPECT_EQ(*q.MaybePop(), 2);
|
||||
|
||||
q.Push(4);
|
||||
q.Push(5);
|
||||
EXPECT_EQ(*q.MaybePop(), 3);
|
||||
EXPECT_EQ(*q.MaybePop(), 4);
|
||||
EXPECT_EQ(*q.MaybePop(), 5);
|
||||
EXPECT_EQ(q.MaybePop(), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(Queue, Emplace) {
|
||||
Queue<std::pair<std::string, int>> q;
|
||||
q.Emplace("abc", 123);
|
||||
EXPECT_THAT(*q.MaybePop(), testing::Pair("abc", 123));
|
||||
}
|
||||
|
||||
TEST(Queue, Size) {
|
||||
Queue<int> q;
|
||||
EXPECT_EQ(q.size(), 0);
|
||||
|
||||
q.Push(1);
|
||||
EXPECT_EQ(q.size(), 1);
|
||||
|
||||
q.Push(1);
|
||||
EXPECT_EQ(q.size(), 2);
|
||||
|
||||
q.MaybePop();
|
||||
EXPECT_EQ(q.size(), 1);
|
||||
|
||||
q.MaybePop();
|
||||
EXPECT_EQ(q.size(), 0);
|
||||
q.MaybePop();
|
||||
EXPECT_EQ(q.size(), 0);
|
||||
}
|
||||
|
||||
TEST(Queue, Empty) {
|
||||
Queue<int> q;
|
||||
EXPECT_TRUE(q.empty());
|
||||
|
||||
q.Push(1);
|
||||
EXPECT_FALSE(q.empty());
|
||||
|
||||
q.MaybePop();
|
||||
EXPECT_TRUE(q.empty());
|
||||
}
|
||||
|
||||
TEST(Queue, AwaitPop) {
|
||||
Queue<int> q;
|
||||
std::thread t([&] {
|
||||
q.Push(1);
|
||||
q.Push(2);
|
||||
std::this_thread::sleep_for(200ms);
|
||||
q.Push(3);
|
||||
q.Push(4);
|
||||
});
|
||||
|
||||
EXPECT_EQ(*q.AwaitPop(), 1);
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
EXPECT_EQ(*q.AwaitPop(), 2);
|
||||
EXPECT_EQ(*q.AwaitPop(), 3);
|
||||
EXPECT_EQ(*q.AwaitPop(), 4);
|
||||
t.join();
|
||||
|
||||
std::thread t2([&] {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
q.Shutdown();
|
||||
});
|
||||
std::this_thread::sleep_for(200ms);
|
||||
EXPECT_EQ(q.AwaitPop(), std::nullopt);
|
||||
t2.join();
|
||||
}
|
||||
|
||||
TEST(Queue, AwaitPopTimeout) {
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
Queue<int> q;
|
||||
EXPECT_EQ(q.AwaitPop(100ms), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(Queue, Concurrent) {
|
||||
Queue<int> q;
|
||||
|
||||
const int kNumProducers = 10;
|
||||
const int kNumConsumers = 10;
|
||||
const int kNumElementsPerProducer = 300000;
|
||||
|
||||
std::vector<std::thread> producers;
|
||||
std::atomic<int> next{0};
|
||||
|
||||
for (int i = 0; i < kNumProducers; ++i) {
|
||||
producers.emplace_back([&] {
|
||||
for (int i = 0; i < kNumElementsPerProducer; ++i) {
|
||||
q.Push(next++);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<std::thread> consumers;
|
||||
std::vector<int> retrieved[kNumConsumers];
|
||||
std::atomic<int> num_retrieved{0};
|
||||
for (int i = 0; i < kNumConsumers; ++i) {
|
||||
consumers.emplace_back(
|
||||
[&](int thread_id) {
|
||||
while (true) {
|
||||
int count = num_retrieved++;
|
||||
if (count >= kNumProducers * kNumElementsPerProducer) break;
|
||||
retrieved[thread_id].push_back(*q.AwaitPop());
|
||||
}
|
||||
},
|
||||
i);
|
||||
}
|
||||
|
||||
for (auto &t : consumers) {
|
||||
t.join();
|
||||
}
|
||||
for (auto &t : producers) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(q.MaybePop(), std::nullopt);
|
||||
|
||||
std::set<int> all_elements;
|
||||
for (auto &r : retrieved) {
|
||||
all_elements.insert(r.begin(), r.end());
|
||||
}
|
||||
EXPECT_EQ(all_elements.size(), kNumProducers * kNumElementsPerProducer);
|
||||
EXPECT_EQ(*all_elements.begin(), 0);
|
||||
EXPECT_EQ(*all_elements.rbegin(),
|
||||
kNumProducers * kNumElementsPerProducer - 1);
|
||||
}
|
||||
} // namespace
|
Loading…
Reference in New Issue
Block a user