有人想过如何编写一个完全无分支的内存管理器(用C ++编写)吗?我写了一个池,一个堆栈,一个队列和一个链表(从池中分配),但我想知道编写一个免费的通用内存管理器是多么合理。
这有助于为执行可靠的并发,有序CPU和缓存友好开发提供一个真正可重用的框架。
编辑:by branchless我的意思是不进行直接或间接的函数调用,也不使用ifs。我一直在想,我可能会实现一些首先将请求的大小更改为零以进行错误调用的东西,但实际上并没有真正得到更多。 我觉得这不是不可能的,但是这个练习的另一个方面就是在所说的“不友好”的处理器上进行分析,看看是否值得尽可能地努力避免分支。
答案 0 :(得分:2)
虽然我认为这不是一个好主意,但一个解决方案是预先分配各种log 2 大小的桶,愚蠢的伪代码:
class Allocator {
void* malloc(size_t size) {
int bucket = log2(size + sizeof(int));
int* pointer = reinterpret_cast<int*>(m_buckets[bucket].back());
m_buckets[bucket].pop_back();
*pointer = bucket; //Store which bucket this was allocated from
return pointer + 1; //Dont overwrite header
}
void free(void* pointer) {
int* temp = reinterpret_cast<int*>(pointer) - 1;
m_buckets[*temp].push_back(temp);
}
vector< vector<void*> > m_buckets;
};
(您当然也会用简单的数组+计数器替换std::vector
。)
编辑:为了使其更加健壮(即处理存储桶为空的情况),您必须添加某种形式的分支。
EDIT2:这是一个小型无分支log2
函数:
//returns the smallest x such that value <= (1 << x)
int
log2(int value) {
union Foo {
int x;
float y;
} foo;
foo.y = value - 1;
return ((foo.x & (0xFF << 23)) >> 23) - 126; //Extract exponent (base 2) of floating point number
}
这给出了分配的正确结果&lt; 33554432字节。如果你需要更大的分配,你将不得不切换到双打。
这是浮点数如何在内存中表示的link。
答案 1 :(得分:0)
我知道创建一个真正的无分支分配器的唯一方法是保留它可能提前使用的所有内存。否则,总会在某个地方出现一些隐藏代码,看看我们是否超过了某个当前容量,无论它是否在向量中隐藏push_back
检查大小是否超过使用的容量实现它或类似的东西。
以下是固定分配的一个粗略示例,它具有完全无分支malloc
和free
方法。
class FixedAlloc
{
public:
FixedAlloc(int element_size, int num_reserve)
{
element_size = max(element_size, sizeof(Chunk));
mem = new char[num_reserve * element_size];
char* ptr = mem;
free_chunk = reinterpret_cast<Chunk*>(ptr);
free_chunk->next = 0;
Chunk* last_chunk = free_chunk;
for (int j=1; j < num_reserve; ++j)
{
ptr += element_size;
Chunk* chunk = reinterpret_cast<Chunk*>(ptr);
chunk->next = 0;
last_chunk->next = chunk;
last_chunk = chunk;
}
}
~FixedAlloc()
{
delete[] mem;
}
void* malloc()
{
assert(free_chunk && free_chunk->next && "Reserve memory exhausted!");
Chunk* chunk = free_chunk;
free_chunk = free_chunk->next;
return chunk->mem;
}
void free(void* mem)
{
Chunk* chunk = static_cast<Chunk*>(mem);
chunk->next = free_chunk;
free_chunk = chunk;
}
private:
union Chunk
{
Chunk* next;
char mem[1];
};
char* mem;
Chunk* free_chunk;
};
由于它完全没有分支,如果你试图分配比最初保留的更多的内存,它只是段错误。它还具有尝试释放空指针的未定义行为。为了一个更简单的例子,我也避免处理对齐。