added fast allocator implementations

This commit is contained in:
Dominik Tomičević 2015-06-22 14:31:26 +02:00
parent 686275ad30
commit a9a3fc02b0
13 changed files with 250 additions and 16 deletions

BIN
benchmark/allocator Executable file

Binary file not shown.

46
benchmark/allocator.cpp Normal file
View 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;
}

View File

@ -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
View 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

View File

@ -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
View 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

View 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

View File

@ -26,4 +26,4 @@ clean:
test:
make
./tests --success
./tests

68
test/allocator.cpp Normal file
View 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);
}
}

View File

@ -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){

View 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
View 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