2010-02-02 14 views
7

Encontré un error de tiempo de ejecución "double free or corruption" en mi programa C++ que llama a una biblioteca confiable ANN y usa OpenMP para paralizar un ciclo for.double free or corruption

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

¿Significa que la memoria en la dirección 0x0000000002527260 se libera más de una vez?

El error ocurre en "_search_struct-> annkSearch (queryPt, k_max, nnIdx, dists, _eps);" dentro de la función classify_various_k(), que a su vez está dentro de OpenMP for-loop dentro de la función tune_complexity().

Tenga en cuenta que el error ocurre cuando hay más de un subproceso para OpenMP, y no ocurre en el caso de un solo subproceso. No estoy seguro por qué.

El siguiente es mi código. Si no es suficiente para diagnosticar, házmelo saber. ¡Gracias por tu ayuda!

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;  
      }  
      } 

      ...... 
    } 

ACTUALIZACIÓN:

Gracias! Ahora estoy tratando de corregir el conflicto de escritura al mismo problema de memoria en classify_various_k() mediante el uso de "#pragma omp crítico":

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; 

} 

Sin embargo, hay un nuevo error de segmento a "número + = _ etiquetas [nnIdx [yo] ];". ¿Alguna idea? ¡Gracias!

+0

Pruébalo sin openmp: ¿funciona correctamente? –

+1

Recomiendo compilar con -g y ejecutar a través de valgrind si está en OSX o Linux. Eso debería señalar el error para usted. –

+0

@Kornel: funciona correctamente para el caso de una sola rosca. – Tim

Respuesta

5

De acuerdo, ya que ha indicado que funciona correctamente en un caso de hilo único, los métodos "normales" no funcionarán.Que tiene que hacer lo siguiente:

  • encontrar todas las variables que se accede de forma paralela
  • especialmente echar un vistazo a los que se modifican
  • no llaman borrar en un recurso compartido
  • eche un vistazo a todas las funciones de la biblioteca que operan en recursos compartidos - verifique si no hacen asignación/desasignación

Esta es la lista de candidatos que están doble eliminado:

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

Además, este código podría no ser seguro para hilos:

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

Debido a que dos o más procesos pueden tratar de hacer una reseña de errores matriz.

y grande consejos: intente no acceder (¡especialmente modificar!) Nada mientras se encuentre en modo enhebrado, ¡eso no es un parámetro para la función!

+0

Gracias! La variable compartida no está desasignada en la región paralela. Dentro de la región paralela, solo las variables locales se asignan y dealocan. – Tim

+0

@Tim, su problema puede ser corrupción y no una doble asignación: la corrupción puede ocurrir si dos procesadores intentan escribir en el mismo punto de la memoria. –

+0

¡Gracias por señalar eso! Que tiene sentido. ¿Cómo sincronizar la escritura en errores compartidos entre hilos? – Tim

2

Su método de tren borra _search_struct antes de asignarle memoria nueva. Entonces, la primera vez que se llama al tren, se borra. ¿Hay un código para asignarlo antes de esa llamada para entrenar? Podría terminar tratando de eliminar la memoria basura (sin embargo, no tenemos el código para contar).

+0

Sabe que puede llamar a 'eliminar 0;' ¿verdad? –

+0

Sí, train() primero desasignarlo e inmediatamente asignarlo. – Tim

+0

@Kornel, sí eliminar 0 está permitido. ¿Sabía que es 0 la primera vez que se elimina del código? @Tim. Sí, eso es lo que digo también. La primera vez que se llama a un tren, ¿cuál es el valor de _search_struct? –

4

No sé si este es su problema, pero:

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); 
    } 
} 

¿Qué pasa si no están incluidos en ninguna de las if o las cláusulas else if? Has eliminado _search_struct y lo has dejado apuntando a la basura. Debería configurarlo en NULL después.

Si este no es el problema, usted podría intentar reemplazar:

delete p; 

con:

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

(o similar para delete[] sitios). (Esto probablemente plantearía un problema para la primera invocación de KNNClassifier::train, sin embargo).

También, obligatorio: ¿realmente necesita hacer todas estas asignaciones manuales y desasignaciones? ¿Por qué no está al menos usando std::vector en lugar de new[]/delete[] (que casi siempre son malas)?

+0

Gracias. Pero eliminar un puntero nulo es posible. También asignar 0 al puntero después de eliminarlo no resuelve mi problema. – Tim

+0

@Tim: Derecha, 'eliminar NULL' es un no-op (es por eso que sugerí usar un' assert'). No vi su edición posterior mencionar que esto no sucedió en un escenario de subproceso único. – jamesdlin