我在内存中有两个指针,我想以原子方式交换它,但CUDA中的原子操作只支持int类型。有一种方法可以进行以下交换吗?
classA* a1 = malloc(...);
classA* a2 = malloc(...);
atomicSwap(a1,a2);
答案 0 :(得分:1)
虽然CUDA提供原子,但它们不能同时覆盖多个(可能是远程的)内存位置。
要执行此交换,您需要"保护"使用类似互斥体的方式访问这两个这些值,并且让任何想要写值的人在关键部分的持续时间内保留互斥锁(如在C中) ++的主持人std::lock_guard
)。这可以使用CUDA的实际原子设施来完成,例如比较和交换,并且是这个问题的主题:
Implementing a critical section in CUDA
@RobertCrovella提到了上面的一个警告:如果你可以使用一对32位偏移而不是64位指针,那么如果要将它们存储在64位中对齐struct
,您可以在整个结构上使用compare-and-exchange来实现整个struct
的原子交换。
您的代码实际上看起来并不像设备上运行的内容:在启动内核并执行实际工作之前,通常(但并非总是)从主机端完成内存分配。如果您可以确保这些更改只发生在主机端(想想CUDA事件和回调),并且设备端代码不会受到它们的干扰 - 您可以使用普通的C ++工具进行并发编程(如{ {1}}我在上面提到过。)
答案 1 :(得分:-1)
我设法得到了所需的行为,它不是原子交换,但仍然是安全的。上下文是一个单调的链接列表,在CPU和GPU上工作:
template<typename T>
union readablePointer
{
T* ptr;
unsigned long long int address;
};
template<typename T>
struct LinkedList
{
struct Node
{
T value;
readablePointer<Node> previous;
};
Node start;
Node end;
int size;
__host__ __device__ void initialize()
{
size = 0;
start.previous.ptr = nullptr;
end.previous.ptr = &start;
}
__host__ __device__ void push_back(T value)
{
Node* node = nullptr;
malloc(&node, sizeof(Node));
readablePointer<Node> nodePtr;
nodePtr.ptr = node;
nodePtr.ptr->value = value;
#ifdef __CUDA_ARCH__
nodePtr.ptr->previous.address = atomicExch(&end.previous.address, nodePtr.address);
atomicAdd(&size,1);
#else
nodePtr.ptr->previous.address = end.previous.address;
end.previous.address = nodePtr.address;
size += 1;
#endif
}
__host__ __device__ T pop_back()
{
assert(end.previous.ptr != &start);
readablePointer<Node> lastNodePtr;
lastNodePtr.ptr = nullptr;
#ifdef __CUDA_ARCH__
lastNodePtr.address = atomicExch(&end.previous.address,end.previous.ptr->previous.address);
atomicSub(&size,1);
#else
lastNodePtr.address = end.previous.address;
end.previous.address = end.previous.ptr->previous.address;
size -= 1;
#endif
T toReturn = lastNodePtr.ptr->value;
free(lastNodePtr.ptr);
return toReturn;
}
__host__ __device__ void clear()
{
while(size > 0)
{
pop_back();
}
}
};