快速移动任务与Howard Hinnant的short_alloc

问题描述 投票:11回答:1

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

让我感到震惊的是,从一个已经超出其竞技场并因此在堆上分配的向量进行移动分配可以使用通常的快速移动分配(即,抓取目标的资源)来完成。然而,这种情况并非如此:

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中所解释的那样,这是由于向量的移动赋值运算符比较了两个分配器(注意propagate_on_container_move_assignmentstd::false_type)。由于两个分配器不比较相等(因为它们具有不同的竞技场),目标向量需要分配内存并逐个移动值。

通过将等于运算符更改为,可以实现所需的行为

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()检查分配器是否未使用其竞技场。

这个解决方案看起来相当hackish(注意例如,平等不对称),我可以/我会通过这样做射击自己吗?有优雅的解决方案吗?

c++ memory-management c++11 move-semantics allocator
1个回答
2
投票

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

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

正确的解决方案是使用move运算符替换allocator对象。要做到这一点,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;

这将使移动操作符按预期工作。移动后它将开始使用其他竞技场。


In general, such allocation techniques are very dangerous and should be rarely used, due to their high risk of memory-related bugs. For example, if one is to return a vector from a function, there is a high risk for it to refer to an arena on a scope that is about to be exited.

根据我提议的变更,风险因素略高。 arena超出范围的问题现在也转向传递参考向量。当arena在内部块中定义时,arena超出范围的问题也存在。

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


As the question suggests, it is possible to mark a short_alloc allocator as a heap allocator when applicable. It can be marked this way right after the first allocation that uses new[]. This will work well with std::vector, since it holds only one chunk of memory between method calls. Despite working with std::vector, it breaks with most other containers, because most of them use nodes, such as std::map and std::unordered_set.

问题是一些节点来自arena,一些来自堆。如果使用operator==,建议的true返回new[],从std::map的移动将使来自不相关的竞技场的一些节点移动目标std::map。非常意外,也是个坏主意。这将导致std::map对象包含来自其自己的arena和来自无关的arena的节点。不相关的arena的节点永远不会被std::map释放。这些坏节点只有在分配arena时才会被释放。

问题中提出的技术完全被打破了。除了std::vector之外几乎任何事情都会以惊人的方式产生不一致的分配。我会强烈反对。

© www.soinside.com 2019 - 2024. All rights reserved.