Результаты ядра CUDA различаются в режиме выпуска
-
11-12-2019 - |
Вопрос
Я тестирую некоторый код в CUDA (я новичок в CUDA и это мое первое приложение).На данный момент я добился результатов в CUDA, которые были получены при последовательном запуске кода на ЦП.Я использую Visual Studio 2010, а конфигурация сборки — отладочная.Но как только я меняю конфигурацию сборки на «Выпуск», я начинаю получать неправильные результаты.Я не смог пользоваться форумами Nvidia, так как они в данный момент недоступны.Может ли кто-нибудь с опытом CUDA указать на проблему.Код выглядит следующим образом
__global__ void MyKernel(int *Nptr,int *deltaptr, double *gravityptr, double *separationptr, double *fconptr, double *xForce, double *yForce, double *zForce,
double *xPos, double *yPos, double *zPos )
{
int N = *Nptr;
int delta= *deltaptr;
double gravity= *gravityptr;
double separation = *separationptr;
double fcon = *fconptr;
double len=0.0;
double r12X =0.0;
double r12Y =0.0;
double r12Z =0.0;
double PE=0.0;
int nx = blockDim.x * blockIdx.x + threadIdx.x;//use this place of nx
//int ny = blockDim.x * blockIdx.x + threadIdx.y;//use this place of ny
int ny = blockDim.y * blockIdx.y + threadIdx.y;
//printf("nx:%d ny:%d\n", nx,ny);
if(!(nx< N && ny <N))
return;
//printf("nx:%d ny:%d\n", nx,ny);
xForce[nx*N+ny] = 0.0;
yForce[nx*N+ny] = -gravity;
zForce[nx*N+ny] = 0.0;
int lowerValuedx = maxOnDevice(nx-delta,0);
int upperValuedx=minOnDevice(nx+delta+1,N);
for(int dx=lowerValuedx; dx<upperValuedx;dx++)
{
int lowerValuedy=maxOnDevice(ny-delta,0);
int upperValuedy=minOnDevice(ny+delta+1,N);
for(int dy=lowerValuedy; dy<upperValuedy;dy++)
{
len=sqrt((double)((nx-dx)*(nx-dx)+(ny-dy)*(ny-dy)) ) *separation;
bool condition = ny!=dy;
bool condition1 = nx!=dx;
//if (nx!=dx || ny!=dy)
if (condition || condition1)
{
r12X = xPos[dx*N+dy] - xPos[nx*N+ny];
r12Y = yPos[dx*N+dy] - yPos[nx*N+ny];
r12Z = zPos[dx*N+dy] - zPos[nx*N+ny];
xForce[nx*N+ny] = xForce[nx*N+ny] +fcon*normxOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);
yForce[nx*N+ny]= yForce[nx*N+ny] +fcon*normyOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);
zForce[nx*N+ny]= zForce[nx*N+ny] +fcon*normzOnDevice(r12X,r12Y,r12Z)*(magOnDevice(r12X,r12Y,r12Z)-len);
}
}
}
}
Спасибо
Решение
Нет ничего необычного в том, что числовые различия существуют между результатами ЦП и ГП, а также между отладочными и выпускными сборками на ГП.Это не означает, что какой-либо набор результатов неверен, но один может быть более точным, чем другой.См. следующий технический документ от NVIDIA, в котором обсуждаются различные механизмы, которые могут привести к числовым расхождениям:
http://developer.download.nvidia.com/assets/cuda/files/NVIDIA-CUDA-Floating-Point.pdf
Вы можете проверить, устраняет ли флаг nvcc -fmad=false различия, которые вы видите, что указывает на то, что они вызваны слиянием FMA/FMAD и, следовательно, вероятно, безвредны.
Графические процессоры обеспечивают операции FMAD и FMA (слитное умножение-сложение), которые объединяют умножение с плавающей запятой с зависимым сложением с плавающей запятой в одну операцию.Это повышает производительность, поскольку комбинированная операция обычно занимает столько же времени, сколько и каждая из ее составных частей.Однако поведение округления любой комбинированной операции отличается от использования двух операций округления по отдельности:
FMAD одинарной точности (вычислительная способность < 2,0) усекает результат умножения, а затем округляет результат окончательного сложения в соответствии со стандартом IEEE-754, округляя до ближайшего или четного.Напротив, FMA (одинарная точность при вычислительных возможностях >= 2,0 и двойная точность) вычисляет неокругленное произведение двойной ширины, добавляет к нему третий операнд и округляет окончательную сумму в соответствии с IEEE-754, округляя до ближайшего или -даже.Из-за этого единственного округления средняя точность, обеспечиваемая FMA, превосходит использование двух операций округления по отдельности.Операции FMA определены в версии стандарта IEEE-754 с плавающей запятой 2008 года.
По умолчанию для сборок выпуска компилятор CUDA активно генерирует объединенные операции (FMAD, FMA) для достижения наилучшей производительности.Другими словами, значением по умолчанию для компилятора является -fmad=true, что позволяет компилятору объединять умножения и сложения чисел с плавающей запятой.При указании -fmad=false слияние операций умножения и сложения запрещается, что обычно обеспечивает большую согласованность с результатами ЦП, поскольку большинство ЦП не поддерживают операцию FMA.Очевидно, что отключение использования объединенных операций отрицательно влияет на производительность, поэтому -fmad=false в первую очередь полезен для проверки работоспособности.
При подозрении на проблемы с точностью я обычно рекомендую сравнение с эталонной реализацией более высокой точности (например,один основан на методах четырехкратной точности или дабл-дабл), чтобы точно оценить ошибку как на ЦП, так и на графическом процессоре, а не использовать версию ЦП в качестве эталона (поскольку на результаты ЦП также влияет ошибка округления).