простая параллельная OpenMP для цикла медленнее, чем последовательные вычисления

Я новичок в распараллеливании, и я надеюсь, что я не трачу впустую время. Я уже спросил несколько друзей, которые уже использовали openMP, но они не могли мне помочь. Поэтому я предположил, что мое дело может быть интересно и кому-то еще, по крайней мере, в образовательных целях, и я постарался документировать его настолько хорошо, насколько мог. Это два примера, один из которых на 100% взят из уроков Тима Мэттсона в youtube, другой каким-то образом упрощен, но, как мне кажется, все еще является стандартным подходом. В обоих случаях время вычисления масштабируется с количеством потоков за несколько итераций, но для очень большого количества итераций время вычисления, по-видимому, сходится к одному и тому же числу. Это, конечно, неправильно, так как я ожидаю, что время вычислений будет одинаковым для нескольких итераций и действительно оптимизировано для большого количества итераций.

Здесь два примера, оба скомпилированы с

g++ -fopenmp main.cpp -o out

Модель потока: posix
gcc версия 4.8.4 (Ubuntu 4.8.4-2ubuntu1 ~ 14.04), в Ubuntu 14.04
и со следующим заголовком:

#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <chrono>
#include <iostream>

using namespace std;#define NUMBER_OF_THREADS 2
static long num_steps = 1000000000;

Теперь число ядер на компьютере, над которым я сейчас работаю, равно 8 (intel i7), поэтому любое количество потоков от 2 до 4, которое я ожидал, принесло бы некоторое большое преимущество с точки зрения времени вычислений.

Пример 1:

int main() {

omp_set_num_threads(NUMBER_OF_THREADS);
double step = 1.0/(double) num_steps, pi=0.0;

auto begin = chrono::high_resolution_clock::now();

#pragma omp parallel
{
int i, ID, nthrds;
double x, sum = 0;

ID = omp_get_thread_num();
nthrds = omp_get_num_threads();

for (i=ID; i<num_steps; i=i+nthrds) {
x = (i+0.5)*step;
sum = sum + 4.0/(1.0+x*x);
}

#pragma omp critical
pi += step*sum;
}

auto end = chrono::high_resolution_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(end-begin).count()/1e6 << "ms\n";

return 0;

}

Пример 2:

int main() {

omp_set_num_threads(NUMBER_OF_THREADS);
double pi=0, sum = 0;
const double step = 1.0/(double) num_steps;

auto begin = chrono::high_resolution_clock::now();

// #pragma omp parallel
{
#pragma omp parallel for reduction(+:sum)
for (int i=0; i<num_steps; i++) {
double x = (i+0.5)*step;
sum += 4.0/(1.0+x*x);
}
}

pi += step*sum;

auto end = std::chrono::high_resolution_clock::now();
cout << chrono::duration_cast<chrono::nanoseconds>(end-begin).count()/1e6 << "ms\n";

return 0;

}

Вначале я думал, что пример 2 замедляется сокращением переменной, что мешает распараллеливанию, но в примере 1 почти ничего не поделено. Дайте мне знать, если я делаю что-то действительно глупое, или я могу указать больше аспектов проблемы. Спасибо всем.

3

Решение

Как отметил Жиль в комментариях, проблема заключалась в том, что я измерял время с помощью clock (), который суммирует все тики ядер.
с

chrono::high_resolution_clock::now();

я получаю ожидаемое ускорение.

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

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]