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.