GTS 250和费米设备之间的CUDA块同步差异

GTS 250和费米设备之间的CUDA块同步差异,cuda,synchronization,gpgpu,nvidia,Cuda,Synchronization,Gpgpu,Nvidia,所以我一直在做一个在全局内存中创建哈希表的程序。该代码在GTS250(Compute 1.1设备)上完全可用(尽管速度较慢)。但是,在Compute 2.0设备(C2050或C2070)上,哈希表已损坏(数据不正确,指针有时错误) 当只使用一个块(两个设备)时,代码基本上可以正常工作。然而,当使用2个或更多块时,它仅在GTS250上工作,而不在任何费米设备上工作 我知道两个平台之间的warp调度和内存架构是不同的,我在开发代码时会考虑到这一点。根据我的理解,使用\uu theadfence()应

所以我一直在做一个在全局内存中创建哈希表的程序。该代码在GTS250(Compute 1.1设备)上完全可用(尽管速度较慢)。但是,在Compute 2.0设备(C2050或C2070)上,哈希表已损坏(数据不正确,指针有时错误)

当只使用一个块(两个设备)时,代码基本上可以正常工作。然而,当使用2个或更多块时,它仅在GTS250上工作,而不在任何费米设备上工作

我知道两个平台之间的warp调度和内存架构是不同的,我在开发代码时会考虑到这一点。根据我的理解,使用
\uu theadfence()
应该确保所有全局写入都已提交并对其他块可见,但是,从损坏的哈希表来看,它们似乎不可见

我也把问题发布到英伟达CUDA开发者论坛上,可以找到。 相关代码如下:

__device__ void lock(int *mutex) {
    while(atomicCAS(mutex, 0, 1) != 0);
}

__device__ void unlock(int *mutex) {
    atomicExch(mutex, 0);
}

__device__ void add_to_global_hash_table(unsigned int key, unsigned int count, unsigned int sum, unsigned int sumSquared, Table table, int *globalHashLocks, int *globalFreeLock, int *globalFirstFree)
{
    // Find entry if it exists
    unsigned int hashValue = hash(key, table.count);

    lock(&globalHashLocks[hashValue]);

    int bucketHead = table.entries[hashValue];
    int currentLocation = bucketHead;

    bool found = false;
    Entry currentEntry;

    while (currentLocation != -1 && !found) {
        currentEntry = table.pool[currentLocation];
        if (currentEntry.data.x == key) {
            found = true;
        } else {
            currentLocation = currentEntry.next;
        }
    }

    if (currentLocation == -1) {
        // If entry does not exist, create entry
        lock(globalFreeLock);
        int newLocation = (*globalFirstFree)++;
        __threadfence();
        unlock(globalFreeLock);

        Entry newEntry;
        newEntry.data.x = key;
        newEntry.data.y = count;
        newEntry.data.z = sum;
        newEntry.data.w = sumSquared;
        newEntry.next = bucketHead;

        // Add entry to table
        table.pool[newLocation] = newEntry;
        table.entries[hashValue] = newLocation;
    } else {
        currentEntry.data.y += count;
        currentEntry.data.z += sum;
        currentEntry.data.w += sumSquared;
        table.pool[currentLocation] = currentEntry;
    }

    __threadfence();
    unlock(&globalHashLocks[hashValue]);
}

__threadfence保证在返回之前,对全局内存的写入对当前块中的其他线程可见。这与“全局内存上的写入操作已完成”不同!考虑每个多核上的缓存。

正如本文中所指出的,问题在于一级缓存的一致性。使用
\u threadfence()
将保证共享和全局内存写入对其他线程可见,因为它不是原子的,
块1
中的
线程x
可能会达到缓存的内存值,直到
块0
中的
线程y
对threadfence指令执行为止。相反,LSChien在他的帖子中建议使用
atomicCAS()
来强制线程从全局内存而不是缓存值中读取。正确的方法是将内存声明为volatile,要求网格中的所有其他线程都能立即看到对该内存的每次写入。

该设备功能的代码不足以说明错误。设备功能的正确运行取决于正确保护关键部分的锁定和解锁的完整性,但它们是如何工作的?只是添加了锁定/解锁代码。不,这就是_uthreadfence_block所做的__threadfence确保全局内存事务在设备级别对所有线程可见。在CUDA中,volatile实际上意味着必须立即从寄存器将值写回内存。它不能保证写入操作何时对另一个线程可见。其效果是防止编译器应用任何优化,这将导致从代码中删除内存存储指令,并在寄存器中从一个操作到另一个操作传递中间结果。