我想知道是否有一种方法可以实现双重类型的原子操作(特别是atomic_add)。
对于浮动代码,此代码有效,但atomic_xchg不支持双重代码:
while ((value = atomic_xchg(addr, atomic_xchg(addr, 0.0f)+value))!=0.0f);
答案 0 :(得分:2)
过去我一直在寻找同样的东西,我发现了这个:https://github.com/ddemidov/vexcl-experiments/blob/master/sort-by-key-atomic.cpp。 最后我想出了解决问题的不同方法,所以我没有使用它。这是代码:
"#pragma OPENCL EXTENSION cl_khr_fp64: enable\n"
"#pragma OPENCL EXTENSION cl_khr_int64_base_atomics: enable\n"
"void AtomicAdd(__global double *val, double delta) {\n"
" union {\n"
" double f;\n"
" ulong i;\n"
" } old;\n"
" union {\n"
" double f;\n"
" ulong i;\n"
" } new;\n"
" do {\n"
" old.f = *val;\n"
" new.f = old.f + delta;\n"
" } while (atom_cmpxchg ( (volatile __global ulong *)val, old.i, new.i) != old.i);\n"
"}\n"
"kernel void atomic_reduce(\n"
" ulong n,\n"
" global const int * key,\n"
" global const double * val,\n"
" global double * sum\n"
")\n"
"{\n"
" for(size_t idx = get_global_id(0); idx < n; idx += get_global_size(0))\n"
" AtomicAdd(sum + key[idx], val[idx]);\n"
"}\n",
"atomic_reduce"
答案 1 :(得分:0)
最初发帖的方式和doqtor的回答都很好。基本上,有两种方法可以实现双精度:使用联合或使用OpenCL as_type functions。答案的末尾提供了OpenCL 1.0代码段(对于OpenCL 2.x,可以将其缩短,但NVIDIA目前尚不支持)。至于性能,我个人在大溪地芯片上实现AMD OpenCL的经验,所有这些变体或多或少都会产生相同的执行时间(as_和union变体甚至在大多数经过测试的编译器上都产生相同的优化ISA代码)。因此,使用一个或另一个变体是个人喜好的问题。
// define REALDOUBLES for double precision, undefine for single
#if REALDOUBLES
// extensions needed
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
#ifdef cl_khr_int64_base_atomics
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
#endif
// definitions
#define UINTVAR ulong
#define AS_INT as_ulong
#define AS_REAL as_double
#define ATOM_CMPXCHG atom_cmpxchg
#define ATOM_XCHG atom_xchg
#else
// extensions needed
#ifdef cl_khr_local_int32_base_atomics
#pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable
#endif
#ifdef cl_khr_global_int32_base_atomics
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
#endif
// definitions
#define UINTVAR uint
#define AS_INT as_uint
#define AS_REAL as_float
#define ATOM_CMPXCHG atomic_cmpxchg
#define ATOM_XCHG atomic_xchg
#endif
// as_ variants
// variant from GROMACS - https://streamhpc.com/blog/2016-02-09/atomic-operations-for-floats-in-opencl-improved/
inline void atomic_add_local(volatile local REAL * const source, const REAL operand) {
UINTVAR expected, current;
current = AS_INT(*source);
do {
expected = current;
current = ATOM_CMPXCHG((volatile local UINTVAR *)source, expected, AS_INT(AS_REAL(expected) + operand));
} while (current != expected);
}
// NVIDIA variant
inline void atomic_add_local(local REAL * const source, const REAL operand) {
UINTVAR old = AS_INT(operand);
while ((old = ATOM_XCHG((local UINTVAR *)source, AS_INT(AS_REAL(ATOM_XCHG((local UINTVAR *)source, AS_INT((REAL)0))) + AS_REAL(old)))) != AS_INT((REAL)0));
}
// union variants
typedef union {
UINTVAR intVal;
REAL floatVal;
} uni;
// NVIDIA variant
inline void atomic_add_local(local REAL * const source, const REAL operand) {
uni old, t, zero;
old.floatVal = operand;
zero.floatVal = 0;
do {
t.intVal = ATOM_XCHG((local UINTVAR *)source, zero.intVal);
t.floatVal += old.floatVal;
} while ((old.intVal = ATOM_XCHG((local UINTVAR *)source, t.intVal)) != zero.intVal);
}
// shortened variant from GROMACS - https://streamhpc.com/blog/2016-02-09/atomic-operations-for-floats-in-opencl-improved/
inline void atomic_add_local(volatile local REAL * const source, const REAL operand) {
uni expected, current;
current.floatVal = *source;
do {
expected.floatVal = current.floatVal;
current.floatVal = expected.floatVal + operand;
current.intVal = ATOM_CMPXCHG((volatile local UINTVAR *)source, expected.intVal, current.intVal);
} while (current.intVal != expected.intVal);
}
显然可以用local<->global
代替全局内存。