2010-02-02 39 views
7

Tôi gặp một lỗi thời gian chạy "đôi miễn phí hoặc tham nhũng" trong chương trình C++ của tôi gọi một thư viện ANN đáng tin cậy và sử dụng OpenMP để parallize một vòng lặp for.double miễn phí hoặc tham nhũng

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

Có nghĩa là bộ nhớ tại địa chỉ 0x0000000002527260 được giải phóng nhiều lần?

Lỗi xảy ra tại "_search_struct-> annkSearch (queryPt, k_max, nnIdx, dists, _eps);" bên trong hàm classify_various_k(), lần lượt bên trong hàm OpenMP for-loop bên trong hàm tune_complexity().

Lưu ý rằng lỗi xảy ra khi có nhiều hơn một luồng cho OpenMP, và không xảy ra trong trường hợp chuỗi đơn lẻ. Không chắc chắn lý do tại sao.

Sau đây là mã của tôi. Nếu nó không đủ để chẩn đoán, hãy cho tôi biết. Cảm ơn bạn đã giúp đỡ!

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:

Cảm ơn! Bây giờ tôi đang cố gắng để khắc phục xung đột về văn bản cho vấn đề bộ nhớ cùng trong classify_various_k() bằng cách sử dụng "#pragma OMP quan trọng":

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; 

} 

Tuy nhiên, có một lỗi lỗi phân khúc mới tại "Kết quả + = _ nhãn [nnIdx [i]]; ". Một số ý tưởng? Cảm ơn!

+0

Dùng thử mà không có openmp - có hoạt động chính xác không? –

+1

Tôi khuyên bạn nên biên dịch bằng -g và chạy qua valgrind nếu bạn đang sử dụng OSX hoặc Linux. Điều đó sẽ xác định lỗi cho bạn. –

+0

@Kornel: Nó hoạt động chính xác cho trường hợp đơn luồng. – Tim

Trả lời

5

Được rồi, vì bạn đã tuyên bố rằng nó hoạt động chính xác trên một trường hợp một luồng, sau đó các phương thức "bình thường" sẽ không hoạt động.Bạn cần phải làm như sau:

  • tìm tất cả các biến được truy cập song song
  • đặc biệt hãy nhìn vào những người được sửa đổi
  • không gọi xóa trên một nguồn tài nguyên chia sẻ
  • hãy xem tất cả các chức năng thư viện hoạt động trên các tài nguyên được chia sẻ - hãy kiểm tra xem chúng có không phân bổ/deallocation

Đây là danh sách các ứng cử viên đôi xóa:

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

Ngoài ra, mã này có thể không được thread an toàn:

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

Bởi vì hai hay nhiều quá trình có thể cố gắng làm một ghi vào mảng lỗi.

lời khuyên lớn - cố gắng không truy cập (đặc biệt là sửa đổi!) Bất cứ điều gì trong khi ở chế độ chuỗi, đó không phải là tham số cho hàm!

+0

Cảm ơn! Biến chia sẻ không được phân bổ trong vùng song song. Bên trong vùng song song, chỉ các biến cục bộ được phân bổ và xử lý. – Tim

+0

@Tim, vấn đề của bạn có thể bị hỏng, và không phân bổ kép - tham nhũng có thể xảy ra nếu hai bộ xử lý cố ghi vào cùng một điểm trong bộ nhớ. –

+0

Cảm ơn bạn đã chỉ ra điều đó! Nó có ý nghĩa. Làm thế nào để đồng bộ hóa các văn bản vào các lỗi được chia sẻ giữa các chủ đề? – Tim

2

Phương pháp đào tạo của bạn sẽ xóa _search_struct trước khi cấp phát bộ nhớ mới cho nó. Vì vậy, lần đầu tiên tàu được gọi, nó sẽ bị xóa. Có mã để phân bổ nó trước cuộc gọi đó để đào tạo không? Bạn có thể cuối cùng cố gắng xóa bộ nhớ rác (mặc dù chúng tôi không có mã để nói).

+0

Bạn biết bạn có thể gọi 'xóa 0;' phải không? –

+0

Có, đào tạo() đầu tiên deallocate nó và sau đó ngay lập tức phân bổ nó. – Tim

+0

@Kornel, có xóa 0 được cho phép. Bạn có biết rằng đây là lần đầu tiên nó bị xóa khỏi mã đã cho không? @Tim. Vâng, đó là những gì tôi nói quá. Chuyến tàu đầu tiên được gọi, giá trị của _search_struct là bao nhiêu? –

4

Tôi không biết nếu điều này là vấn đề của bạn, nhưng:

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

gì xảy ra nếu bạn không rơi vào một trong hai if hoặc else if khoản? Bạn đã xóa _search_struct và để nó trỏ vào thùng rác. Bạn nên đặt nó thành NULL sau đó.

Nếu đây không phải là vấn đề, bạn có thể thử thay thế:

delete p; 

với:

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

(hoặc tương tự cho delete[] trang web). (Điều này có thể sẽ đặt ra một vấn đề cho lần gọi đầu tiên của KNNClassifier::train, tuy nhiên.)

Ngoài ra, bắt buộc: bạn có thực sự cần phải làm tất cả các phân bổ thủ công và deallocations? Tại sao bạn không sử dụng ít nhất std::vector thay vì new[]/delete[] (hầu như luôn luôn xấu)?

+0

Cảm ơn. Nhưng xóa một con trỏ null là có thể. Ngoài ra gán 0 cho con trỏ sau khi xóa nó không giải quyết được vấn đề của tôi. – Tim

+0

@Tim: Phải, 'xóa NULL' là một no-op (đó là lý do tại sao tôi đề xuất sử dụng' assert'). Tôi không thấy bản chỉnh sửa sau của bạn đề cập đến việc điều này không xảy ra trong một kịch bản đơn luồng. – jamesdlin

Các vấn đề liên quan