C++ 程序无错退出。如何调试?
C++ program exiting without error. How to debug?
我有一个程序(完整代码 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
只是一个包含垃圾的 char
数组。 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 << " ";
}
这两个片段的主要区别在于,在第一个片段中,我 new
将容器放入分配器,然后将分配器传递给容器,依靠分配器的销毁来解除分配容器的所有内存(无需实际调用容器本身的析构函数)。在第二个片段中,我通过在每次循环迭代结束时超出范围来允许更自然地破坏容器。
我正在使用 Clang 构建它,运行 在 Debian 上的 Docker 容器中。关于问题可能是什么或如何开始调试的建议?
虽然您依靠分配器的销毁来释放分配给 container
的内存,但这不会释放所包含的 string
所使用的内存,后者不会使用分配器对于 vector
但将使用全局堆 (new
)。当程序 运行 内存不足时,它会退出而不报告任何内容,可能是因为它没有足够的可用内存来执行此操作。
在您的第二个版本中,container
被销毁,这将在 vector
被销毁时释放分配的字符串。
至于如何调试这样的东西,"try stepping thru it in the debugger" 的通常建议是一个开始。如果您 运行 附加到调试器中,它可能会在创建或抛出 std::bad_alloc
异常时中断。并且可以监控进程的内存使用情况。
我有一个程序(完整代码 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
只是一个包含垃圾的 char
数组。 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 << " ";
}
这两个片段的主要区别在于,在第一个片段中,我 new
将容器放入分配器,然后将分配器传递给容器,依靠分配器的销毁来解除分配容器的所有内存(无需实际调用容器本身的析构函数)。在第二个片段中,我通过在每次循环迭代结束时超出范围来允许更自然地破坏容器。
我正在使用 Clang 构建它,运行 在 Debian 上的 Docker 容器中。关于问题可能是什么或如何开始调试的建议?
虽然您依靠分配器的销毁来释放分配给 container
的内存,但这不会释放所包含的 string
所使用的内存,后者不会使用分配器对于 vector
但将使用全局堆 (new
)。当程序 运行 内存不足时,它会退出而不报告任何内容,可能是因为它没有足够的可用内存来执行此操作。
在您的第二个版本中,container
被销毁,这将在 vector
被销毁时释放分配的字符串。
至于如何调试这样的东西,"try stepping thru it in the debugger" 的通常建议是一个开始。如果您 运行 附加到调试器中,它可能会在创建或抛出 std::bad_alloc
异常时中断。并且可以监控进程的内存使用情况。