【发布时间】:2015-12-25 01:33:10
【问题描述】:
我有一个程序(完整代码 here)在第 46000 次迭代左右退出:
{
PROCESSER<MONO_CONT> processer;
c_start = std::clock();
for (unsigned long long i = 0; i < iterations; i++) {
BloombergLP::bdlma::BufferedSequentialAllocatoralloc(pool, sizeof(pool));
MONO_CONT* container = new(alloc) MONO_CONT(&alloc);
container->reserve(elements);
processer(container, elements);
}
c_end = std::clock();
std::cout << (c_end - c_start) * 1.0 / CLOCKS_PER_SEC << " ";
}
在这种情况下,MONO_CONT 是 vector<string, scoped_allocator_adaptor<alloc_adaptor<BloombergLP::bdlma::BufferedSequentialAllocator>>>。
我的理解是scoped_allocator_adaptor 将确保提供的分配器用于分配传入的字符串,从而确保在每次循环迭代结束时释放字符串(避免@1201ProgramAlarm 对问题的建议) . alloc_adapter 只是使 Bloomberg 分配器符合正确接口的包装器。
PROCESSER 是以下模板化仿函数,它只对模板化容器MONO_CONT 执行一些基本操作:
template<typename DS2>
struct process_DS2 {
void operator() (DS2 *ds2, size_t elements) {
escape(ds2);
for (size_t i = 0; i < elements; i++) {
ds2->emplace_back(&random_data[random_positions[i]], random_lengths[i]);
}
clobber();
}
};
请注意,escape 和 clobber 只是一些魔法,除了击败优化器之外什么都不做(如果您有兴趣,请参阅 this talk)。 random_data 只是一个包含垃圾的 chars 数组。 random_positions 将有效索引定义为 random_data。 random_lengths 定义了从random_positions 中的相应位置开始的有效字符串长度(不会超出垃圾数据的末尾)。
我有类似的代码运行完全相同的迭代次数,并且不会失败:
{
PROCESSER<MONO_CONT> processer;
c_start = std::clock();
for (unsigned long long i = 0; i < iterations; i++) {
BloombergLP::bdlma::BufferedSequentialAllocator alloc(pool, sizeof(pool));
MONO_CONT container(&alloc);
container.reserve(elements);
processer(&container, elements);
}
c_end = std::clock();
std::cout << (c_end - c_start) * 1.0 / CLOCKS_PER_SEC << " ";
}
这两个sn-ps的主要区别在于,首先我new将容器放入分配器,然后将分配器传递给容器,依靠分配器的销毁来释放所有的容器的内存(不必实际调用容器本身的析构函数)。在第二个 sn-p 中,我通过在循环的每次迭代结束时超出范围来允许更自然地破坏容器。
我正在使用 Clang 构建它,在 Debian 上的 Docker 容器中运行。关于问题可能是什么或如何开始调试的建议?
【问题讨论】:
-
第一个例子使用
placement-new。那么对析构函数的显式调用在哪里呢? isocpp.org/wiki/faq/dtors#placement-new -
@PaulMcKenzie 据我所知,它被定义为不调用析构函数,因为分配器的析构函数会清理内存,并且在容器的内存被处理后我从不访问容器。请查看第 3 章的最后 3 段,了解我为什么这样做的更多详细信息:open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0089r0.pdf
-
如果您关心分配器的效率,请考虑在循环外创建
MONO_CONT对象,在每次迭代后clear()ing 它,然后reserve()s 达到高水位标记并且很少需要完成任何分配。 -
@TonyD,通常这是一个很好的建议,但在这种情况下,我实际上是在对分配器性能进行基准测试
标签: c++ memory-management allocator