двойное освобождение или повреждение при многопоточном запуске - PullRequest
7 голосов
/ 02 февраля 2010

Я встретил ошибку времени выполнения "двойное освобождение или повреждение" в моей программе на C ++, которая вызывает надежную библиотеку ANN и использует OpenMP для парализации цикла for.

*** 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 внутри функции 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;  

}

Тем не менее, существует ошибка нового сегмента в "result + = _ tags [nnIdx [i]];". Какая-то идея? Спасибо!

Ответы [ 3 ]

5 голосов
/ 02 февраля 2010

Хорошо, поскольку вы заявили, что он работает правильно в однопоточном случае, тогда «нормальные» методы не будут работать. Вам необходимо сделать следующее:

  • найти все переменные, к которым обращаются параллельно
  • особенно взгляните на те, которые были изменены
  • не вызывать delete на общем ресурсе
  • взгляните на все библиотечные функции, которые работают с общими ресурсами - проверьте, не занимаются ли они распределением / освобождением

Это список кандидатов, которые были дважды удалены:

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

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

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

4 голосов
/ 02 февраля 2010

Я не знаю, если это ваша проблема, но:

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[] (что почти всегда плохо)?

2 голосов
/ 02 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...