mpz_add()导致分段大型程序错误

时间:2015-11-10 19:16:05

标签: c gcc segmentation-fault gdb gmp

我的问题如下。我必须编写一个计算斐波纳契数的一个非常大的元素的程序(它必须计算的最低值是pow(2,10)成员,最大的是pow(2,20)成员。为此,我使用GMP的mpz_t和它的计算功能。

我为此使用尾递归算法(稍后我必须让它并行运行)。问题是它会运行一段时间,然后突然:Segmentation fault (core dumped)

我告诉你我的代码,解释一下,所以你不必浪费时间搞清楚它并告诉你我要知道的事情。

int main(int argc, char** argv){
  char result[1000000]; char *r; r = result;
  long int n;
  mpz_t num;
  mpz_init(num);
  double start_t, end_t, total_t;
  start_t = omp_get_wtime();
  for(int i = 0; i < 11; i++){
    n = pow(2,i+10);
    fibo(num,n);
    char *d = mpz_get_str(NULL,10,num);
    strcpy(r,d);
    printf("The %ld. element of Fibonacci is: %s\n",n,result);
    fflush(stdout);
    memset(result,  0, sizeof result);
  }

  end_t = omp_get_wtime();
  total_t = end_t - start_t;

  printf("Time of running: %.6f\n",total_t);

  return 0; 
  }

main()函数基本上创建(并初始化)变量,设置时间测量,并在for循环中调用fibo()函数,获取结果并打印它。一切都完成后,程序会写出运行时间并退出。

void fibo(mpz_t res, long int n){
  if(n == 0){
      mpz_set_str(res,"0",10);      
      return;
  }else{
  mpz_t temp1;
  mpz_t temp2;
  mpz_init_set_si(temp1,0);
  mpz_init_set_si(temp2,1);
  fiboTail(res,n,1,temp1,temp2);
  mpz_clear(temp1);
  mpz_clear(temp2);
}

}

fibo()获得2个参数,第一个是mpz_t(对于那些不知道的人,这是一个指针,它将属于main()中创建的那个,所以最终值将返回那里以供进一步使用),第二个值是我们需要计算的元素的数量。如果元素编号为0,我们只返回“0”,否则我们生成两个mpz_t变量,设置一个两个“0”,另一个设置为“1”并将它们与其他一些参数一起交给fiboTail()

void fiboTail(mpz_t res, long int n, long int m, mpz_t fibPrev, mpz_t fibCurrent){
    if(n == m){
      mpz_set(res,fibCurrent);
    }else{
      mpz_add(fibPrev,fibPrev,fibCurrent);
      fiboTail(res,n, m + 1, fibCurrent, fibPrev);
    }
}

所以这个基本上是核心m计算我们已完成的添加次数,我们所处的元素,n是我们需要的元素数量,fibCurrentfibPrev是当前和之前的斐波那契分别是数字。

对于这个愚蠢的解释,我很抱歉,如果没有我试图解释,我认为大多数人都知道这一点。

所以,这个程序非常快。问题(Segmentation fault)在计算第131072个元素时发生(有时在较小的元素上,它的......随机(?))。然后程序停止大约相同数量的加法/ m值(并不总是在同一个值上,但接近那里),并显示前面提到的错误消息。我使用gcc进行编译(实际上使用Makefile),因此我添加了-g开关并使用gdb来获取更多信息。这是我发现的:

我在gdb中运行了该程序,并使用生成thisbacktrace

Here是在帧#0-5上使用info frame的详细堆栈信息。错误发生在mpz_add调用,但我不知道为什么。

如果您需要更多信息,我可以给他们,但是现在我不知道还有什么用处。

很抱歉这篇长篇文章,请提前感谢您的答案!

修改

由于mpz_add似乎已经死亡,我得到了电话的信息,你可以看到它:i.imgur.com/XOpTve1.png(对不起,不能发布超过2个链接:/)

2 个答案:

答案 0 :(得分:0)

不确定这是否有帮助,但这是用于查找fibonacci(n)的Lucas序列方法,这种方法很快并且可用于确认您的结果(例如结果的大小可能太大)。它类似于以矩阵形式实现fibnoacci(n)并使用重复平方将矩阵提升到n次幂,其中fib(n)= M ^ nx fib(0),其中M是2乘2矩阵,和fib()是一个2元素向量。 Lucas序列函数需要运行1 + log2(n)个循环,因此对于n = 2 ^ 20,它将需要21个循环。

uint64_t fibl(uint64_t n) {
    uint64_t a, b, p, q, qq, aq;
    a = q = 1;
    b = p = 0;
    while(1) {
        if(n & 1) {
            aq = a*q;
            a = b*q + aq + a*p;
            b = b*p + aq;
        }
        n >>= 1;
        if(n == 0)
            break;
        qq = q*q;
        q = 2*p*q + qq;
        p = p*p + qq;
    }
    return b;
}

答案 1 :(得分:0)

尝试这样做:不是反复进行可能未优化的尾调用,而是迭代计算值或使用memoization。该程序可能在某处内存不足。