OpenMP и ядра / потоки - PullRequest
       27

OpenMP и ядра / потоки

5 голосов
/ 15 февраля 2012

Мой процессор - Core i3 330M с 2 ядрами и 4 потоками. Когда я выполняю команду cat /proc/cpuinfo в моем терминале, это как будто у меня есть 4 CPUS. Когда я использую функцию OpenMP get_omp_num_procs(), я также получаю 4.

Теперь у меня есть стандартный векторный класс C ++, я имею в виду класс двойного массива фиксированного размера, который не использует шаблоны выражений. Я тщательно распараллелил все методы моего класса и получил «ожидаемое» ускорение.

Вопрос в том, могу ли я угадать ожидаемое ускорение в таком простом случае? Например, если я добавлю два вектора без параллельных циклов for, я получу некоторое время (используя команду времени оболочки). Теперь, если я использую OpenMP, я должен получить время, деленное на 2 или 4, в зависимости от количества ядер / потоков? Я подчеркиваю, что я прошу только об этой конкретной простой проблеме, где нет взаимозависимости в данных, и все является линейным (сложение вектора).

Вот код:

Vector Vector::operator+(const Vector& rhs) const
{
    assert(m_size == rhs.m_size);
    Vector result(m_size);
    #pragma omp parallel for schedule(static)
    for (unsigned int i = 0; i < m_size; i++) 
            result.m_data[i] = m_data[i]+rhs.m_data[i];

    return result;
}

Я уже читал этот пост: Отображение потока OpenMP на физические ядра .

Я надеюсь, что кто-нибудь расскажет мне больше о том, как OpenMP выполняет работу в этом простом случае. Я должен сказать, что я новичок в параллельных вычислениях.

Спасибо!

Ответы [ 2 ]

3 голосов
/ 15 февраля 2012

РЕДАКТИРОВАТЬ: Теперь, когда некоторый код был добавлен.

В этом конкретном примере очень мало вычислений и много доступа к памяти. Так что производительность будет сильно зависеть от:

  • Размер вектора.
  • Как вы рассчитываете это. (у вас есть внешний цикл для определения времени)
  • Данные уже находятся в кеше.

Для больших векторных размеров вы, вероятно, обнаружите, что производительность ограничена пропускной способностью вашей памяти. В этом случае параллелизм не сильно поможет. Для меньших размеров преобладают накладные расходы на нарезание резьбы. Если вы получаете «ожидаемое» ускорение, вы, вероятно, находитесь где-то посередине, где результат является оптимальным.

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

Так же, как простой пример, взятый из моего ответа здесь: Как получить 100% использование ЦП из программы на C

На Core i7 920 @ 3,5 ГГц (4 ядра, 8 потоков):

Если я использую 4 потока , результат будет:

This machine calculated all 78498 prime numbers under 1000000 in 39.3498 seconds

Если я запускаю с 4 потока и явно (с помощью диспетчера задач) закрепляем потоки на 4 отдельных физических ядрах , результат будет:

This machine calculated all 78498 prime numbers under 1000000 in 30.4429 seconds

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

1 голос
/ 21 сентября 2012

Чтобы добавить в мистические ответы. Ваша проблема - ограниченная пропускная способность памяти . Посмотрите на STREAM . Запустите его на своем компьютере в однопотоковом и многопоточном случаях и посмотрите на результаты Triad - это ваш случай (ну, почти, поскольку ваш выходной вектор является одновременно одним из ваших входных векторов). Подсчитайте, сколько данных вы перемещаете, и вы точно будете знать, какую производительность ожидать.

Многопоточность работает для этой проблемы? Да. Редко когда одно ядро ​​ЦП может насыщать всю пропускную способность памяти системы. Современные компьютеры балансируют доступную пропускную способность памяти с количеством доступных ядер. Исходя из моего опыта, вам понадобится около половины ядер для насыщения пропускной способности памяти с помощью простой операции memcopy. Это может занять больше времени, если вы сделаете некоторые вычисления в пути.

Обратите внимание, что в системах NUMA вам необходимо привязать потоки к ядрам процессора и использовать локальное распределение памяти для получения оптимальных результатов. Это связано с тем, что в таких системах каждый ЦП имеет собственную локальную память, доступ к которой самый быстрый. Вы по-прежнему можете получить доступ ко всей системной памяти, как на обычных SMP, но это требует затрат на связь - процессоры должны явно обмениваться данными. Привязка потоков к процессорам и использование локального распределения чрезвычайно важны. Неспособность сделать это убивает масштабируемость. Проверьте libnuma, если вы хотите сделать это в Linux.

...