2010-02-02 73 views
7

我在C++程序中遇到了一个运行时错误“double free or corruption”,它调用了可靠的库ANN并使用OpenMP来并行化for循环。double free or corruption

*** glibc detected *** /home/tim/test/debug/test: double free or corruption (!prev): 0x0000000002527260 ***  

这是否意味着地址0x0000000002527260处的内存被释放多次?

错误发生在“_search_struct-> annkSearch(queryPt,k_max,nnIdx,dists,_eps);”内部函数classify_various_k(),该函数反过来在函数tune_complexity()内的OpenMP for-loop内。

请注意,当OpenMP有多个线程时会发生错误,并且在单线程情况下不会发生。不知道为什么。

以下是我的代码。如果诊断不够,请告诉我。谢谢你的帮助!

void KNNClassifier::train(int nb_examples, int dim, double **features, int * labels) {       
     _nPts = nb_examples; 

     _labels = labels; 
     _dataPts = features; 

     setting_ANN(_dist_type,1); 

    delete _search_struct; 
    if(strcmp(_search_neighbors, "brutal") == 0) {                 
     _search_struct = new ANNbruteForce(_dataPts, _nPts, dim); 
    }else if(strcmp(_search_neighbors, "kdtree") == 0) { 
     _search_struct = new ANNkd_tree(_dataPts, _nPts, dim); 
     } 

    } 


     void KNNClassifier::classify_various_k(int dim, double *feature, int label, int *ks, double * errors, int nb_ks, int k_max) {    
     ANNpoint  queryPt = 0;                             
     ANNidxArray nnIdx = 0;                           
     ANNdistArray dists = 0;                           

     queryPt = feature;  
     nnIdx = new ANNidx[k_max];                
     dists = new ANNdist[k_max];                     

     if(strcmp(_search_neighbors, "brutal") == 0) {                    
      _search_struct->annkSearch(queryPt, k_max, nnIdx, dists, _eps);  
     }else if(strcmp(_search_neighbors, "kdtree") == 0) {  
      _search_struct->annkSearch(queryPt, k_max, nnIdx, dists, _eps); // where error occurs  
     }  

     for (int j = 0; j < nb_ks; j++)  
     {  
      scalar_t result = 0.0;  
      for (int i = 0; i < ks[j]; i++) {                      
       result+=_labels[ nnIdx[i] ];  
      }  
      if (result*label<0) errors[j]++;  
     }  

     delete [] nnIdx;  
     delete [] dists;  

     }  

     void KNNClassifier::tune_complexity(int nb_examples, int dim, double **features, int *labels, int fold, char *method, int nb_examples_test, double **features_test, int *labels_test) {  
      int nb_try = (_k_max - _k_min)/scalar_t(_k_step);  
      scalar_t *error_validation = new scalar_t [nb_try];  
      int *ks = new int [nb_try];  

      for(int i=0; i < nb_try; i ++){  
      ks[i] = _k_min + _k_step * i;  
      }  

      if (strcmp(method, "ct")==0)                              
      {  

      train(nb_examples, dim, features, labels);// train once for all nb of nbs in ks                         

      for(int i=0; i < nb_try; i ++){  
       if (ks[i] > nb_examples){nb_try=i; break;}  
       error_validation[i] = 0;  
      }  

      int i = 0;  
     #pragma omp parallel shared(nb_examples_test, error_validation,features_test, labels_test, nb_try, ks) private(i)  
      {  
     #pragma omp for schedule(dynamic) nowait  
       for (i=0; i < nb_examples_test; i++)   
       {  
       classify_various_k(dim, features_test[i], labels_test[i], ks, error_validation, nb_try, ks[nb_try - 1]); // where error occurs  
       }  
      }  
      for (i=0; i < nb_try; i++)  
      {  
       error_validation[i]/=nb_examples_test;  
      }  
      } 

      ...... 
    } 

UPDATE:

谢谢!我现在想通过使用“的#pragma OMP关键”纠正classify_various_k()写入同一个存储问题的冲突:

void KNNClassifier::classify_various_k(int dim, double *feature, int label, int *ks, double * errors, int nb_ks, int k_max) { 
    ANNpoint  queryPt = 0;  
    ANNidxArray nnIdx = 0;  
    ANNdistArray dists = 0;  

    queryPt = feature; //for (int i = 0; i < Vignette::size; i++){ queryPt[i] = vignette->content[i];}   
    nnIdx = new ANNidx[k_max];     
    dists = new ANNdist[k_max];    

    if(strcmp(_search_neighbors, "brutal") == 0) {// search 
    _search_struct->annkSearch(queryPt, k_max, nnIdx, dists, _eps); 
    }else if(strcmp(_search_neighbors, "kdtree") == 0) { 
    _search_struct->annkSearch(queryPt, k_max, nnIdx, dists, _eps); 
    } 

    for (int j = 0; j < nb_ks; j++) 
    { 
    scalar_t result = 0.0; 
    for (int i = 0; i < ks[j]; i++) {   
     result+=_labels[ nnIdx[i] ]; // Program received signal SIGSEGV, Segmentation fault 
    } 
    if (result*label<0) 
    { 
    #pragma omp critical 
    { 
     errors[j]++; 
    } 
    } 

    } 

    delete [] nnIdx; 
    delete [] dists; 

} 

然而,在“结果产生一个新段故障错误+ = _标签[nnIdx [一世] ];”。一些想法?谢谢!

+0

尝试它没有openmp - 它工作正常吗? – 2010-02-02 05:14:33

+1

如果你在OSX或Linux上,我推荐使用-g编译并通过valgrind运行。这应该为您找出错误。 – 2010-02-02 05:14:51

+0

@Kornel:它适用于单线程的情况。 – Tim 2010-02-02 05:22:39

回答

5

好的,既然你已经说过它可以在单线程的情况下正常工作,那么“正常”方法将不起作用。你需要做到以下几点:

  • 发现,在平行
  • 访问的所有变量尤其是看看那些被修改
  • 不叫删除共享资源
  • 看看在共享资源上运行的所有库函数 - 检查他们是否不分配/释放

这是候选人列表双删除:

shared(nb_examples_test, error_validation,features_test, labels_test, nb_try, ks) 

此外,此代码可能不是线程安全的:

 for (int i = 0; i < ks[j]; i++) { 
     result+=_labels[ nnIdx[i] ]; 
     }  
     if (result*label<0) errors[j]++; 

因为两个或多个进程可能会尝试做错误阵列的写操作。

and a big建议 - 尽量不要在线程模式下访问(特别是修改!)任何东西,这不是函数的参数!

+0

谢谢!共享变量不会在并行区域中解除分配。在并行区域内部,只有局部变量被分配和分配。 – Tim 2010-02-02 05:38:29

+0

@Tim,你的问题可能是腐败,而不是双重分配 - 如果两个处理器试图写入内存中的相同点,则可能发生损坏。 – 2010-02-02 05:40:27

+0

谢谢指出!这说得通。如何将写入同步到线程之间的共享错误? – Tim 2010-02-02 05:46:48

2

你的列车方法在分配新内存之前删除_search_struct。所以第一次列车被叫,它被删除。是否有代码在调用之前分配它来训练?你最终可能会试图删除垃圾内存(尽管我们没有要告诉的代码)。

+0

你知道你可以调用'delete 0;'对吗? – 2010-02-02 05:21:43

+0

是的,train()首先释放它,然后立即分配它。 – Tim 2010-02-02 05:21:49

+0

@Kornel,是删除0是允许的。你知道从第一次从给出的代码中删除它是0吗? @Tim。是的,我也是这么说的。第一次调用train时,_search_struct的值是多少? – 2010-02-02 05:24:10

4

我不知道这是不是你的问题,但:

void KNNClassifier::train(int nb_examples, int dim, double **features, int * labels) { 
    ... 
    delete _search_struct; 
    if(strcmp(_search_neighbors, "brutal") == 0) { 
    _search_struct = new ANNbruteForce(_dataPts, _nPts, dim); 
    }else if(strcmp(_search_neighbors, "kdtree") == 0) { 
    _search_struct = new ANNkd_tree(_dataPts, _nPts, dim); 
    } 
} 

,如果你不归类于ifelse if条款,会发生什么?您已删除_search_struct,并将其指向垃圾。之后您应该将其设置为NULL

如果这不是问题,你可以尝试更换:

delete p; 

有:

assert(p != NULL); 
delete p; 
p = NULL; 

(或类似的delete[]网站)。 (但是,这可能会对第一次调用KNNClassifier::train造成问题。)

另外,强制性:您是否确实需要执行所有这些手动分配和释放操作?你为什么不至少使用std::vector而不是new[]/delete[](它几乎总是坏的)?

+0

谢谢。但删除空指针是可能的。删除它后也指定0指针不能解决我的问题。 – Tim 2010-02-02 05:48:55

+0

@Tim:对,'删除NULL'是一个无操作(这就是为什么我建议使用'assert')。我没有看到你后来的编辑提到这在单线程场景中没有发生。 – jamesdlin 2010-02-02 05:54:13