c++ /Debugging (AIX上的c++)导致分段错误的递归快速排序
C++/Debugging (g++ on AIX) Recursive Quicksort Causing Segmentation Faults
我有一个程序,我需要对大量的大型数字分布进行排序。为了减少执行此操作所需的时间,我正在尝试将此多线程化。
我写了一个小的,简单的程序抽象,试图隔离问题。我相信我遇到了堆栈溢出,或者达到了操作系统的堆栈限制,因为我的测试程序在以下情况下反映了分段错误问题:
- 分布都是相同的值(意味着qsort将像垃圾一样运行)
- 线程被启用
猫叫
#include <boost/thread/thread.hpp>
#include <vector>
#include <stdlib.h> // for rand()
void swapvals(double *distribution, const size_t &d1, const size_t &d2)
{
double temp = 0;
temp = distribution[d2];
distribution[d2] = distribution[d1];
distribution[d1] = temp;
//std::swap(distribution[d1], distribution[d2]);
}
size_t partition(double *distribution, size_t left, size_t right)
{
const double pivot = distribution[right];
while (left < right) {
while ((left < right) && distribution[left] <= pivot)
left++;
while ((left < right) && distribution[right] > pivot)
right--;
if (left < right)
{
swapvals(distribution, left, right);
}
}
return right;
}
void quickSort(double *distribution, const size_t left, const size_t right)
{
if (left >= right) {
return;
}
size_t part = partition(distribution, left, right);
quickSort(distribution, left, part - 1);
quickSort(distribution, part + 1, right);
}
void processDistribution(double *distributions, const size_t distribution_size)
{
std::clog << "beginning qsorting." << std::endl;
quickSort(distributions, 0, distribution_size - 1);
std::clog << "done qsorting." << std::endl;
}
int main(int argc, char* argv[])
{
size_t distribution_size = 65000;
size_t num_distributions = 10;
std::vector<double *> distributions;
// Create num_distributions distributions.
for (int i = 0; i < num_distributions; i++)
{
double * new_dist = new double[distribution_size];
for (int k = 0; k < distribution_size; k++)
{
// Works when I have actual numbers in the distributions.
// Seg faults when all the numbers are the same.
new_dist[k] =1;
//new_dist[k] = rand() % 1000 + 1; // uncomment this, and it works.
}
distributions.push_back(new_dist);
}
// Submit each distribution to a quicksort thread.
boost::thread_group threads;
for (std::vector<double *>::const_iterator it=distributions.begin(); it != distributions.end(); ++it)
{
// It works when I run processDistribution directly. Segfaults when I run it via threads.
//processDistribution(*it, distribution_size);
threads.create_thread(boost::bind(&processDistribution, *it, distribution_size));
}
threads.join_all();
// Show the results of the sort for all the distributions.
for (std::vector<double *>::const_iterator it=distributions.begin(); it != distributions.end(); ++it)
{
for (size_t i = 0; i < distribution_size; i++)
{
// print first and last 20 results.
if (i < 20 || i > (distribution_size - 20))
std::cout << (*it)[i] << ",";
}
std::cout << std::endl;
}
}
核心文件的GDB分析结果:
Error in re-setting breakpoint -1: aix-thread: ptrace (52, 18220265) returned -1 (errno = 3 The process does not exist.)
Error in re-setting breakpoint -1: aix-thread: ptrace (52, 18220265) returned -1 (errno = 3 The process does not exist.)
Error in re-setting breakpoint -2: aix-thread: ptrace (52, 18220265) returned -1 (errno = 3 The process does not exist.)
Error in re-setting breakpoint -3: aix-thread: ptrace (52, 18220265) returned -1 (errno = 3 The process does not exist.)
Core was generated by `testthreads'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00000001000056bc in partition (distribution=0x1101d1430, left=0, right=63626) at testthreads.cpp:18
warning: Source file is more recent than executable.
18
(gdb) bt 7
#0 0x00000001000056bc in partition (distribution=0x1101d1430, left=0, right=63626) at testthreads.cpp:18
#1 0x0000000100005834 in quickSort (distribution=0x1101d1430, left=0, right=63626) at testthreads.cpp:42
#2 0x0000000100005850 in quickSort (distribution=0x1101d1430, left=0, right=63627) at testthreads.cpp:43
#3 0x0000000100005850 in quickSort (distribution=0x1101d1430, left=0, right=63628) at testthreads.cpp:43
#4 0x0000000100005850 in quickSort (distribution=0x1101d1430, left=0, right=63629) at testthreads.cpp:43
#5 0x0000000100005850 in quickSort (distribution=0x1101d1430, left=0, right=63630) at testthreads.cpp:43
#6 0x0000000100005850 in quickSort (distribution=0x1101d1430, left=0, right=63631) at testthreads.cpp:43
(More stack frames follow...)
(gdb) frame 0
#0 0x00000001000056bc in partition (distribution=0x1101d1430, left=0, right=63626) at testthreads.cpp:18
18
(gdb) info locals
pivot = 1
(gdb) info args
distribution = 0x1101d1430
left = 0
right = 63626
(gdb)
同样,我的实际程序处理更多的线程和发行版。那里的GDB检查经常显示更奇怪的堆栈跟踪,看起来像内存损坏(注意如何在d1 = 12119时调用swapVals,但在分区堆栈帧中它以4568618016的形式出现):
(gdb) bt 3
#0 0x00000001002aa0b8 in ScenRankReplacer<double>::swapvals (this=0xfffffffffffdfc8, distribution=..., d1=@0x1104c8178: 4568618016, d2=@0x1104c8140: 4568416720, ranking_values=0x1104c81d0,
r1=@0x1104c8170: 1152921504606838728, r2=@0x1002a16c8: 6917529029728344952) at ScenRankReplacer.h:96
#1 0x00000001002a7120 in ScenRankReplacer<double>::partition (this=0xfffffffffffdfc8, distribution=..., ranking_values=0x11069ae50, left=1, right=24237) at ScenRankReplacer.h:122
#2 0x00000001002a16c8 in ScenRankReplacer<double>::quickSort (this=0xfffffffffffdfc8, distribution=..., ranking_values=0x11069ae50, left=1, right=24237) at ScenRankReplacer.h:91
(More stack frames follow...)
(gdb) frame 1
#1 0x00000001002a7120 in ScenRankReplacer<double>::partition (this=0xfffffffffffdfc8, distribution=..., ranking_values=0x11069ae50, left=1, right=24237) at ScenRankReplacer.h:122
122 swapvals(distribution, mid, left, ranking_values, mid - 1, left - 1);
(gdb) p mid
$1 = 12119
(gdb) p left
$2 = 1
所以…我的问题:
- 我说的对吗?我达到堆栈限制了吗?
- 我究竟如何确定这是事实(除了我上面所做的推论)?有没有一种简单的方法来检测这些?GDB的线索还是什么? 为什么线程很重要?所有线程共享相同的内容吗堆栈限制?
- 最重要的:我如何得到这个工作?是一个在海量数据集上进行递归快速排序是不可行的?
编译级别为O2时发生错误。线程模型:aixgcc version 4.8.3 (gcc)
这看起来可能与堆栈空间有关。线程很重要,因为虽然所有线程都有自己的堆栈,但这些堆栈都共享相同的内存池。堆栈通常会根据需要增长,直到它们运行到已经使用的内存中,在这种情况下,可能是来自另一个线程的堆栈。单线程程序不会有这个问题,并且可以增加它的堆栈。(也有多个线程,你做多个排序在同一时间,这将需要更多的堆栈空间。)
解决这个问题的一种方法是删除递归,并使用一些循环和本地存储来替换它。类似这样的代码(未编译或已测试):void quickSort(double *distribution, size_t left, size_t right) {
std::vector<std::pair<size_t, size_t>> ranges;
for (;;) {
for (;;) {
if (left <= right)
break;
size_t part = partition(distribution, left, right);
// save range for later to replace the second recursive call
ranges.push_back(std::make_pair(part + 1, right));
// set right == part - 1, then loop, to replace the first recursive call
right = part - 1;
}
if (ranges.empty())
break;
// Take top off of ranges for the next loop, replacing the second recursive call
left = ranges.back().first;
right = ranges.back().second;
ranges.pop_back();
}
}
经过一番折腾,我终于找到了问题的答案。
-
我对吗?我达到堆栈限制了吗?我到底该怎么做确定事实是这样的(而不是我所做的推论)以上)?和
-
是否有一种简单的方法来检测这些?一个GDB线索或什么东西吗?
: 是的。程序溢出了堆栈。我无法确定一种直接的方法来确定AIX上就是这种情况。然而,当我将代码放入Windows上的visual studio 2015并运行它时,程序崩溃了,并出现了一个明确的"Stack Overflow"错误。
我希望有一种方法可以在AIX上得到一个明确的"堆栈溢出"错误,类似于VS的结果。我找不到办法。即使使用-fstack-check编译也没有给我一个存储错误:(
- 为什么线程很重要?所有线程都共享吗相同的堆栈限制?
- 最重要的:我如何得到这个工作?!是在海量数据集上进行递归快速排序是不可行的吗?
A: AIX上线程的默认堆栈大小非常小!
摘自IBM developerworks博客文章:
对于AIX上的32位编译应用程序,默认的pthread堆栈大小是96 KB;对于AIX上的64位编译应用程序,
我只能想到两种方法:A1:第一种方法是增加堆栈大小。
摘自IBM线程调试指南线程的最小堆栈大小是96KB。它也是默认的堆栈大小。这个数字可以在编译时使用pthread.h头文件中定义的PTHREAD_STACK_MIN符号常量来检索。
请注意,最大堆栈大小为256MB,即一个段的大小。这个限制由pthread.h头文件中的PTHREAD_STACK_MAX符号常量表示。
因此可以将堆栈大小增加到最大256MB,这是相当大的。
A2:另一种方法是简单地避免潜在的未绑定递归。我的数据集非常大。可能不足以花费256MB的堆栈,但是迭代地重写快速排序函数是相当简单的。
void quickSort_iter(double *distribution, size_t left, size_t right)
{
if (left >= right)
return;
std::stack<std::pair<size_t, size_t> > partition_stack;
partition_stack.push(std::pair<size_t, size_t>(left, right));
while (!partition_stack.empty())
{
left = partition_stack.top().first;
right = partition_stack.top().second;
partition_stack.pop();
size_t pivot = partition(distribution, left, right);
if (pivot > 1)
partition_stack.push(std::pair<size_t, size_t>(left, pivot - 1));
if (pivot + 1 < right)
partition_stack.push(std::pair<size_t, size_t>(pivot + 1, right));
}
}
std::stack是使用默认的std::allocator创建的,因此它在内部使用堆分配来存储排序分区的堆栈,因此不会与堆栈限制发生冲突。
- 分段故障(堆芯转储)矢量
- 数组的指针从不分段故障
- 在某些循环内使用vector.push_back时出现分段错误
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 在线编译器中的分段C++没有打印消息
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 当我的阵列太大时出现分段错误
- Windows 10-使用gtkmm-3.0库和g++[包括再现]的分段故障
- 分段错误当我试图运行程序时出错
- 在c++中初始化矩阵时出现分段错误(核心转储)
- 尝试使用集合函数时出现分段错误
- 我无法缩小此分段错误的原因
- g++的分段错误(在NaN上使用to_string两次时)
- 我是如何在这段代码中出现分段错误的
- 创建结构的数组时遇到分段错误
- 在c++中键入向量中的所有值后,得到分段错误(核心转储)
- 在 c++ 中实现 Trie 时出现分段错误
- 为什么 fstream 在打开带有格式的文件时会导致分段错误?
- 为什么我遇到分段错误?
- 动态类的分段错误(家庭作业问题)