Add FIFO behaviour for unchecked code.
Consuming available slabs in LIFO order makes predicting address reuse harder but appears to have performance implications. Condition this on CHECK_CLIENT and instead use FIFO order on !CHECK_CLIENT builds.
This commit is contained in:
committed by
Matthew Parkinson
parent
4987a19fe9
commit
cec015f296
104
src/ds/seqset.h
104
src/ds/seqset.h
@@ -10,34 +10,66 @@
|
||||
namespace snmalloc
|
||||
{
|
||||
/**
|
||||
* Simple sequential queue of T.
|
||||
* Simple sequential set of T.
|
||||
*
|
||||
* Linked using the T::next field.
|
||||
*
|
||||
* Can be used in either Fifo or Lifo mode, which is
|
||||
* specified by template parameter.
|
||||
*/
|
||||
template<typename T>
|
||||
class SeqQueue
|
||||
template<typename T, bool Fifo = false>
|
||||
class SeqSet
|
||||
{
|
||||
/**
|
||||
* Field representation for Fifo behaviour.
|
||||
*/
|
||||
struct FieldFifo
|
||||
{
|
||||
T* head{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Field representation for Lifo behaviour.
|
||||
*/
|
||||
struct FieldLifo
|
||||
{
|
||||
T* head{nullptr};
|
||||
T** end{&head};
|
||||
};
|
||||
|
||||
static_assert(
|
||||
std::is_same<decltype(T::next), T*>::value,
|
||||
"T->next must be a queue pointer to T");
|
||||
T* head{nullptr};
|
||||
T** end{&head};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Empty queue
|
||||
* Field indirection to actual representation.
|
||||
* Different numbers of fields are required for the
|
||||
* two behaviours.
|
||||
*/
|
||||
constexpr SeqQueue() = default;
|
||||
std::conditional_t<Fifo, FieldFifo, FieldLifo> v;
|
||||
|
||||
/**
|
||||
* Check for empty
|
||||
*/
|
||||
SNMALLOC_FAST_PATH bool is_empty()
|
||||
{
|
||||
SNMALLOC_ASSERT(end != nullptr);
|
||||
return &head == end;
|
||||
if constexpr (Fifo)
|
||||
{
|
||||
return v.head == nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
SNMALLOC_ASSERT(v.end != nullptr);
|
||||
return &(v.head) == v.end;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Empty queue
|
||||
*/
|
||||
constexpr SeqSet() = default;
|
||||
|
||||
/**
|
||||
* Remove an element from the queue
|
||||
*
|
||||
@@ -46,11 +78,18 @@ namespace snmalloc
|
||||
SNMALLOC_FAST_PATH T* pop()
|
||||
{
|
||||
SNMALLOC_ASSERT(!this->is_empty());
|
||||
auto result = head;
|
||||
if (&(head->next) == end)
|
||||
end = &head;
|
||||
auto result = v.head;
|
||||
if constexpr (Fifo)
|
||||
{
|
||||
v.head = result->next;
|
||||
}
|
||||
else
|
||||
head = head->next;
|
||||
{
|
||||
if (&(v.head->next) == v.end)
|
||||
v.end = &(v.head);
|
||||
else
|
||||
v.head = v.head->next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -64,13 +103,20 @@ namespace snmalloc
|
||||
template<typename Fn>
|
||||
SNMALLOC_FAST_PATH void filter(Fn&& f)
|
||||
{
|
||||
T** prev = &head;
|
||||
// Check for empty case.
|
||||
if (prev == end)
|
||||
if (is_empty())
|
||||
return;
|
||||
|
||||
T** prev = &(v.head);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if constexpr (Fifo)
|
||||
{
|
||||
if (*prev == nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
T* curr = *prev;
|
||||
// Note must read curr->next before calling `f` as `f` is allowed to
|
||||
// mutate that field.
|
||||
@@ -85,12 +131,16 @@ namespace snmalloc
|
||||
// Keep element
|
||||
prev = &(curr->next);
|
||||
}
|
||||
|
||||
if (&(curr->next) == end)
|
||||
break;
|
||||
if constexpr (!Fifo)
|
||||
{
|
||||
if (&(curr->next) == v.end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if constexpr (!Fifo)
|
||||
{
|
||||
v.end = prev;
|
||||
}
|
||||
|
||||
end = prev;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,8 +148,16 @@ namespace snmalloc
|
||||
*/
|
||||
SNMALLOC_FAST_PATH void insert(T* item)
|
||||
{
|
||||
*end = item;
|
||||
end = &(item->next);
|
||||
if constexpr (Fifo)
|
||||
{
|
||||
item->next = v.head;
|
||||
v.head = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
*(v.end) = item;
|
||||
v.end = &(item->next);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
|
||||
Reference in New Issue
Block a user