2010-02-02 3 views
7

Я встретил ошибку времени выполнения «двойная свобода или коррупция» в моей программе на C++, которая вызывает надежную библиотеку ANN и использует OpenMP для блокировки цикла for.double free или 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(), которая, в свою очередь, внутри внутренней функции OpenMP for-loop tune_complexity().

Обратите внимание, что ошибка возникает, когда для 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:

Спасибо! Я сейчас пытаюсь исправить конфликт письменного же проблемы памяти в classify_various_k() с помощью «#pragma OMP критического»:

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 [i]]; ". Некоторая идея? Благодаря!

+0

Попробуйте это без openmp - он работает правильно? –

+1

Я рекомендую компилировать с -g и запускать valgrind, если вы находитесь на OSX или Linux. Это должно указывать на ошибку для вас. –

+0

@Kornel: он работает правильно для однопоточного корпуса. – Tim

ответ

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]++; 

Поскольку два или более процессов могут попытаться сделать запись в массив ошибок.

И большой совет - попробуйте не иметь доступа (особенно изменять!) Что угодно, в то время как в режиме с резьбой, это не параметр для функции!

+0

Спасибо! Общая переменная не освобождается в параллельной области. Внутри параллельной области выделяются и распределяются только локальные переменные. – Tim

+0

@ Тит, ваша проблема может быть коррупцией, а не двойным распределением - может произойти повреждение, если два процессора попытаются записать в одну и ту же точку памяти. –

+0

Спасибо за это! Это имеет смысл. Как синхронизировать запись в общие ошибки между потоками? – Tim

2

Ваш метод отправки удаляет _search_struct перед тем, как распределить ему новую память. Поэтому первый раз, когда вызывается поезд, он удаляется. Есть ли код, чтобы выделить его до этого вызова для обучения? Вы можете в конечном итоге попытаться удалить мусорную память (у нас нет кода, чтобы сказать, хотя).

+0

Вы знаете, что можете называть 'delete 0;' right? –

+0

Да, поезд() сначала освободите его, а затем сразу выделите. – Tim

+0

@ Kornel, yes delete 0 разрешено. Знаете ли вы, что это первый раз, когда он удаляется из приведенного кода? @Tim. Да, это то, что я тоже говорю. Вызывается первый поезд, каково значение _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); 
    } 
} 

Что произойдет, если вы не попали ни в одну в if или else if статей? Вы удалили _search_struct и оставили его, указывая на мусор. После этого вы должны установить его на NULL.

Если это не проблема, вы можете попробовать заменить:

delete p; 

с:

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

(или аналогично для delete[] сайтов). (Это, вероятно, создало бы проблему для первого вызова KNNClassifier::train.)

Кроме того, обязательно: вам действительно нужно выполнить все эти ручные распределения и освобождения от ответственности? Почему вы, по крайней мере, не используете std::vector вместо new[]/delete[] (что почти всегда плохо)?

+0

Спасибо. Но удалить нулевой указатель можно. Также присваивание 0 указателю после его удаления не решает мою проблему. – Tim

+0

@Tim: Правильно, 'delete NULL' - это не-op (поэтому я предложил использовать' assert'). Я не видел, чтобы ваши последующие изменения упоминали, что это не произошло в однопоточном сценарии. – jamesdlin

Смежные вопросы