Lower DeferredDeleter memory allocations
Reviewers: teon.banek, msantl Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1688
This commit is contained in:
parent
218384786d
commit
25adfdb90c
@ -16,17 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class DeferredDeleter {
|
class DeferredDeleter {
|
||||||
public:
|
private:
|
||||||
/**
|
|
||||||
* @brief - keep track of what object was deleted at which time.
|
|
||||||
*/
|
|
||||||
struct DeletedObject {
|
struct DeletedObject {
|
||||||
const T *object;
|
T *object;
|
||||||
const tx::TransactionId deleted_at;
|
tx::TransactionId deleted_at;
|
||||||
DeletedObject(const T *object, tx::TransactionId deleted_at)
|
|
||||||
: object(object), deleted_at(deleted_at) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief - check if everything is freed
|
* @brief - check if everything is freed
|
||||||
*/
|
*/
|
||||||
@ -36,22 +32,15 @@ class DeferredDeleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief - Add objects to this deleter. This method assumes that it will
|
* @brief - Add object to this deleter. This method assumes that it will
|
||||||
* always be called with a non-decreasing sequence of `deleted_at`.
|
* always be called with a non-decreasing sequence of `deleted_at`.
|
||||||
* @param objects - vector of objects to add
|
* @param object - object to add
|
||||||
* @param last_transaction - nothing newer or equal to it can see these
|
* @param deleted_at - when was the object deleted
|
||||||
* objects
|
|
||||||
*/
|
*/
|
||||||
void AddObjects(const std::vector<DeletedObject> &objects) {
|
void AddObject(T *object, tx::TransactionId deleted_at) {
|
||||||
auto previous_tx_id = objects_.empty()
|
CHECK(previous_tx_id_ <= deleted_at) << "deleted_at must be non-decreasing";
|
||||||
? std::numeric_limits<tx::TransactionId>::min()
|
previous_tx_id_ = deleted_at;
|
||||||
: objects_.back().deleted_at;
|
objects_.push_back(DeletedObject{object, deleted_at});
|
||||||
for (auto object : objects) {
|
|
||||||
CHECK(previous_tx_id <= object.deleted_at)
|
|
||||||
<< "deleted_at must be non-decreasing";
|
|
||||||
previous_tx_id = object.deleted_at;
|
|
||||||
objects_.push_back(object);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,4 +64,6 @@ class DeferredDeleter {
|
|||||||
private:
|
private:
|
||||||
// Ascendingly sorted list of deleted objects by `deleted_at`.
|
// Ascendingly sorted list of deleted objects by `deleted_at`.
|
||||||
std::list<DeletedObject> objects_;
|
std::list<DeletedObject> objects_;
|
||||||
|
// Last transaction ID that had deleted objects
|
||||||
|
tx::TransactionId previous_tx_id_{0};
|
||||||
};
|
};
|
||||||
|
@ -34,33 +34,22 @@ class GarbageCollector {
|
|||||||
void Run(const tx::Snapshot &snapshot, const tx::Engine &engine) {
|
void Run(const tx::Snapshot &snapshot, const tx::Engine &engine) {
|
||||||
auto collection_accessor = collection_.access();
|
auto collection_accessor = collection_.access();
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
std::vector<typename DeferredDeleter<TRecord>::DeletedObject>
|
|
||||||
deleted_records;
|
|
||||||
std::vector<
|
|
||||||
typename DeferredDeleter<mvcc::VersionList<TRecord>>::DeletedObject>
|
|
||||||
deleted_version_lists;
|
|
||||||
for (auto id_vlist : collection_accessor) {
|
for (auto id_vlist : collection_accessor) {
|
||||||
mvcc::VersionList<TRecord> *vlist = id_vlist.second;
|
mvcc::VersionList<TRecord> *vlist = id_vlist.second;
|
||||||
// If the version_list is empty, i.e. there is nothing else to be read
|
// If the version_list is empty, i.e. there is nothing else to be read
|
||||||
// from it we can delete it.
|
// from it we can delete it.
|
||||||
auto ret = vlist->GcDeleted(snapshot, engine);
|
auto ret = vlist->GcDeleted(snapshot, engine);
|
||||||
if (ret.first) {
|
if (ret.first) {
|
||||||
deleted_version_lists.emplace_back(vlist, engine.LocalLast());
|
version_list_deleter_.AddObject(vlist, engine.LocalLast());
|
||||||
count += collection_accessor.remove(id_vlist.first);
|
count += collection_accessor.remove(id_vlist.first);
|
||||||
}
|
}
|
||||||
if (ret.second != nullptr)
|
if (ret.second != nullptr) {
|
||||||
deleted_records.emplace_back(ret.second, engine.LocalLast());
|
record_deleter_.AddObject(ret.second, engine.LocalLast());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DLOG_IF(INFO, count > 0)
|
DLOG_IF(INFO, count > 0)
|
||||||
<< "GC started cleaning with snapshot: " << snapshot;
|
<< "GC started cleaning with snapshot: " << snapshot;
|
||||||
DLOG_IF(INFO, count > 0) << "Destroyed: " << count;
|
DLOG_IF(INFO, count > 0) << "Destroyed: " << count;
|
||||||
|
|
||||||
// Add records to deleter, with the id larger or equal than the last active
|
|
||||||
// transaction.
|
|
||||||
record_deleter_.AddObjects(deleted_records);
|
|
||||||
// Add version_lists to deleter, with the id larger or equal than the last
|
|
||||||
// active transaction.
|
|
||||||
version_list_deleter_.AddObjects(deleted_version_lists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -16,17 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class DeferredDeleter {
|
class DeferredDeleter {
|
||||||
public:
|
private:
|
||||||
/**
|
|
||||||
* @brief - keep track of what object was deleted at which time.
|
|
||||||
*/
|
|
||||||
struct DeletedObject {
|
struct DeletedObject {
|
||||||
const T *object;
|
T *object;
|
||||||
const tx::TransactionId deleted_at;
|
tx::TransactionId deleted_at;
|
||||||
DeletedObject(const T *object, tx::TransactionId deleted_at)
|
|
||||||
: object(object), deleted_at(deleted_at) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief - check if everything is freed
|
* @brief - check if everything is freed
|
||||||
*/
|
*/
|
||||||
@ -36,22 +32,15 @@ class DeferredDeleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief - Add objects to this deleter. This method assumes that it will
|
* @brief - Add object to this deleter. This method assumes that it will
|
||||||
* always be called with a non-decreasing sequence of `deleted_at`.
|
* always be called with a non-decreasing sequence of `deleted_at`.
|
||||||
* @param objects - vector of objects to add
|
* @param object - object to add
|
||||||
* @param last_transaction - nothing newer or equal to it can see these
|
* @param deleted_at - when was the object deleted
|
||||||
* objects
|
|
||||||
*/
|
*/
|
||||||
void AddObjects(const std::vector<DeletedObject> &objects) {
|
void AddObject(T *object, tx::TransactionId deleted_at) {
|
||||||
auto previous_tx_id = objects_.empty()
|
CHECK(previous_tx_id_ <= deleted_at) << "deleted_at must be non-decreasing";
|
||||||
? std::numeric_limits<tx::TransactionId>::min()
|
previous_tx_id_ = deleted_at;
|
||||||
: objects_.back().deleted_at;
|
objects_.push_back(DeletedObject{object, deleted_at});
|
||||||
for (auto object : objects) {
|
|
||||||
CHECK(previous_tx_id <= object.deleted_at)
|
|
||||||
<< "deleted_at must be non-decreasing";
|
|
||||||
previous_tx_id = object.deleted_at;
|
|
||||||
objects_.push_back(object);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,4 +64,6 @@ class DeferredDeleter {
|
|||||||
private:
|
private:
|
||||||
// Ascendingly sorted list of deleted objects by `deleted_at`.
|
// Ascendingly sorted list of deleted objects by `deleted_at`.
|
||||||
std::list<DeletedObject> objects_;
|
std::list<DeletedObject> objects_;
|
||||||
|
// Last transaction ID that had deleted objects
|
||||||
|
tx::TransactionId previous_tx_id_{0};
|
||||||
};
|
};
|
||||||
|
@ -34,33 +34,22 @@ class GarbageCollector {
|
|||||||
void Run(const tx::Snapshot &snapshot, const tx::Engine &engine) {
|
void Run(const tx::Snapshot &snapshot, const tx::Engine &engine) {
|
||||||
auto collection_accessor = collection_.access();
|
auto collection_accessor = collection_.access();
|
||||||
uint64_t count = 0;
|
uint64_t count = 0;
|
||||||
std::vector<typename DeferredDeleter<TRecord>::DeletedObject>
|
|
||||||
deleted_records;
|
|
||||||
std::vector<
|
|
||||||
typename DeferredDeleter<mvcc::VersionList<TRecord>>::DeletedObject>
|
|
||||||
deleted_version_lists;
|
|
||||||
for (auto id_vlist : collection_accessor) {
|
for (auto id_vlist : collection_accessor) {
|
||||||
mvcc::VersionList<TRecord> *vlist = id_vlist.second;
|
mvcc::VersionList<TRecord> *vlist = id_vlist.second;
|
||||||
// If the version_list is empty, i.e. there is nothing else to be read
|
// If the version_list is empty, i.e. there is nothing else to be read
|
||||||
// from it we can delete it.
|
// from it we can delete it.
|
||||||
auto ret = vlist->GcDeleted(snapshot, engine);
|
auto ret = vlist->GcDeleted(snapshot, engine);
|
||||||
if (ret.first) {
|
if (ret.first) {
|
||||||
deleted_version_lists.emplace_back(vlist, engine.LocalLast());
|
version_list_deleter_.AddObject(vlist, engine.LocalLast());
|
||||||
count += collection_accessor.remove(id_vlist.first);
|
count += collection_accessor.remove(id_vlist.first);
|
||||||
}
|
}
|
||||||
if (ret.second != nullptr)
|
if (ret.second != nullptr) {
|
||||||
deleted_records.emplace_back(ret.second, engine.LocalLast());
|
record_deleter_.AddObject(ret.second, engine.LocalLast());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DLOG_IF(INFO, count > 0)
|
DLOG_IF(INFO, count > 0)
|
||||||
<< "GC started cleaning with snapshot: " << snapshot;
|
<< "GC started cleaning with snapshot: " << snapshot;
|
||||||
DLOG_IF(INFO, count > 0) << "Destroyed: " << count;
|
DLOG_IF(INFO, count > 0) << "Destroyed: " << count;
|
||||||
|
|
||||||
// Add records to deleter, with the id larger or equal than the last active
|
|
||||||
// transaction.
|
|
||||||
record_deleter_.AddObjects(deleted_records);
|
|
||||||
// Add version_lists to deleter, with the id larger or equal than the last
|
|
||||||
// active transaction.
|
|
||||||
version_list_deleter_.AddObjects(deleted_version_lists);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -10,10 +10,8 @@
|
|||||||
TEST(DeferredDeleter, AddObjects) {
|
TEST(DeferredDeleter, AddObjects) {
|
||||||
DeferredDeleter<Vertex> deleter;
|
DeferredDeleter<Vertex> deleter;
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
std::vector<DeferredDeleter<Vertex>::DeletedObject> v;
|
deleter.AddObject(new Vertex, 5);
|
||||||
v.emplace_back(new Vertex, 5);
|
deleter.AddObject(new Vertex, 5);
|
||||||
v.emplace_back(new Vertex, 5);
|
|
||||||
deleter.AddObjects(v);
|
|
||||||
EXPECT_EQ(deleter.Count(), (i + 1) * 2);
|
EXPECT_EQ(deleter.Count(), (i + 1) * 2);
|
||||||
}
|
}
|
||||||
deleter.FreeExpiredObjects(tx::Transaction::MaxId());
|
deleter.FreeExpiredObjects(tx::Transaction::MaxId());
|
||||||
@ -24,10 +22,8 @@ TEST(DeferredDeleter, Destructor) {
|
|||||||
std::atomic<int> count{0};
|
std::atomic<int> count{0};
|
||||||
DeferredDeleter<DestrCountRec> *deleter = new DeferredDeleter<DestrCountRec>;
|
DeferredDeleter<DestrCountRec> *deleter = new DeferredDeleter<DestrCountRec>;
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
std::vector<DeferredDeleter<DestrCountRec>::DeletedObject> v;
|
deleter->AddObject(new DestrCountRec(count), 5);
|
||||||
v.emplace_back(new DestrCountRec(count), 5);
|
deleter->AddObject(new DestrCountRec(count), 5);
|
||||||
v.emplace_back(new DestrCountRec(count), 5);
|
|
||||||
deleter->AddObjects(v);
|
|
||||||
EXPECT_EQ(deleter->Count(), (i + 1) * 2);
|
EXPECT_EQ(deleter->Count(), (i + 1) * 2);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(0, count);
|
EXPECT_EQ(0, count);
|
||||||
@ -40,11 +36,9 @@ TEST(DeferredDeleter, Destructor) {
|
|||||||
// Check if deleter frees objects.
|
// Check if deleter frees objects.
|
||||||
TEST(DeferredDeleter, FreeExpiredObjects) {
|
TEST(DeferredDeleter, FreeExpiredObjects) {
|
||||||
DeferredDeleter<DestrCountRec> deleter;
|
DeferredDeleter<DestrCountRec> deleter;
|
||||||
std::vector<DeferredDeleter<DestrCountRec>::DeletedObject> v;
|
|
||||||
std::atomic<int> count{0};
|
std::atomic<int> count{0};
|
||||||
v.emplace_back(new DestrCountRec(count), 5);
|
deleter.AddObject(new DestrCountRec(count), 5);
|
||||||
v.emplace_back(new DestrCountRec(count), 5);
|
deleter.AddObject(new DestrCountRec(count), 5);
|
||||||
deleter.AddObjects(v);
|
|
||||||
|
|
||||||
deleter.FreeExpiredObjects(5);
|
deleter.FreeExpiredObjects(5);
|
||||||
EXPECT_EQ(deleter.Count(), 2);
|
EXPECT_EQ(deleter.Count(), 2);
|
||||||
|
Loading…
Reference in New Issue
Block a user