2016-07-28 2 views
2

Я работаю с OpenMP, чтобы получить алгоритм с почти линейным ускорением. К сожалению, я заметил, что не могу получить желаемое ускорение.Нет ускорения с OpenMP

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

Это игрушка пример я писал:

#include <omp.h> 
#include <cmath> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <string.h> 
#include <cstdlib> 
#include <fstream> 
#include <sstream> 
#include <iomanip> 
#include <iostream> 
#include <stdexcept> 
#include <algorithm> 
#include "mkl.h" 

int main() { 
     int number_of_threads = 1; 
     int n = 600; 
     int m = 50; 
     int N = n/number_of_threads; 
     int time_limit = 600; 
     double total_clock = omp_get_wtime(); 
     int time_flag = 0; 

     #pragma omp parallel num_threads(number_of_threads) 
     { 
      int thread_id = omp_get_thread_num(); 
      int iteration_number_local = 0; 
      double *C = new double[n]; std::fill(C, C+n, 3.0); 
      double *D = new double[n]; std::fill(D, D+n, 3.0); 
      double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

      while (time_flag == 0){ 
       for (int i = 0; i < N; i++)      
        for(int z = 0; z < m; z++) 
         for(int x = 0; x < n; x++) 
          for(int c = 0; c < n; c++){ 
           CD[c] = C[z]*D[x]; 
           C[z] = CD[c] + D[x]; 
          } 
       iteration_number_local++; 
       if ((omp_get_wtime() - total_clock) >= time_limit) 
        time_flag = 1; 
      } 
     #pragma omp critical 
     std::cout<<"I am "<<thread_id<<" and I got" <<iteration_number_local<<"iterations."<<std::endl; 
     } 
    } 

Я хочу еще раз подчеркнуть, что этот код только игрушка-пример, чтобы попытаться увидеть ускорение: первый для цикла становится короче, когда число параллельных потоков увеличивается (поскольку N уменьшается).

Однако, когда я иду от 1 до 2-4 потоков, число итераций удваивается, как ожидалось; но это не тот случай, когда я использую 8-10-20 потоков: количество итераций не увеличивается линейно с количеством потоков.

Не могли бы вы помочь мне в этом? Правильно ли код? Должен ли я ожидать почти линейного ускорения?

Результаты

Выполнение кода выше, я получил следующие результаты.

1 нить: 23 итерации.

20 потоков: 397-401 итераций на резьбу (вместо 420-460).

+0

Какое оборудование вы используете? Пожалуйста, обратите внимание на процессор (ы) и память. Какая версия и параметры компилятора и какая операционная система? Сколько итераций вы наблюдаете? – Zulan

+0

Проблемные проблемы в ваших измерениях: 'CD' никогда не используется, поэтому компилятор может просто оптимизировать все, что вы ожидаете, дорого. Вы должны хотя бы вывести все 'iteration_number_local' (используйте' pragma omp критический'). – Zulan

+0

Я запускаю код на аппаратном обеспечении с двумя 10-ядерными процессорами Intel Xeon-E5 (так, у меня всего 20 ядер) с 256 ГБ оперативной памяти. Операционная система - Linux. Я не знаю о компиляторе: загружаю модуль под названием «gsl 1.15», а cmake вызывает компилятор под названием «icc». Я думаю, что это не то, что вы просили, пожалуйста, уточните меня лучше. Я запускаю несколько быстрых симуляций с n = 1000, m = 200. С 1thread я получаю 3 итерации за 120 секунд. С двумя нитями я получаю 5 итераций на поток (вместо 6). С 20 потоками я получаю от 40 до 44 итераций на поток (вместо 60!). – Mobius88

ответ

0

Вы делаете некоторое объявление внутри параллельной области, что означает, что вы выделите запоминающее устройство и заполните его числом_____________________________________________________________________________________________________________________________________расда времени разминирования. Вместо этого я Recommand вам:

double *C = new double[n]; std::fill(C, C+n, 3.0); 
double *D = new double[n]; std::fill(D, D+n, 3.0); 
double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 
#pragma omp parallel firstprivate(C,D,CD) num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
    } 

Ваше оборудование имеет ограниченное количество потоков, которое зависит от количества ядер вашего процессора. У вас может быть 2 или 4 ядра.

Параллельная область не ускоряет ваш код. С открытым OpenMP вы должны использовать #omp параллельно для ускорения для контура или

#pragma omp parallel 
{ 
    #pragma omp for 
    { 
    } 
} 

это обозначение эквивалентно #pragma OMP параллельно для. Он будет использовать несколько потоков (в зависимости от вашего оборудования), чтобы ускорить цикл for. быть осторожным

#pragma omp parallel 
{ 
    for 
    { 
    } 
} 

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

+0

Я не уверен, что вы правы. Конструкция #pragma omp parallel Разрешает количество потоков, которые требуются для выполнения всех команд внутри блока независимо. Таким образом, каждый поток будет выполнять вложенный вклад в параллельный блок. Вы можете видеть, что первый цикл за цикл становится короче, когда число потоков увеличивается, поэтому должно быть линейное ускорение. Я запускаю этот код на компьютере кластера с 20 ядрами на узел. – Mobius88

+0

Конечно, первый цикл за цикл становится короче, но вы будете делать это число_о___продолжений. Итак, вы сделаете операции n/number_of_threads * number_of_threads. –

+0

#pragma OMP параллельно { #pragma OMP для { }} и #pragma OMP параллельно { для { }} не та же команда –

0

Вы должны попробовать

#pragma omp parallel num_threads(number_of_threads) 
    { 
     int thread_id = omp_get_thread_num(); 
     int iteration_number_local = 0; 
     double *C = new double[n]; std::fill(C, C+n, 3.0); 
     double *D = new double[n]; std::fill(D, D+n, 3.0); 
     double *CD = new double[n]; std::fill(CD, CD+n, 0.0); 

     while (time_flag == 0){ 
      #pragma omp for 
      for (int i = 0; i < N; i++)      
       for(int z = 0; z < m; z++) 
        for(int x = 0; x < n; x++) 
         for(int c = 0; c < n; c++) 
          CD[c] = C[z]*D[x]; 
      iteration_number_local++; 
      if ((omp_get_wtime() - total_clock) >= time_limit) 
       time_flag = 1; 
     } 
     if(thread_id == 0) 
     iteration_number = iteration_number_local; 
    } 
    std::cout<<"Iterations= "<<iteration_number<<std::endl; 
} 
+2

Вы должны отредактировать свой предыдущий ответ, а не публиковать новый. –

1

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

1 нить: 3 итерации.

3 сообщили итераций на самом деле означает, что 2 итерации закончили менее чем за 120 с. Третье заняло больше времени. Время 1 итерации составляет от 40 до 60 с.

2 темы: 5 итераций на резьбу (вместо 6).

4 итерации завершены менее чем за 120 секунд. Время 1 итерации составляет от 24 до 30 с.

20 потоков: 40-44 итераций на резьбу (вместо 60).

40 итераций, завершенных менее чем за 120 секунд. Время 1 итерации составляет от 2,9 до 3 с.

Как вы можете видеть, ваши результаты фактически не противоречат линейному ускорению.

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

Некоторые причины (не исчерпывающий), почему вы не видите линейного ускорения являются:

  1. Память связана производительность. В случае с вашей игрушкой, например, n = 1000. Более общий разговор: конкуренция за общий ресурс (основная память, кеши, ввод-вывод).
  2. Синхронизация между потоками (например, критические секции). В случае с вашей игрушкой не так.
  3. Несбалансированность нагрузки между потоками. В случае с вашей игрушкой не так.
  4. Режим Turbo будет использовать более низкие частоты при использовании всех ядер. Это может произойти в вашем примере игрушек.

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

Более общий совет будет слишком широким для этого формата и потребует более конкретной информации о примере, отличном от игрушки.

+0

Я согласен с этим вашим ответом, поэтому я запускаю более длительные симуляции. Я запускаю код, подобный приведенному выше, включая другие инструкции внутри вложенного цикла: C [z] = CD [c] + D [x]; для использования CD, как вы предложили. Установив n = 600 и m = 50, я получил 23 итератора с одним потоком за 600 секунд и 400 итераций на поток с 20 потоками за 600 секунд. Это не ожидаемое ускорение. Я прав? – Mobius88

+0

399/22 довольно близок к 20-кратному ускорению. Достаточно близко, чтобы быть вполне приемлемым как почти линейное ускорение в реальном приложении. Также легко объясняется турбо-режимом или даже просто дисперсией. – Zulan

+0

Поскольку массивы достаточно малы, чтобы вписаться в кеш L1, это, вероятно, масштабирование частоты. –

Смежные вопросы