2010-02-02 75 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