src/demo was removed, src/examples and src/benchmarks were moved to the top level
This commit is contained in:
29
include/memory/deferred_recycler.hpp
Normal file
29
include/memory/deferred_recycler.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "recycler.hpp"
|
||||
|
||||
template <class T, class Allocator>
|
||||
class DeferredRecycler : Recycler<T, Allocator>
|
||||
{
|
||||
public:
|
||||
using Recycler<T, Allocator>::acquire;
|
||||
|
||||
void recycle(T* item)
|
||||
{
|
||||
auto guard = this->acquire_unique();
|
||||
dirty.push_back(item);
|
||||
}
|
||||
|
||||
void clean()
|
||||
{
|
||||
auto guard = this->acquire_unique();
|
||||
|
||||
for(auto item : dirty)
|
||||
this->recycle_or_delete(item);
|
||||
|
||||
dirty.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<T*> dirty;
|
||||
};
|
||||
148
include/memory/hp.hpp
Normal file
148
include/memory/hp.hpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace memory
|
||||
{
|
||||
|
||||
constexpr const size_t HP_SIZE = 128;
|
||||
|
||||
class HP
|
||||
{
|
||||
public:
|
||||
// this object can't be copied or moved
|
||||
HP(HP&) = delete;
|
||||
HP(HP&&) = delete;
|
||||
|
||||
// grabs a singleton instance
|
||||
static HP& get()
|
||||
{
|
||||
static HP hp;
|
||||
return hp;
|
||||
}
|
||||
|
||||
class reference
|
||||
{
|
||||
friend class HP;
|
||||
public:
|
||||
reference(reference&) = delete;
|
||||
|
||||
// this type shouldn't be copyable to avoid calling its destructor
|
||||
// multiple times, but should be movable
|
||||
reference(reference&& other)
|
||||
{
|
||||
this->idx = other.idx;
|
||||
|
||||
// set the index to a negative number to indicate that this
|
||||
// index has been moved and that you should not free its
|
||||
// hazard pointer
|
||||
other.idx = -1;
|
||||
}
|
||||
// hazard pointer is cleared once reference goes out of scope
|
||||
~reference()
|
||||
{
|
||||
// TODO: remove
|
||||
// std::cout << "reference destructor called: ";
|
||||
// std::cout << this->idx;
|
||||
// std::cout << std::endl;
|
||||
|
||||
// check if this reference was moved during its lifetime
|
||||
if(idx < 0)
|
||||
return;
|
||||
|
||||
auto& hp = HP::get();
|
||||
hp.clear(*this);
|
||||
}
|
||||
|
||||
reference& operator=(reference&& other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
reference(int64_t idx) : idx(idx) {}
|
||||
int64_t idx;
|
||||
};
|
||||
|
||||
friend class reference;
|
||||
|
||||
template <class T>
|
||||
reference insert(T* ptr)
|
||||
{
|
||||
auto p = reinterpret_cast<uintptr_t>(ptr);
|
||||
|
||||
while(true)
|
||||
{
|
||||
// try to find a free spot in the hazard pointer list
|
||||
for(size_t i = 0; i < HP_SIZE; ++i)
|
||||
{
|
||||
auto hazard = ptr_list[i].load();
|
||||
|
||||
// if this spot isn't free, continue searching
|
||||
if(hazard != 0)
|
||||
continue;
|
||||
|
||||
// try to take this spot, if we fail, then another thread has
|
||||
// just taken it. continue searching for a new one
|
||||
if(!ptr_list[i].compare_exchange_strong(hazard, p))
|
||||
continue;
|
||||
|
||||
// found a free spot! return a reference to this spot so it
|
||||
// can be cleared later
|
||||
return reference(i);
|
||||
}
|
||||
|
||||
// we didn't find any free spots, sleep for a while and try again
|
||||
// from the beginning, some other thread might have freed a spot
|
||||
// while we were traversing the lsit.
|
||||
usleep(250);
|
||||
}
|
||||
}
|
||||
|
||||
bool find(uintptr_t hptr)
|
||||
{
|
||||
for (size_t i = 0; i < HP_SIZE; ++i) {
|
||||
auto& hptr_i = ptr_list[i];
|
||||
|
||||
if (hptr_i != hptr)
|
||||
continue;
|
||||
|
||||
if (hptr_i.load() == 1)
|
||||
return true;
|
||||
|
||||
if (hptr_i.load() == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const HP& hp)
|
||||
{
|
||||
os << "Hazard pointers: ";
|
||||
for (size_t i = 0; i < HP_SIZE; ++i) {
|
||||
auto& hptr_i = hp.ptr_list[i];
|
||||
os << hptr_i.load() << " ";
|
||||
}
|
||||
return os << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
HP()
|
||||
{
|
||||
for(size_t i = 0; i < HP_SIZE; ++i)
|
||||
ptr_list[i].store(0);
|
||||
}
|
||||
|
||||
void clear(reference& ref)
|
||||
{
|
||||
ptr_list[ref.idx].store(0);
|
||||
}
|
||||
|
||||
std::atomic<uintptr_t> ptr_list[HP_SIZE];
|
||||
};
|
||||
|
||||
}
|
||||
26
include/memory/literals.hpp
Normal file
26
include/memory/literals.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace memory
|
||||
{
|
||||
namespace literals
|
||||
{
|
||||
|
||||
constexpr unsigned long long operator"" _GB(unsigned long long gb)
|
||||
{
|
||||
return 1024 * 1024 * 1024 * gb;
|
||||
}
|
||||
|
||||
constexpr unsigned long long operator"" _MB(unsigned long long mb)
|
||||
{
|
||||
return 1024 * 1024 * mb;
|
||||
}
|
||||
|
||||
constexpr unsigned long long operator"" _kB(unsigned long long kb)
|
||||
{
|
||||
return 1024 * kb;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
55
include/memory/memory.hpp
Normal file
55
include/memory/memory.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "storage/model/record.hpp"
|
||||
#include "storage/model/vertex.hpp"
|
||||
#include "storage/model/edge.hpp"
|
||||
|
||||
// TODO implement the memory engine using the allocator style allocation to
|
||||
// make this class non-dependent on the memory allocation strategy
|
||||
|
||||
// TODO implement real recycling of vertices and edges to improve performance
|
||||
class MemoryEngine
|
||||
{
|
||||
public:
|
||||
|
||||
template <class T,
|
||||
typename... Args>
|
||||
T* create(Args&&... args)
|
||||
{
|
||||
return new T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T* allocate()
|
||||
{
|
||||
return static_cast<T*>(malloc(sizeof(T)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void recycle(Record<T>* record)
|
||||
{
|
||||
recycle(&record->derived());
|
||||
}
|
||||
|
||||
void recycle(Vertex* v)
|
||||
{
|
||||
delete v;
|
||||
}
|
||||
|
||||
void recycle(Edge* e)
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::unique_lock<SpinLock> acquire()
|
||||
{
|
||||
return std::unique_lock<SpinLock>(lock);
|
||||
}
|
||||
|
||||
SpinLock lock;
|
||||
};
|
||||
46
include/memory/recycler.hpp
Normal file
46
include/memory/recycler.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T, class Allocator=std::allocator<T>>
|
||||
class Recycler : public Lockable<SpinLock>
|
||||
{
|
||||
static constexpr size_t default_max_reserved = 100;
|
||||
|
||||
public:
|
||||
Recycler() = default;
|
||||
Recycler(size_t max_reserved) : max_reserved(max_reserved) {}
|
||||
|
||||
template <class... Args>
|
||||
T* acquire(Args&&... args)
|
||||
{
|
||||
auto guard = acquire_unique();
|
||||
return fetch_or_create(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void release(T* item)
|
||||
{
|
||||
auto guard = acquire_unique();
|
||||
return recycle_or_delete(item);
|
||||
}
|
||||
|
||||
protected:
|
||||
Allocator alloc;
|
||||
size_t max_reserved {default_max_reserved};
|
||||
std::queue<T*> items;
|
||||
|
||||
template <class... Args>
|
||||
T* fetch_or_create(Args&&... args)
|
||||
{
|
||||
return new T(std::forward<Args>(args)...); // todo refactor :D
|
||||
}
|
||||
|
||||
void recycle_or_delete(T* item)
|
||||
{
|
||||
delete item; // todo refactor :D
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user