OpenMP в Ubuntu: параллельная программа работает на двухъядерном процессоре в два раза медленнее, чем однопоточная.Зачем? - PullRequest
0 голосов
/ 08 августа 2011

Я получаю код из Википедии :

#include <stdio.h>
#include <omp.h>

#define N 100

int main(int argc, char *argv[])
{
  float a[N], b[N], c[N];
  int i;
  omp_set_dynamic(0);
  omp_set_num_threads(10); 


  for (i = 0; i < N; i++)
  {
      a[i] = i * 1.0;
      b[i] = i * 2.0;
  }

#pragma omp parallel shared(a, b, c) private(i)
  {
#pragma omp for
    for (i = 0; i < N; i++)
      c[i] = a[i] + b[i];
  }
  printf ("%f\n", c[10]);
  return 0;
}

Я попытался скомпилировать и запустить его в своем Ubuntu 11.04 с gcc4.5 (моя конфигурация: Intel C2D T7500M 2,2 ГГц, 2048 МБ ОЗУ), и эта программа работала в два раза медленнее, чем однопоточная. Почему?

Ответы [ 4 ]

1 голос
/ 09 августа 2011

Очень простой ответ: увеличьте N. И установите количество потоков, равное количеству процессоров, которое у вас есть.

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

Другой вопрос: как вы измеряете время вычислений? Обычно для получения сопоставимых результатов требуется программа время .

0 голосов
/ 05 июня 2013

Запустите приведенный ниже код и увидите разницу.

1.) У OpenMP есть издержки, поэтому время выполнения должно быть больше, чем издержки, чтобы увидеть преимущество.

2.) Не устанавливайте количество потоков самостоятельно.В общем, я использую темы по умолчанию.Однако, если ваш процессор имеет гиперпоточность, вы можете получить немного лучшую производительность, установив число потоков, равное количеству ядер.При гиперпоточности число потоков по умолчанию будет вдвое больше количества ядер.Например, на моей машине у меня четыре ядра, а число потоков по умолчанию - восемь.Установив значение 4 в некоторых ситуациях, я получаю лучшие результаты, а в других - худшие результаты.

3.) Существует некоторое ложное разделение в c, но пока N достаточно велико (чтонеобходимо преодолеть накладные расходы) ложное разделение не вызовет особых проблем.Вы можете играть с размером чанка, но я не думаю, что это будет полезно.

4.) Проблемы с кэшем.У вас есть как минимум четыре уровня памяти (значения для моей системы): L1 (32 КБ), L2 (256 КБ), L3 (12 МБ) и основная память (>> 12 МБ).Преимущества параллелизма будут уменьшаться по мере перехода на более высокий уровень.Однако в приведенном ниже примере я установил N на 100 миллионов операций с плавающей запятой, что составляет 400 миллионов байтов или около 381 МБ, и это все еще значительно быстрее при использовании нескольких потоков.Попробуйте настроить N и посмотрите, что произойдет.Например, попробуйте установить N на уровень кеша / 4 (один float равен 4 байта) (массивы a и b также должны находиться в кеше, поэтому вам может потребоваться установить N на уровень кеша / 12).Однако, если N слишком мало, вы боретесь с издержками OpenMP (что и делает код в вашем вопросе).

#include <stdio.h>
#include <omp.h>

#define N 100000000

int main(int argc, char *argv[]) {
    float *a = new float[N];
    float *b = new float[N];
    float *c = new float[N];

    int i;
    for (i = 0; i < N; i++) {
        a[i] = i * 1.0;
        b[i] = i * 2.0;
    }
    double dtime;
    dtime = omp_get_wtime();
    for (i = 0; i < N; i++) {
        c[i] = a[i] + b[i];
    }
    dtime = omp_get_wtime() - dtime;
    printf ("time %f, %f\n", dtime, c[10]);

    dtime = omp_get_wtime();
    #pragma omp parallel for private(i)
    for (i = 0; i < N; i++) {
        c[i] = a[i] + b[i];
    }
    dtime = omp_get_wtime() - dtime;
    printf ("time %f, %f\n", dtime, c[10]);

    return 0;
}
0 голосов
/ 05 июня 2013

Проблема, с которой вы сталкиваетесь, - ложное совместное использование памяти.Каждый поток должен иметь свой собственный c [i].

Попробуйте: #pragma omp параллельный общий (a, b) private (i, c)

0 голосов
/ 08 августа 2011

Полагаю, компилятор оптимизировал цикл for в не-smp-случае (например, с помощью инструкций SSE), а в варианте OMP это невозможно.

Используйте gcc -S (или objdump -S) для просмотра сборки для различных вариантов.

Возможно, вы все равно захотите следить за общими переменными, потому что они должны быть синхронизированы, что делает процесс очень медленным. Если вы можете «умные» чанки (посмотрите на прагму расписания), вы можете уменьшить конкуренцию, но опять же:

  • проверить выданный код
  • Профиль
  • не стоит недооценивать эффективность однопоточного кода (из-за локальности кэша и отсутствия переключения контекста)
  • установить количество потоков равным количеству процессоров (пусть openMP решит это за вас!); если ваша потоковая команда не имеет главного потока с выделенными задачами, в этом случае может быть целесообразно выделить ОДИН дополнительный поток

Во всех случаях, когда я пытался применить OMP для распараллеливания, примерно в 70% случаев медленнее . Случаи, когда это определенное ускорение происходит с

  • крупнозернистый параллелизм (ваш образец находится на мелкозернистом конце спектра)
  • нет общих данных
...