gpt4 book ai didi

c++ - 使用 Howard Hinnant 的 short_alloc 进行快速 move 分配

转载 作者:可可西里 更新时间:2023-11-01 15:26:35 25 4
gpt4 key购买 nike

我正在使用 Howard Hinnant 的漂亮的基于竞技场的小分配器,short_alloc .

令我震惊的是,从 vector 进行的 move 分配可以使用通常的快速 move 分配(即获取目标的资源)来完成,该 vector 已经超出其 arena,因此分配在堆上。然而,事实并非如此:

typedef arena<16>                     arena_type;
typedef short_alloc<int, 16> alloc_type;
typedef std::vector<int, alloc_type> vec_type;

arena_type arena1, arena2;
vec_type vec1(alloc_type(arena1)), vec2(alloc_type(arena2));
vec1.resize(100);

void* data = vec1.data();
vec2 = std::move(vec1);
assert(vec2.data() == data); // fails

this answer 中所述,这是由于 vector 的 move 赋值运算符比较了两个分配器(请注意 propagate_on_container_move_assignmentstd::false_type)。由于两个分配器比较不相等(因为它们有不同的领域),目标 vector 需要分配内存并一个一个地 move 值。

通过将相等运算符更改为来实现所需的行为

template <class T1, size_t N1, class T2, size_t N2>
bool operator==(const short_alloc<T1, N1>& x, const short_alloc<T2, N2>& y) noexcept
{
return N1 == N2 && (&x.a_ == &y.a_ || y.a_.on_heap());
}

其中 on_heap() 检查分配器是否未使用其 arena。

这个解决方案看起来很老套(例如,注意平等不是对称的),这样做我可以/会搬起石头砸自己的脚吗?有没有优雅的解决方案?

最佳答案

两个不同的 arena 对象可能有不同的生命周期。依赖于不同 arena 对象的两个不同 short_alloc 对象管理具有不同生命周期的内存。因此,具有不同 short_alloc 对象的两个 std::vector 对象不能简单地在它们之间 move 指针。

你的 hack 不会起作用,因为它是从 arenanew[] 分配的指针。您的黑客假设分配器成为大 vector 的堆分配器,但事实并非如此。如果不检查请求的大小或释放的指针,分配器就不知道这一点。

正确的解决方案是用 move 运算符替换分配器对象。为此,short_alloc 应该定义:

   using propagate_on_container_move_assignment = std::true_type;
private:
arena_type * a_; // <--- instead of reference
public:
short_alloc(const short_alloc&) = default;
// !!! Don't delete the move assignment !!!
// short_alloc& operator=(const short_alloc&) = delete;

这将使 move 运算符按预期工作。 move 后它将开始使用另一个竞技场。


一般来说,这种分配技术非常危险,应该很少使用,因为它们存在与内存相关的错误的高风险。例如,如果要从函数返回一个 vector ,则它在即将退出的作用域上引用 arena 的风险很高。

根据我提议的更改,风险系数略高。 arena 超出范围的问题现在也涉及到传递引用 vector 。当 arena 定义在内部 block 中时,arena 超出范围的问题也存在。

其他 arena 超出范围的行为可能会让程序员大吃一惊,并引入错误。这就是为什么我不喜欢这个解决方案。然而,有时人们愿意在时间关键部分编写危险代码(在分析和分析之后)。


如问题所示,可以在适用时将 short_alloc 分配器标记为 heap 分配器。它可以在使用 new[] 的第一次分配后立即以这种方式标记。这将适用于 std::vector,因为它在方法调用之间只保留一 block 内存。尽管使用 std::vector,但它与大多数其他容器不兼容,因为它们中的大多数都使用节点,例如 std::mapstd::unordered_set

问题是一些节点来自 arena 而一些来自堆。使用建议的 operator== 如果使用 new[] 则返回 true,从 std::map 将使来自不相关领域的一些节点 move 到目标 std::map。非常出乎意料,而且是个坏主意。这将生成一个 std::map 对象,该对象包含来自其自己的 arena 和来自不相关的 arena 的节点。不相关的arena 的节点永远不会被std::map 释放。只有当它们分配的 arena 死亡时,这些坏节点才会被释放。

问题中提出的技术完全被破坏了。除了 std::vector 之外,它会以令人惊讶的方式导致几乎所有内容的分配不一致。我强烈反对。

关于c++ - 使用 Howard Hinnant 的 short_alloc 进行快速 move 分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19230283/

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