added fast allocator implementations
This commit is contained in:
parent
686275ad30
commit
a9a3fc02b0
BIN
benchmark/allocator
Executable file
BIN
benchmark/allocator
Executable file
Binary file not shown.
46
benchmark/allocator.cpp
Normal file
46
benchmark/allocator.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "utils/memory/allocator.hpp"
|
||||
#include "utils/memory/maker.hpp"
|
||||
|
||||
struct TestStruct
|
||||
{
|
||||
TestStruct(int a, int b, int c, int d)
|
||||
: a(a), b(b), c(c), d(d) {}
|
||||
|
||||
int a, b, c, d;
|
||||
};
|
||||
|
||||
void test_classic(int N)
|
||||
{
|
||||
TestStruct** xs = new TestStruct*[N];
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
xs[i] = new TestStruct(i, i, i, i);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
delete xs[i];
|
||||
|
||||
delete[] xs;
|
||||
}
|
||||
|
||||
void test_fast(int N)
|
||||
{
|
||||
TestStruct** xs = makeme<TestStruct*>(N);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
xs[i] = makeme<TestStruct>(i, i, i, i);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
delete xs[i];
|
||||
|
||||
delete[] xs;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
constexpr int N = 20000000;
|
||||
test_classic(N);
|
||||
test_fast(N);
|
||||
return 0;
|
||||
}
|
@ -1,16 +1,17 @@
|
||||
#ifndef MEMGRAPH_DATA_MODEL_EDGE_HPP
|
||||
#define MEMGRAPH_DATA_MODEL_EDGE_HPP
|
||||
|
||||
template <class T>
|
||||
#include "json/all.hpp"
|
||||
#include "record.hpp"
|
||||
|
||||
struct Node;
|
||||
|
||||
template <class T>
|
||||
struct Edge
|
||||
struct Edge : Record
|
||||
{
|
||||
Node* from;
|
||||
Node* to;
|
||||
|
||||
Node<T>* from;
|
||||
Node<T>* to;
|
||||
T* data;
|
||||
json::Object* data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
13
data_model/graph.hpp
Normal file
13
data_model/graph.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef MEMGRAPH_DATA_MODEL_GRAPH_HPP
|
||||
#define MEMGRAPH_DATA_MODEL_GRAPH_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "node.hpp"
|
||||
#include "edge.hpp"
|
||||
|
||||
struct Graph
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
@ -3,14 +3,16 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "json/all.hpp"
|
||||
#include "record.hpp"
|
||||
#include "edge.hpp"
|
||||
|
||||
template <class T>
|
||||
struct Node
|
||||
struct Node : Record
|
||||
{
|
||||
std::vector<Edge<T>*> in;
|
||||
std::vector<Edge<T>*> out;
|
||||
T* data;
|
||||
std::vector<Edge*> in;
|
||||
std::vector<Edge*> out;
|
||||
|
||||
json::Object* data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
14
data_model/record.hpp
Normal file
14
data_model/record.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef MEMGRAPH_DATA_MODEL_RECORD_HPP
|
||||
#define MEMGRAPH_DATA_MODEL_RECORD_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class Record
|
||||
{
|
||||
uint64_t id;
|
||||
|
||||
// used by MVCC to keep track of what's visible to transactions
|
||||
uint64_t xmin, xmax;
|
||||
};
|
||||
|
||||
#endif
|
12
data_structures/sllist.hpp
Normal file
12
data_structures/sllist.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef MEMGRAPH_DATA_STRUCTURES_SLLIST_HPP
|
||||
#define MEMGRAPH_DATA_STRUCTURES_SLLIST_HPP
|
||||
|
||||
#include <list>
|
||||
|
||||
template <class T,
|
||||
class allocator=std::allocator<T>>
|
||||
class SafeList
|
||||
{
|
||||
};
|
||||
|
||||
#endif
|
@ -26,4 +26,4 @@ clean:
|
||||
|
||||
test:
|
||||
make
|
||||
./tests --success
|
||||
./tests
|
||||
|
68
test/allocator.cpp
Normal file
68
test/allocator.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "utils/memory/allocator.hpp"
|
||||
|
||||
TEST_CASE("A block of integers can be allocated")
|
||||
{
|
||||
constexpr int N = 100;
|
||||
|
||||
fast_allocator<int> a;
|
||||
|
||||
int* xs = a.allocate(N);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
xs[i] = i;
|
||||
|
||||
// can we read them back?
|
||||
for(int i = 0; i < N; ++i)
|
||||
REQUIRE(xs[i] == i);
|
||||
|
||||
// we should be able to free the memory
|
||||
a.deallocate(xs, N);
|
||||
}
|
||||
|
||||
TEST_CASE("Allocator should work with structures")
|
||||
{
|
||||
struct TestObject
|
||||
{
|
||||
TestObject(int a, int b, int c, int d)
|
||||
: a(a), b(b), c(c), d(d) {}
|
||||
|
||||
int a, b, c, d;
|
||||
};
|
||||
|
||||
fast_allocator<TestObject> a;
|
||||
|
||||
SECTION("Allocate a single object")
|
||||
{
|
||||
auto* test = a.allocate(1);
|
||||
*test = TestObject(1, 2, 3, 4);
|
||||
|
||||
REQUIRE(test->a == 1);
|
||||
REQUIRE(test->b == 2);
|
||||
REQUIRE(test->c == 3);
|
||||
REQUIRE(test->d == 4);
|
||||
|
||||
a.deallocate(test, 1);
|
||||
}
|
||||
|
||||
SECTION("Allocate a block of structures")
|
||||
{
|
||||
constexpr int N = 8;
|
||||
auto* tests = a.allocate(N);
|
||||
|
||||
// structures should not overlap!
|
||||
for(int i = 0; i < N; ++i)
|
||||
tests[i] = TestObject(i, i, i, i);
|
||||
|
||||
for(int i = 0; i < N; ++i)
|
||||
{
|
||||
REQUIRE(tests[i].a == i);
|
||||
REQUIRE(tests[i].b == i);
|
||||
REQUIRE(tests[i].c == i);
|
||||
REQUIRE(tests[i].d == i);
|
||||
}
|
||||
|
||||
a.deallocate(tests, N);
|
||||
}
|
||||
}
|
@ -5,8 +5,6 @@
|
||||
#include "catch.hpp"
|
||||
#include "utils/sync/spinlock.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
TEST_CASE("a thread can acquire and release the lock", "[spinlock]")
|
||||
{
|
||||
SpinLock lock;
|
||||
@ -30,7 +28,7 @@ void test_lock()
|
||||
x++;
|
||||
|
||||
REQUIRE(x < 2);
|
||||
std::this_thread::sleep_for(1s);
|
||||
std::this_thread::sleep_for(25ms);
|
||||
|
||||
x--;
|
||||
lock.release();
|
||||
@ -38,9 +36,11 @@ void test_lock()
|
||||
|
||||
TEST_CASE("only one thread at a time can own the lock", "[spinlock]")
|
||||
{
|
||||
constexpr int N = 64;
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
for(int i = 0; i < 10; ++i)
|
||||
for(int i = 0; i < N; ++i)
|
||||
threads.push_back(std::thread(test_lock));
|
||||
|
||||
for(auto& thread : threads){
|
||||
|
51
utils/memory/allocator.hpp
Normal file
51
utils/memory/allocator.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef MEMGRAPH_UTILS_MEMORY_ALLOCATOR_HPP
|
||||
#define MEMGRAPH_UTILS_MEMORY_ALLOCATOR_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
template <class Tp>
|
||||
struct fast_allocator {
|
||||
typedef Tp value_type;
|
||||
|
||||
fast_allocator() = default;
|
||||
|
||||
template <class T>
|
||||
fast_allocator(const fast_allocator<T>&) {}
|
||||
|
||||
Tp* allocate(std::size_t n);
|
||||
void deallocate(Tp* p, std::size_t n);
|
||||
};
|
||||
|
||||
template <class Tp>
|
||||
Tp* fast_allocator<Tp>::allocate(std::size_t n)
|
||||
{
|
||||
// hopefully we're using jemalloc here!
|
||||
Tp* mem = static_cast<Tp*>(malloc(n * sizeof(Tp)));
|
||||
|
||||
if(mem != nullptr)
|
||||
return mem;
|
||||
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
template <class Tp>
|
||||
void fast_allocator<Tp>::deallocate(Tp* p, std::size_t)
|
||||
{
|
||||
// hopefully we're using jemalloc here!
|
||||
free(p);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(const fast_allocator<T>&, const fast_allocator<U>&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(const fast_allocator<T>& a, const fast_allocator<U>& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
#endif
|
27
utils/memory/maker.hpp
Normal file
27
utils/memory/maker.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef MEMGRAPH_UTILS_MEMORY_MAKER_HPP
|
||||
#define MEMGRAPH_UTILS_MEMORY_MAKER_HPP
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "allocator.hpp"
|
||||
|
||||
template <class T,
|
||||
typename... Args,
|
||||
class allocator=fast_allocator<T>>
|
||||
T* makeme(Args... args)
|
||||
{
|
||||
allocator alloc;
|
||||
T* mem = alloc.allocate(1);
|
||||
return new (mem) T(args...);
|
||||
}
|
||||
|
||||
template <class T,
|
||||
class allocator=fast_allocator<T>>
|
||||
void takeme(T* mem)
|
||||
{
|
||||
allocator alloc;
|
||||
mem->~T();
|
||||
alloc.deallocate(mem, 1);
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user