Я считаю, что проблема заключается в объявлении int i
и float argg
вне параллельного цикла.
Происходит следующее: все ваши 200 потоков перезаписывают i
и argg
, поэтому иногда argg
потока перезаписывается argg
из другого потока, что приводит к непредсказуемой ошибке, которую вы наблюдаете.
Вот рабочий код, который всегда печатает одно и то же значение (до 6 десятичных знаков или около того):
void main()
{
int nb = 200, blob;
float summ = 0, dx;// , argg;
dx = 1. / nb;
printf("\n dx------------: %f \n", dx);
omp_set_num_threads(nb);
#pragma omp parallel
{
blob = omp_get_num_threads();
printf("\n we have now %d number of threads...\n", blob);
int i = omp_get_thread_num();
printf("\n i is now: %d \n", i);
float argg = (4. / (1. + i * dx*i*dx))*dx;
summ = summ + argg;
printf("\t\t and summ is %f \n", summ);
}
printf("\ntotal summ after loop: %f\n", summ);
}
Однако изменение последней строки на% .9f показывает, что на самом деле это не то же самое число с плавающей точкой. Это связано с числовыми ошибками при сложении с плавающей запятой. a + b + c не гарантирует тот же результат, что и a + c + b. Вы можете попробовать это в следующем примере:
Сначала добавьте float* arr = new float[nb];
перед параллельным циклом И arr[i] = argg;
в параллельном цикле, после определения argg
, конечно. Затем добавьте после параллельного цикла:
float testSum = 0;
for (int i = 0; i < nb; i++)
testSum += arr[i];
printf("random sum: %.9f\n", testSum);
std::sort(arr, arr + nb);
testSum = 0;
for (int i = 0; i < nb; i++)
testSum += arr[i];
printf("sorted sum: %.9f\n", testSum);
testSum = 0;
for (int i = nb-1; i >= 0; i--)
testSum += arr[i];
printf("reversed sum: %.9f\n", testSum);
Скорее всего, отсортированная сумма и обратная сумма немного отличаются, даже если они составлены путем сложения одинаковых 200 чисел.
Еще одна вещь, которую вы, возможно, захотите отметить, - это то, что вы вряд ли найдете процессор, который может фактически запускать 200 потоков параллельно. Большинство распространенных процессоров могут обрабатывать от 4 до 32 потоков, в то время как специализированные серверные процессоры могут использовать до 112 потоков с Xeon Platinum 9282. стоимостью $ 15 тыс. *
Поэтому мы обычно делаем следующее:
Удаляем omp_set_num_threads(nb);
, чтобы использовать рекомендуемое количество потоков
Мы удаляем int i = omp_get_thread_num();
, чтобы использовать int i
для цикла for
Переписываем цикл как цикл for:
#pragma omp parallel for
for (int i = 0; i < nb; i++)
{...}
Результат должен быть идентичным, но теперь вы используете только столько потоков, сколько доступно на реальном оборудовании. Это уменьшает переключение контекста между потоками и должно повысить быстродействие вашего кода.