2015-10-18 2 views
2

Я пытаюсь запустить эту программу с несколькими потоками.Почему эта программа OpenMP дает мне разные ответы каждый раз?

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

#define NUM_THREADS 4 
static long num_steps = 1000000000; 

int main() 
{ 
    int i; 
    double x, pi, sum = 0.0; 
    double step = 1.0/(double)num_steps; 

    clock_t start = clock(), diff; 
    #pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum) 
    for (i = 0; i < num_steps; i++) 
    { 
    x = (i+0.5)*step; 
    sum += 4.0/(1.0 + x*x); 
    } 
    #pragma omp ordered 
    pi = step*sum; 
    printf("pi = %.15f\n %d iterations\n", pi, num_steps); 

    diff = clock() - start; 
    int msec = diff * 1000/CLOCKS_PER_SEC; 
    printf("Time taken %d seconds %d milliseconds", msec/1000, msec%1000); 

    return 0; 
} 

, добавив #pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum). У меня также есть #pragma omp ordered после цикла for, который я не думаю, что мне действительно нужно, потому что нить не должна продолжаться до тех пор, пока все потоки не будут выполнены с циклом for. Это верно? Будет ли это также причиной, по которой я получаю только второе увеличение производительности по сравнению с ее запуском в виде однопоточной программы? Это 6 секунд по сравнению с 7 секундами для меня.

Я не могу ответить, почему эта программа дает мне другой ответ для pi каждый раз, когда я запускаю его?

+0

Я протестировал вашу программу в vs2015, она дает мне такое же значение pi каждый раз – HDJEMAI

+0

Очень странно.Просто скопировал и вставил его снова и провел дважды. Впервые я получил 3.141737761473218, а во второй раз я получил 3.141576654805244. Я нахожусь в Windows, если это что-то значит. Возможно, я должен попробовать это в Linux. – rangeme

+0

Я получаю все время 3.141592653589971 Ведьма кажется хорошей – HDJEMAI

ответ

2

Ваша проблема связана с тем, что вы забыли объявить xprivate. Если вы измените директиву OpenMP в:

#pragma omp parallel for num_threads(NUM_THREADS) reduction(+:sum) private(x) 

ваш код становится действительным.

Однако, есть еще два вопроса, в здесь:

  1. #pragma omp ordered не имеет смысла, так как вы не в parallel регионе. Вы должны удалить его.
  2. Использование clock() для измерения времени в многопоточном коде является опасным, а не потому, что функция не потокобезопасным, а потому, что она возвращает время CPU текущих потоков и его детской, а не истекшее время. Поэтому вы часто получаете результаты, почти идентичные с включенным OpenMP и без него, и люди задаются вопросом, почему их код не выдает никакого ускорения ... Поэтому, если у вас нет веских оснований для использования clock(), вместо этого используйте omp_get_wtime().
+2

Обратите внимание, что OP использует Windows. В библиотеке времени выполнения Microsoft C, которую MSVC и MinGW (но не MinGW-w64) используют 'clock()', возвращает время стены. Но я согласен, что 'omp_get_wtime()' лучше всего использовать в целом. –

+0

Использование 'default (none)' является хорошей идеей именно по этой причине. – Richard

3

Помимо ошибок, отмеченных Жилем, здесь есть более фундаментальная проблема.

Сокращение поперечных потоков не обязательно должно быть детерминированным. Порядок, в котором объединены вклады за потоки, может меняться при каждом выполнении кода. Если вы не знаете, для чего это важно, перейдите к разделу «What Every Computer Scientist Should Know About Floating-Point Arithmetic»

Если вы не поняли, что все три потока выполняли сокращение суммы на десятичной арифметической машине, которая поддерживает три цифры точность. Предположим, что мы накапливаем множество (100, -100, 0.1), если мы добавим их в этом порядке, у нас будет 100 - 100 = 0 + 0,1 = 0,1, однако, если мы добавим их в порядок (100, 0,1, -100), мы получим 100 + 0,1 = 100 (три значимые цифры, помните!) -100 == 0

Если вы используете компилятор Intel, есть переменная среды, которую вы можете установить для запроса детерминированных сокращений (KMP_DETERMINISTIC_REDUCTION), однако это только усиливает детерминизм при использовании того же количества потоков. Он не обеспечивает его выполнение между прогонами с различным количеством потоков. (Для этого потребуется принудительное выполнение заказа на накопление вкладов за потоки, что потребует различного генерации кода и некоторой межпоточной синхронизации).