OpenMP замедляет вычисления - PullRequest
3 голосов
/ 11 мая 2019

Я пытаюсь распараллелить простой цикл, используя OpenMP. Ниже мой код:

#include <iostream>
#include <omp.h>
#include <time.h>
#define SIZE 10000000

float calculate_time(clock_t start, clock_t end) {
    return (float) ((end - start) / (double) CLOCKS_PER_SEC) * 1000;
}

void openmp_test(double * x, double * y, double * res, int threads){
    clock_t start, end;
    std::cout <<  std::endl << "OpenMP, " << threads << " threads" << std::endl;
    start = clock();
    #pragma omp parallel for num_threads(threads)
    for(int i = 0; i < SIZE; i++){
        res[i] = x[i] * y[i];
    }
    end = clock();

    for(int i = 1; i < SIZE; i++){
        res[0] += res[i];
    }
    std::cout << "time: " << calculate_time(start, end) << std::endl;
    std::cout << "result: " << res[0] << std::endl;
}

int main() {

    double *dbl_x = new double[SIZE];
    double *dbl_y = new double[SIZE];
    double *res = new double[SIZE];
    for(int i = 0; i < SIZE; i++){
        dbl_x[i] = i % 1000;
        dbl_y[i] = i % 1000;
    }

    openmp_test(dbl_x, dbl_y, res, 1);
    openmp_test(dbl_x, dbl_y, res, 1);
    openmp_test(dbl_x, dbl_y, res, 2);
    openmp_test(dbl_x, dbl_y, res, 4);
    openmp_test(dbl_x, dbl_y, res, 8);

    delete [] dbl_x;
    delete [] dbl_y;
    delete [] res;
    return 0;
}

Я компилирую, как показано ниже

g++ -O3 -fopenmp main.cpp -o ompTest

Однако после запуска теста на Core-i7 у меня получаются следующие результаты:

OpenMP, 1 поток время: 31.468 результат: 3.32834e + 12

OpenMP, 1 поток время: 18.663 результат: 3.32834e + 12

OpenMP, 2 потока время: 34,393 результат: 3.32834e + 12

OpenMP, 4 потока время: 56,31 результат: 3.32834e + 12

OpenMP, 8 потоков время: 108,54 результат: 3.32834e + 12

Я не понимаю, что я делаю не так? Почему OpenMP замедляет вычисления?

А также, почему первый результат значительно медленнее, чем второй (оба с 1-ным потоком)?

Моя тестовая среда: Core i7-4702MQ CPU @ 2,20 ГГц, Ubuntu 18.04.2 LTS, g ++ 7.4.0.

Ответы [ 2 ]

3 голосов
/ 11 мая 2019

Здесь происходит как минимум две вещи.

  1. clock() истекшее время измерения процессор время, которое можно рассматривать как меру количестваработа выполнена, а вы хотите измерить прошедшее время * стены .См. OpenMP time и clock () вычисляют два разных результата .

  2. Совокупное время процессора должно быть выше в параллельной программе, чем в сопоставимой последовательной программе, потому что распараллеливание увеличивает издержки,Чем больше потоков, тем больше накладных расходов, поэтому увеличение скорости на добавленный поток уменьшается с увеличением количества потоков и может даже стать отрицательным.

Сравните с этим вариантом в вашем коде, который реализует более подходящийметод измерения прошедшего времени на стене:

float calculate_time(struct timespec start, struct timespec end) {
    long long start_nanos = start.tv_sec * 1000000000LL + start.tv_nsec;
    long long end_nanos = end.tv_sec * 1000000000LL + end.tv_nsec;
    return (end_nanos - start_nanos) * 1e-6f;
}

void openmp_test(double * x, double * y, double * res, int threads){
    struct timespec start, end;
    std::cout <<  std::endl << "OpenMP, " << threads << " threads" << std::endl;
    clock_gettime(CLOCK_MONOTONIC, &start);

    #pragma omp parallel num_threads(threads)
    for(int i = 0; i < SIZE; i++){
        res[i] = x[i] * y[i];
    }

    clock_gettime(CLOCK_MONOTONIC, &end);

    for(int i = 1; i < SIZE; i++){
        res[0] += res[i];
    }
    std::cout << "time: " << calculate_time(start, end) << std::endl;
    std::cout << "result: " << res[0] << std::endl;
}

Результаты для меня:

OpenMP, 1 threads
time: 92.5535
result: 3.32834e+12

OpenMP, 2 threads
time: 56.128
result: 3.32834e+12

OpenMP, 4 threads
time: 59.8112
result: 3.32834e+12

OpenMP, 8 threads
time: 78.9066
result: 3.32834e+12

Обратите внимание, как измеренное время с двумя нитями сокращается примерно вдвое,но добавление большего количества ядер мало что улучшает, и в конечном итоге начинается тенденция к однопоточному времени. * Это демонстрирует конкурирующий эффект одновременного выполнения большего объема работы на моей четырехъядерной машине с восемью гиперпотокамии из-за увеличения накладных расходов и разногласий в ресурсах, связанных с наличием большего количества потоков для координации.

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


* Полное раскрытие: я чепопробуйте эти конкретные результаты из числа нескольких прогонов.Все показали схожие тенденции, но эта тенденция особенно выражена - и, следовательно, вероятно, переоценена - в этом.

3 голосов
/ 11 мая 2019

В настоящее время вы создаете потоки, но вы даете им одинаковую работу.

Я думаю, вы забыли «для» в прагме, которая заставляет потоки делить цикл на части.

    #pragma omp parallel for num_threads(threads)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...