Ошибка сегментации OpenMP - PullRequest
0 голосов
/ 30 ноября 2018

В последнее время я работаю над кодом OpenMP AC, который выполняет планирование соответствия.По сути, после того, как поток завершил свои назначенные итерации, он начнет искать другие потоки, которые имеют наибольшую рабочую нагрузку, и украсть у них некоторые задания.

Все отлично работает, я могу скомпилировать файл с помощью icc.Однако, когда я пытаюсь запустить его, он вызывает ошибку сегментации (дамп памяти).Но забавно то, что ошибка не всегда происходит, то есть даже я получаю сообщение об ошибке при первом запуске кода, когда я пытаюсь запустить снова, иногда это работает.Это так странно для меня.Интересно, что я сделал неправильно в своем коде и как решить проблему.Спасибо.Я только изменил метод runloop и affinity, другие приводятся в начале, который отлично работает.

#include <stdio.h>
#include <math.h>


#define N 729
#define reps 1000 
#include <omp.h> 


double a[N][N], b[N][N], c[N];
int jmax[N];  


void init1(void);
void init2(void);
void runloop(int); 
void loop1chunk(int, int);
void loop2chunk(int, int);
void valid1(void);
void valid2(void);
int affinity(int*, int*, int, int, float, int*, int*);

int main(int argc, char *argv[]) { 

    double start1,start2,end1,end2;
    int r;

  init1(); 

  start1 = omp_get_wtime(); 

  for (r=0; r<reps; r++){ 
    runloop(1);
  } 

  end1  = omp_get_wtime();  

  valid1(); 

  printf("Total time for %d reps of loop 1 = %f\n",reps, (float)(end1-start1)); 


  init2(); 

  start2 = omp_get_wtime(); 

  for (r=0; r<reps; r++){ 
    runloop(2);
  } 

  end2  = omp_get_wtime(); 

  valid2(); 

  printf("Total time for %d reps of loop 2 = %f\n",reps, (float)(end2-start2)); 

} 

void init1(void){
  int i,j; 

  for (i=0; i<N; i++){ 
    for (j=0; j<N; j++){ 
      a[i][j] = 0.0; 
      b[i][j] = 3.142*(i+j); 
    }
  }

}

void init2(void){ 
  int i,j, expr; 

  for (i=0; i<N; i++){ 
    expr =  i%( 3*(i/30) + 1); 
    if ( expr == 0) { 
      jmax[i] = N;
    }
    else {
      jmax[i] = 1; 
    }
    c[i] = 0.0;
  }

  for (i=0; i<N; i++){ 
    for (j=0; j<N; j++){ 
      b[i][j] = (double) (i*j+1) / (double) (N*N); 
    }
  }

}  


void runloop(int loopid)
{
  int nthreads = omp_get_max_threads(); // we set it before the parallel region, using opm_get_num_threads() will always return 1 otherwise                             
    int ipt = (int) ceil((double)N/(double)nthreads);           
    float chunks_fraction = 1.0 / nthreads;                                 
    int threads_lo_bound[nthreads];                                         
    int threads_hi_bound[nthreads];                                     

    #pragma omp parallel default(none) shared(threads_lo_bound, threads_hi_bound, nthreads, loopid, ipt, chunks_fraction)
    {
        int myid = omp_get_thread_num();
        int lo = myid * ipt;
    int hi = (myid+1)*ipt;
    if (hi > N) hi = N; 

        threads_lo_bound[myid] = lo;
        threads_hi_bound[myid] = hi;

        int current_lower_bound = 0;
        int current_higher_bound = 0;
        int affinity_steal = 0;

        while(affinity_steal != -1)
        {
            switch(loopid)
            {
                case 1: loop1chunk(current_lower_bound, current_higher_bound); break;
                case 2: loop2chunk(current_lower_bound, current_higher_bound); break;
            }

            #pragma omp critical
            {
                affinity_steal = affinity(threads_lo_bound, threads_hi_bound, nthreads, myid, chunks_fraction, &current_lower_bound, &current_higher_bound);
            }
        }
    }
}

int affinity(int* threads_lo_bound, int* threads_hi_bound, int num_of_thread, int thread_num, float chunks_fraction, int *current_lower_bound, int *current_higher_bound)
{
    int current_pos;

    if (threads_hi_bound[thread_num] - threads_lo_bound[thread_num] > 0)
    {
        current_pos = thread_num;
    }
    else
    {
        int new_pos = -1;
        int jobs_remain = 0;
    int i;
        for (i = 0; i < num_of_thread; i++)
        {
            int diff = threads_hi_bound[i] - threads_lo_bound[i];
            if (diff > jobs_remain)
            {
                new_pos = i;
                jobs_remain = diff;
            }
        }

        current_pos = new_pos;
    }

    if (current_pos == -1) return -1;

    int remaining_iterations = threads_hi_bound[current_pos] - threads_lo_bound[current_pos];
    int iter_size_fractions = (int)ceil(chunks_fraction * remaining_iterations);

    *current_lower_bound = threads_lo_bound[current_pos];
    *current_higher_bound = threads_lo_bound[current_pos] + iter_size_fractions;
    threads_lo_bound[current_pos] = threads_lo_bound[current_pos] + iter_size_fractions;

    return current_pos;
}

void loop1chunk(int lo, int hi) { 
  int i,j; 

  for (i=lo; i<hi; i++){ 
    for (j=N-1; j>i; j--){
      a[i][j] += cos(b[i][j]);
    } 
  }

} 


void loop2chunk(int lo, int hi) {
  int i,j,k; 
  double rN2; 

  rN2 = 1.0 / (double) (N*N);  

  for (i=lo; i<hi; i++){ 
    for (j=0; j < jmax[i]; j++){
      for (k=0; k<j; k++){ 
    c[i] += (k+1) * log (b[i][j]) * rN2;
      } 
    }
  }

}

void valid1(void) { 
  int i,j; 
  double suma; 

  suma= 0.0; 
  for (i=0; i<N; i++){ 
    for (j=0; j<N; j++){ 
      suma += a[i][j];
    }
  }
  printf("Loop 1 check: Sum of a is %lf\n", suma);

} 


void valid2(void) { 
  int i; 
  double sumc; 

  sumc= 0.0; 
  for (i=0; i<N; i++){ 
    sumc += c[i];
  }
  printf("Loop 2 check: Sum of c is %f\n", sumc);
}      

1 Ответ

0 голосов
/ 30 ноября 2018

Вы не инициализируете массивы threads_lo_bound и threads_hi_bound, поэтому они изначально содержат некоторые совершенно случайные значения (это источник случайности номер 1).

Затем вы входите в параллельную область, где необходимо понимать, что не все потоки будут перемещаться по коду синхронно, фактическая скорость каждого потока довольно случайна, так как он разделяет ЦП со многими другими программами,даже если они используют только 1%, это все равно будет отображаться (это источник случайности номер 2, я бы сказал, что это более важно для того, почему вы видите, что это работает время от времени).

Так что же происходит в случае сбоя кода?

Один из потоков (скорее всего, мастер) достигает критической области до того, как хотя бы один из потоков достигнетстрока, где вы устанавливаете threads_lo_bound[myid] и threads_hi_bound[myid].

После этого, в зависимости от того, какими были эти случайные значения, хранящиеся в нем (вы обычно можете предполагать, что они выходили за пределы, ваш массив довольно мал, шансы того, что значения являются действительными индексами, довольно малы),Поток попытается украсть некоторые из заданий (которые не существуют), установив current_lower_bound и / или current_upper_bound на какое-то значение, которое находится вне диапазона ваших начальных массивов a, b, c.

Затем он войдет во вторую итерацию вашего цикла while(affinity_steal != -1) и получит доступ к памяти, которая выходит за границы, неизбежно приводящие к ошибке сегментации (в конечном счете, в принципе это неопределенное поведение, и сбой может произойти в любой момент посленеверный доступ к памяти или, в некоторых случаях, никогда, что приводит вас к убеждению, что все в порядке, когда это совершенно точно не так).

Исправление, конечно, простое, добавьте

#pragma omp barrier

непосредственно перед циклом while(affinity_steal != -1), чтобы убедиться, что все потоки достигли этой точки (т. е. синхронизировать потоки в этой точке), и границы правильно установлены, прежде чем вы перейдете в цикл.Затраты на это минимальны, но если по какой-то причине вы хотите избежать использования барьеров, вы можете просто установить значения массива перед входом в параллельную область.

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

...