параллельный код C для вычисления расстояния - PullRequest
5 голосов
/ 30 января 2012

У меня есть C-код, который вычисляет расстояние между двумя наборами узлов (по три координаты в каждом), хотя мой код и так был достаточно быстрым, я хочу увеличить его с помощью параллельных вычислений. Я уже нашел некоторую информацию об openMP и пытаюсь использовать ее прямо сейчас, но есть кое-что немного странное. Без omp время процессора составляет 20 с, добавление двух строк прагмы - 160 с! Как это могло случиться?

Я добавляю свой код сюда

float computedist(float **vG1, float **vG2, int ncft, int ntri2, int jump, float *dist){
    int k = 0, i, j;
    float min = 0;
    float max = 0;
    float avg = 0;
    float *d = malloc(3*sizeof(float));
    float diff;

    #pragma omp parallel
    for(i=0;i<ncft;i+=jump){
        #pragma omp parallel
        for(j=0;j<ntri2;j++){
            d[0] = vG1[i][0] - vG2[j][0];
            d[1] = vG1[i][1] - vG2[j][1];
            d[2] = vG1[i][2] - vG2[j][2];
            diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2));
            if(j==0)
                dist[k] = diff;
            else
                if(diff<dist[k])
                    dist[k] = diff;

        }
        avg += dist[k];
        if(dist[k]>max)
            max = dist[k];
        k++;
    }

    printf("max distance: %f\n",max);
    printf("average distance: %f\n",avg/(int)(ncft/jump));

    free(d);

    return max;
}

Большое спасибо за любую помощь

Ответы [ 3 ]

5 голосов
/ 30 января 2012

(Ответ ниже относится к исходному коду в вопросе, который с тех пор был улучшен с применением этих предложений)


Вам необходимо прочитать больше о том, как использовать OpenMP.Спецификация доступна на http://www.openmp.org; и есть ссылки на учебные пособия и другие ресурсы.

Я укажу на некоторые проблемы в вашем коде и дам рекомендации, как их исправить.

    float *d = malloc(3*sizeof(float));
    float diff;

d используется как временная переменная, поэтому следует пометить ее как private в #pragma omp parallel for (см. Ниже), чтобы избежать скачек данных.Между тем вместо динамического размещения я бы просто использовал 3 отдельных числа с плавающей точкой.diff также содержит временное значение, поэтому также должно быть private.

    #pragma omp parallel
    for(i=0;i<ncft;i+=jump){
        #pragma omp parallel
        for(j=0;j<ntri2;j++){

Вы создали параллельную область, в которой каждый поток выполняет весь цикл (так как область не содержит никаких конструкций совместного использования работы)и внутри него вы создали вложенную область с новым (!) набором потоков, каждый из которых выполняет весь внутренний цикл.Это добавляет много накладных и ненужных вычислений в вашу программу.Вам нужно #pragma omp parallel for, и оно применяется только к внешнему циклу.

            d[0] = vG1[i][0] - vG2[j][0];
            d[1] = vG1[i][1] - vG2[j][1];
            d[2] = vG1[i][2] - vG2[j][2];
            diff = sqrt(pow(d[0],2) + pow(d[1],2) + pow(d[2],2));

Не связано с параллелизмом, но зачем вызывать pow просто для вычисления квадратов?Старое доброе умножение, вероятно, будет и проще для чтения, и быстрее.

            if(j==0)
                dist[k] = diff;
            else
                if(diff<dist[k])
                    dist[k] = diff;

Поскольку действие одинаковое (dist[k]=diff;), код можно упростить, комбинируя два условия с || (логическийили).

        }
        avg += dist[k];
        if(dist[k]>max)
            max = dist[k];

Здесь вы вычисляете агрегированные значения во внешнем цикле.В OpenMP это делается с помощью reduction предложения #pragma omp for.

        k++;
    }

В настоящее время вы увеличиваете k на каждой итерации, создавая тем самым ненужную зависимость между итерациями, которая приводит к гонке данных впараллельный код.Согласно вашему коду, k - это просто удобный «псевдоним» для i/jump, поэтому просто присвойте его такому в начале итерации и сделайте private.

2 голосов
/ 30 января 2012

Вы используете много синхронизации, когда вы добавляете #pragma omp parallel как во внешний цикл, так и во внутренний цикл.

При использовании #pragma omp parallel, существует барьер после цикла, поэтомувсе потоки ожидают окончания последнего потока.
В вашем случае вам придется ждать всех потоков как во внутреннем цикле, так и во внешнем цикле, таким образом, вы получаете много накладных расходов на использование синхронизации.

Обычно лучше всего использовать #pragma omp parallelтолько во внешнем цикле [при условии, что там достаточно работы ...], чтобы минимизировать количество барьеров.

0 голосов
/ 09 мая 2015

В вашем коде вы пишете в массив, общий для всех потоков, dist. Вероятно, у вас есть ложные проблемы с обменом там. Попробуйте выделить этот массив с отступом.

...