CUDA atomicAdd для ошибки определения двойников
В предыдущих версиях CUDA, то atomicAdd не был реализован в парном разряде, поэтому она является общей для реализации этого, как здесь. С новым CUDA 8 RC я сталкиваюсь с проблемами, когда пытаюсь скомпилировать свой код, который включает такую функцию. Я предполагаю, что это связано с тем, что с Pascal и Compute Capability 6.0 была добавлена собственная двойная версия atomicAdd, но почему-то это должным образом не игнорируется для предыдущих вычислительных возможностей.
Приведенный ниже код используется для компиляции и запуска отлично с предыдущими версиями CUDA, но теперь я получаю эту ошибку компиляции:
test.cu(3): error: function "atomicAdd(double *, double)" has already been defined
Но если я удалю свою реализацию, я вместо этого получу эту ошибку:
test.cu(33): error: no instance of overloaded function "atomicAdd" matches the argument list
argument types are: (double *, double)
Я должен добавить, что я вижу это только в том случае, если я компилирую с -arch=sm_35
или подобным образом. Если я компилирую с -arch=sm_60
, я получаю ожидаемое поведение, т. е. только первую ошибку, и успешную компиляцию во втором случае.
Edit: кроме того, он специфичен для atomicAdd
- если я изменю имя, он хорошо работает.
Это действительно похоже на компилятор жук. Может ли кто-то еще подтвердить, что это так?
Пример кода:
__device__ double atomicAdd(double* address, double val)
{
unsigned long long int* address_as_ull = (unsigned long long int*)address;
unsigned long long int old = *address_as_ull, assumed;
do {
assumed = old;
old = atomicCAS(address_as_ull, assumed,
__double_as_longlong(val + __longlong_as_double(assumed)));
} while (assumed != old);
return __longlong_as_double(old);
}
__global__ void kernel(double *a)
{
double b=1.3;
atomicAdd(a,b);
}
int main(int argc, char **argv)
{
double *a;
cudaMalloc(&a,sizeof(double));
kernel<<<1,1>>>(a);
cudaFree(a);
return 0;
}
Edit: я получил ответ от Nvidia, которые признают эту проблему, и вот что говорят об этом разработчики:
Архитектура sm_60, которая недавно поддерживается в CUDA 8.0, имеет собственная функция fp64 atomicAdd. Из-за ограничений нашего инструментария и языка CUDA, то в декларации эта функция должна присутствуйте даже тогда, когда код не используется специально составлено для sm_60. Это вызывает проблему в коде, так как вы также определяете функция fp64 atomicAdd.
Встроенные функции CUDA, такие как atomicAdd, определяются реализацией и может быть изменен между выпусками CUDA. Пользователи не должны определять функции с теми же именами, что и любые встроенные функции CUDA. Мы бы так и сделали предлагаю вам переименовать вашу функцию atomicAdd в ту, которая не является то же самое, что и любые встроенные функции CUDA.
1 ответ:
Этот аромат atomicAdd - новый метод, введенный для вычислительной способности 6.0. Вы можете сохранить свою предыдущую реализацию других вычислительных возможностей, защищая ее с помощью определения макросов
#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600 #else <... place here your own pre-pascal atomicAdd definition ...> #endif
Этот макрос, названный макросом идентификации архитектуры, задокументирован здесь :
[7]}5.7.4. Макрос Идентификации Виртуальной АрхитектурыМакросу идентификации архитектуры
Этот макрос может быть использован при реализации функций GPU для определения виртуальной архитектуры, для которой он в настоящее время компилируется. Код хоста (код, не относящийся к GPU) не должен зависеть от него.__CUDA_ARCH__
присваивается трехзначная строка значений xy0 (оканчивающаяся на литерал 0) во время каждый этап компиляции nvcc 1, который компилируется для compute_xy.Я предполагаю, что NVIDIA не поместила его для предыдущего CC, чтобы избежать конфликта для пользователей, определяющих его и не переходящих к вычислительным возможностям >= 6.x. я бы не стал считать это ошибкой, а скорее доставкой релиза практика.
EDIT : macro guard был неполным (фиксированным) - вот полный пример.
#if !defined(__CUDA_ARCH__) || __CUDA_ARCH__ >= 600 #else __device__ double atomicAdd(double* a, double b) { return b; } #endif __device__ double s_global ; __global__ void kernel () { atomicAdd (&s_global, 1.0) ; } int main (int argc, char* argv[]) { kernel<<<1,1>>> () ; return ::cudaDeviceSynchronize () ; }
Компиляция с:
$> nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2016 NVIDIA Corporation Built on Wed_May__4_21:01:56_CDT_2016 Cuda compilation tools, release 8.0, V8.0.26
Командные строки (обе успешные):
$> nvcc main.cu -arch=sm_60 $> nvcc main.cu -arch=sm_35
Вы можете узнать, почему он работает с включаемым файлом:
sm_60_atomic_functions.h
, где метод не объявляется, если__CUDA_ARCH__
меньше 600.