gpt4 book ai didi

c++ - Hinnant 的 short_alloc 和对齐保证

转载 作者:IT老高 更新时间:2023-10-28 12:48:00 29 4
gpt4 key购买 nike

我最近遇到了 Howard Hinnant 的 short_alloc这是我见过的自定义分配器的最佳示例。

但是当我花更多时间研究代码以将其集成到我的个人项目中时,我突然想到提供基于堆栈的分配的 arena 类可能并不总是返回正确对齐的内存.实际上,我担心只有第一次分配才能保证适当对齐(因为缓冲区本身具有强制对齐),请参见下面的相关代码片段:

template <std::size_t N>
class arena
{
static const std::size_t alignment = 16;
alignas(alignment) char buf_[N];
char* ptr_;
//...
};

template <std::size_t N>
char*
arena<N>::allocate(std::size_t n)
{
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
if (buf_ + N - ptr_ >= n)
{
char* r = ptr_;
ptr_ += n;
return r;
}
return static_cast<char*>(::operator new(n));
}

我可以想出几种方法来解决这个问题(以浪费一些内存为代价),最简单的方法是在 allocate/deallocate 函数中舍入 sizealignment 的倍数。

但在更改任何内容之前,我想确保我在这里没有遗漏任何东西......

最佳答案

这段代码是在我拥有 std::max_align_t 之前编写的在我的工具箱中(现在位于 <cstddef> 中)。我现在可以这样写:

static const std::size_t alignment = alignof(std::max_align_t);

在我的系统上,它与当前代码完全相同,但现在更便携。这是 new 的对齐方式和 malloc保证返回。一旦你有了这个“最大对齐”的缓冲区,你就可以在其中放入任何一种类型的数组。但是你不能使用相同的arena对于不同的类型(至少不是具有不同对齐要求的不同类型)。出于这个原因,也许最好使用模板 arena在第二个 size_t ,等于 alignof(T) .这样你就可以防止同样的arena避免被具有不同对齐要求的类型意外使用:

arena<N, alignof(T)>& a_;

假设每个分配来自 arena具有相同的对齐要求,并且假设缓冲区最大对齐,那么缓冲区中的每个分配都将针对 T 进行适当对齐.

例如在我的系统上alignof(std::max_align_t) == 16 .具有这种对齐方式的缓冲区可以保存以下数组:

  • 类型为 alignof == 1 .
  • 类型为 alignof == 2 .
  • 类型为 alignof == 4 .
  • 类型为 alignof == 8 .
  • 类型为 alignof == 16 .

由于某些环境可能支持具有“ super 对齐”要求的类型,因此需要添加额外的安全预防措施(例如在 short_alloc 内):

static_assert(alignof(T) <= alignof(std::max_align_t), "");

如果你是 super 偏执狂,你也可以检查一下 alignof(T)是 2 的幂,尽管 C++ 标准本身保证这将始终为真 ([basic.align]/p4)。

更新

我仔细研究了这个问题,并认为将请求的分配大小四舍五入到下一个 alignment (正如OP所建议的)是最好的解决方案。我更新了"short_alloc"在我的网站上执行此操作。

template <std::size_t N>
char*
arena<N>::allocate(std::size_t n)
{
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
n = align_up(n);
if (buf_ + N - ptr_ >= n)
{
char* r = ptr_;
ptr_ += n;
return r;
}
return static_cast<char*>(::operator new(n));
}

对于您知道不需要最大对齐分配的特殊情况(例如 vector<unsigned char> ),可以简单地调整 alignment适本地。也可以有short_alloc::allocate通过alignof(T)arena::allocateassert(requested_align <= alignment)

template <std::size_t N>
char*
arena<N>::allocate(std::size_t n, std::size_t requested_align)
{
assert(requested_align <= alignment);
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
n = align_up(n);
if (buf_ + N - ptr_ >= n)
{
char* r = ptr_;
ptr_ += n;
return r;
}
return static_cast<char*>(::operator new(n));
}

这会让您确信,如果您调整了 alignment向下,你没有向下调整太多。

再次更新!

我已更新 descriptioncode由于这个出色的问题(我多年来一直忽略此代码),因此对这个分配器的影响很大。

之前更新中提到的对齐检查现在在编译时完成(编译时错误总是优于运行时错误,甚至是断言)。

arenashort_alloc现在在对齐上进行了模板化,以便您可以轻松自定义您预期的对齐要求(如果您猜得太低,它会在编译时被捕获)。该模板参数默认为alignof(std::max_align_t) .

arena::allocate函数现在看起来像:

template <std::size_t N, std::size_t alignment>
template <std::size_t ReqAlign>
char*
arena<N, alignment>::allocate(std::size_t n)
{
static_assert(ReqAlign <= alignment, "alignment is too small for this arena");
assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena");
auto const aligned_n = align_up(n);
if (buf_ + N - ptr_ >= aligned_n)
{
char* r = ptr_;
ptr_ += aligned_n;
return r;
}
return static_cast<char*>(::operator new(n));
}

感谢别名模板,这个分配器比以往更容易使用。例如:

// Create a vector<T> template with a small buffer of 200 bytes.
// Note for vector it is possible to reduce the alignment requirements
// down to alignof(T) because vector doesn't allocate anything but T's.
// And if we're wrong about that guess, it is a comple-time error, not
// a run time error.
template <class T, std::size_t BufSize = 200>
using SmallVector = std::vector<T, short_alloc<T, BufSize, alignof(T)>>;

// Create the stack-based arena from which to allocate
SmallVector<int>::allocator_type::arena_type a;
// Create the vector which uses that arena.
SmallVector<int> v{a};

这不一定是此类分配器的最终决定。但希望这是您构建自定义分配器的坚实基础。

关于c++ - Hinnant 的 short_alloc 和对齐保证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33722907/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com