БПФ с CUDA на одних и тех же данных каждый раз дает разные результаты? - PullRequest
0 голосов
/ 14 февраля 2012

Почему при использовании CUDA, если я выполняю БПФ размером 1 миллион, я каждый раз получаю несколько слегка отличающиеся результаты?

Мое оборудование имеет архитектуру Fermi.

1 Ответ

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

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

РЕДАКТИРОВАТЬ: Просто чтобы прояснить, это является необходимым (хотя и недостаточным) условием, чтобы CUDA не гарантировала, что одно и то же ядро ​​будет выполняться в одном и том же порядке при нескольких выполнениях. Если CUDA действительно гарантирует это, то не должно быть возможным, чтобы порядок выполнения арифметических операций варьировался от прогона к прогону, и поэтому не следует ожидать увидеть разные значения для одного и того же вычисления с плавающей запятой.

Вот простая программа на C, демонстрирующая вышеуказанное утверждение. Попробуйте код

#include <stdio.h>

int main()
{
   float a = 100.0f, b = 0.00001f, c = 0.00001f;

   printf("a + b + c = %f\n", a + b + c);
   printf("b + c + a = %f\n", b + c + a);
   printf("a + b + c == b + c + a ? %d\n", (a + b + c) == (b + c + a));

   return 0;
}

в Linux и посмотрите, что вы получите (я использую 64-битный RHEL 6 и gcc версии 4.4.4-13). Мой вывод следующий:

[user@host directory]# gcc add.c -o add
[user@host directory]# ./add
a + b + c = 100.000015
b + c + a = 100.000023
a + b + c == b + c + a ? 0

РЕДАКТИРОВАТЬ: обратите внимание, что, хотя эта программа может предположить, что основная проблема заключается в том, что сложение с плавающей точкой некоммутативно, на самом деле сложение с плавающей точкой неассоциативно (поскольку C оценивает операции сложения слева направо верно, так уж получилось, что первое сложение выполняется как (a + b) + c, а второе - как (b + c) + a). Причиной неассоциативности является то, что представления с плавающей точкой могут представлять только конечное число значащих цифр (в базе 2, но обсуждение системы с базой 10 по существу эквивалентно). Например, если можно представить только три значащие цифры, мы получим (100 + 0,5) + 0,5 = 100 + 0,5 = 100, тогда как 100 + (0,5 + 0,5) = 100 + 1 = 101. В первом случае промежуточный результат 100 + 0,5 должен быть усечен (или, возможно, округлен в большую сторону), поскольку невозможно представить промежуточное значение 100,5 только с тремя значащими цифрами.

Существует ряд важных последствий этого явления; например, вы получите более точный ответ, добавив числа в порядке возрастания размера (экспонента). Реальный вывод состоит в том, что вы не должны ожидать, что результаты будут идентичны, если только вычисления не выполняются в том же порядке, что может быть трудно гарантировать при использовании CUDA на реальном GPU.

...