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. Из-за ограничений нашего toolchain и язык 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
этот макрос с именем макрос идентификации архитектуры документирован здесь:
5.7.4. Макрос Идентификации Виртуальной Архитектуры
макрос идентификации архитектуры
__CUDA_ARCH__
присваивается строка трехзначного значения xy0 (заканчивающийся литералом 0) во время каждого этапа компиляции nvcc 1, который компилируется для compute_xy.этот макрос может использоваться при реализации функций GPU для определения виртуальной архитектуры, для которой он в настоящее время компилируется. Код хоста (код без GPU) не должен зависеть от него.
Я предполагаю, что NVIDIA не разместила его для предыдущего CC, чтобы избежать конфликта для пользователей, определяющих его и не переходящих к вычислительной способности >= 6.х. Я бы не стал считайте это ошибкой, хотя, скорее, практика доставки релиза.
редактировать: 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
вы можете узнать, почему он работает с файлом include:sm_60_atomic_functions.h
, где метод не объявлен, если __CUDA_ARCH__
ниже 600.