CUDAの以前のバージョンでは、atomicAddはdoubleに対して実装されていなかったため、これを here のように実装することが一般的です。新しいCUDA 8 RCでは、そのような関数を含むコードをコンパイルしようとすると問題が発生します。これは、PascalとCompute Capability 6.0で、atomicAddのネイティブダブルバージョンが追加されたためと思われますが、以前のCompute Capabilityでは、どういうわけか適切に無視されていません。
以下のコードは以前の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
を使用してコンパイルすると、予期した動作、つまり最初のエラーのみが発生し、2番目のケースでは正常にコンパイルされます。
編集:また、これは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;
}
編集:この問題を認識しているNvidiaから回答を得ました。開発者がそれについて次のように言っています。
CUDA 8.0で新たにサポートされるsm_60アーキテクチャには、ネイティブのfp64 atomicAdd関数があります。ツールチェーンとCUDA言語の制限のため、コードがsm_60用に特別にコンパイルされていない場合でも、この関数の宣言が必要です。また、fp64 atomicAdd関数も定義しているため、コードに問題が発生します。
AtomicAddなどのCUDA組み込み関数は実装定義であり、CUDAリリース間で変更できます。ユーザーは、CUDA組み込み関数と同じ名前の関数を定義しないでください。 atomicAdd関数の名前を、どのCUDA組み込み関数とも異なる名前に変更することをお勧めします。
この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__
には、compute_xyをコンパイルする各nvccコンパイルステージ1で、3桁の値の文字列xy0(リテラル0で終わる)が割り当てられます。このマクロは、現在コンパイルされている仮想アーキテクチャーを判別するためのGPU関数の実装で使用できます。ホストコード(非GPUコード)はそれに依存してはなりません。
私は、NVIDIAが以前のCCにそれを定義し、ユーザーがそれを定義し、Compute Capability> = 6.xに移行しないようにするための競合を回避するためにそれを配置しなかったと思います。私はそれをバグとは考えていません。むしろリリース配信の慣行です。
[〜#〜] edit [〜#〜]:マクロガードが不完全(修正済み)-ここに完全な例があります。
#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未満です。