在CUDA上以原子方式交换内存指针

时间:2017-12-07 07:42:46

标签: pointers cuda swap atomic-swap

我在内存中有两个指针,我想以原子方式交换它,但CUDA中的原子操作只支持int类型。有一种方法可以进行以下交换吗?

classA* a1 = malloc(...);
classA* a2 = malloc(...);
atomicSwap(a1,a2);

2 个答案:

答案 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();
        }
    }
};