Как правильно распараллелить цикл for с использованием OpenMP? - PullRequest
0 голосов
/ 04 сентября 2018

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

Я получаю странные результаты при запуске следующего кода.

  • Скорость от распараллеливания не такая большая, как я ожидал
  • Когда не используются флаги -O, код выполняется медленнее .

Я использую компилятор g ++ версии 7.3.0 и ОС Ubuntu 18.04 на процессоре i5-8600 с 16 ГБ ОЗУ.

Выходы:

Выход 1 (пока не разрешено встраивать, так как я новый участник)

Transript:

.../OpenMPTest$ g++ -O3 -o openmp main.cpp -fopenmp
.../OpenMPTest$ ./openmp
6 processors used.
Linear action took: 2.87415 seconds.
Parallel action took: 0.99954 seconds.

Выход 2

.../OpenMPTest$ g++ -o openmp main.cpp -fopenmp
.../OpenMPTest$ ./openmp
6 processors used.
Linear action took: 25.7037 seconds.
Parallel action took: 68.0485 seconds.

Как вы можете видеть, для 6 процессоров я получаю улучшение только в ~ 2,9 раза, если не опускать флаги -O, в этом случае программа работает намного медленнее, но все равно использует все 6 процессоров при 100% -ной загрузке. (проверено с использованием htop).

Почему это? Кроме того, что я могу сделать, чтобы добиться полного 6-кратного увеличения производительности?

Исходный код:

#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>
#include <array>
#include <omp.h>

int main() {

    using namespace std::chrono;

    const int big_number = 1000000000;
    std::array<double, 6> array = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

    // Sequential

    high_resolution_clock::time_point start_linear = high_resolution_clock::now();

    for(int i = 0; i < 6; i++) {
        for(int j = 0; j < big_number; j++) {
            array[i]++;
        }   
    }

    high_resolution_clock::time_point end_linear = high_resolution_clock::now();

    // Parallel 

    high_resolution_clock::time_point start_parallel = high_resolution_clock::now();

    array = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

    #pragma omp parallel
    {
        #pragma omp for
        for(int i = 0; i < 6; i++) {
            for(int j = 0; j < big_number; j++) {
                array[i]++;
            }   
        }
    }

    high_resolution_clock::time_point end_parallel = high_resolution_clock::now();

    // Stats.

    std::cout << omp_get_num_procs() << " processors used." << std::endl << std::endl;

    duration<double> time_span = duration_cast<duration<double>>(end_linear - start_linear);
    std::cout << "Linear action took: " << time_span.count() << " seconds." << std::endl << std::endl;

    time_span = duration_cast<duration<double>>(end_parallel - start_parallel);
    std::cout << "Parallel action took: " << time_span.count() << " seconds." << std::endl << std::endl;

    return EXIT_SUCCESS;
}

1 Ответ

0 голосов
/ 04 сентября 2018

Кажется, на ваш код повлияло ложное распространение .

не позволяйте разным потокам обращаться к одной и той же строке кэша. Лучший способ - не делить переменные между потоками.

#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>
#include <array>
#include <omp.h>

int main() {

  using namespace std::chrono;

  const int big_number = 1000000000;
  alignas(64) std::array<double, 6*8> array = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };

  // Sequential

  high_resolution_clock::time_point start_linear = high_resolution_clock::now();

  for(int i = 0; i < 6; i++) {
    for(int j = 0; j < big_number; j++) {
      array[i]++;
    }
  }

  high_resolution_clock::time_point end_linear = high_resolution_clock::now();

  // Parallel

  high_resolution_clock::time_point start_parallel = high_resolution_clock::now();

  array = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

      #pragma omp parallel
  {
            #pragma omp for
    for(int i = 0; i < 6; i++) {
      for(int j = 0; j < big_number; j++) {
        array[i*8]++;
      }
    }
  }

  high_resolution_clock::time_point end_parallel = high_resolution_clock::now();

  // Stats.

  std::cout << omp_get_num_procs() << " processors used." << std::endl << std::endl;

  duration<double> time_span = duration_cast<duration<double>>(end_linear - start_linear);
  std::cout << "Linear action took: " << time_span.count() << " seconds." << std::endl << std::endl;

  time_span = duration_cast<duration<double>>(end_parallel - start_parallel);
  std::cout << "Parallel action took: " << time_span.count() << " seconds." << std::endl << std::endl;

  return EXIT_SUCCESS;
}
* Используется 1007 * 8 процессоров.

Линейное действие заняло: 26,9021 секунды.

Параллельное действие заняло: 6,41319 секунд.

И вы можете прочитать это .

...