我有两个CUDA函数可以操作全局内存中的链表。函数pmalloc
删除其中一个列表的head元素。它首先选择一个列表,然后调用实际删除head元素的pmallocBucket
。如果所选列表为空,pmalloc
将尝试其他列表。另一方面,pfree
函数会将新的head元素插入到列表中。
通过信号量实现相互排斥,每个链表都有一个信号量。信号量的实现来自 CUDA By Example 一书。在其他一些测试代码中,信号量工作正常。
我对代码的问题如下:有时,多个线程会尝试同时访问同一个链表。这些访问由信号量成功地序列化,但有时,线程将从列表中删除与前一个线程相同的头元素。这可能会立即连续发生,或者中间可能存在一个或多个其他线程。然后该线程将free
一个未分配的内存区域,我的程序崩溃。
以下是上述功能。 mmd
是全局内存中的一个结构,它是从另一个函数初始化的。
extern __device__ void wait(int* s) {
while(atomicCAS(s, 0, 1) != 0);
}
extern __device__ void signal(int* s) {
atomicExch(s, 0);
}
__device__ void pfree(Expression* node) {
LinkedList* l = (LinkedList*) malloc(sizeof(LinkedList));
l->cell = node;
node->type = EMPTY;
node->funcidx = 0;
node->name = NULL;
node->len = 0;
node->value = 0;
node->numParams = 0;
free(node->params);
int targetBin = (blockIdx.x * mmd.bucketSize + threadIdx.x) / BINSIZE;
/*
* The for loop and subsequent if are necessary to make sure that only one
* thread in a warp is actively waiting for the lock on the semaphore.
* Leaving this out will result in massive headaches.
* See "CUDA by example", p. 273
*/
for(int i = 0; i < WARPSIZE; i++) {
if(((threadIdx.x + blockIdx.x * blockDim.x) % WARPSIZE) == i) {
wait(&mmd.bucketSemaphores[targetBin]);
l->next = mmd.freeCells[targetBin];
mmd.freeCells[targetBin] = l;
signal(&mmd.bucketSemaphores[targetBin]);
}
}
}
__device__ Expression* pmalloc() {
Expression* retval = NULL;
int i = 0;
int bucket = (blockIdx.x * mmd.bucketSize + threadIdx.x) / BINSIZE;
while(retval == NULL && i < mmd.numCellBins) {
retval = pmallocBucket((i + bucket) % mmd.numCellBins);
i++;
}
if(retval == NULL) {
printf("(%u, %u) Out of memory\n", blockIdx.x, threadIdx.x);
}
return retval;
}
__device__ Expression* pmallocBucket(int bucket) {
Expression* retval = NULL;
if(bucket < mmd.numCellBins) {
LinkedList* l = NULL;
for(int i = 0; i < WARPSIZE; i++) {
if(((threadIdx.x + blockIdx.x * blockDim.x) % WARPSIZE) == i) {
wait(&mmd.bucketSemaphores[bucket]);
l = mmd.freeCells[bucket];
if(l != NULL) {
retval = l->cell;
mmd.freeCells[bucket] = l->next;
}
signal(&mmd.bucketSemaphores[bucket]);
free(l);
}
}
}
return retval;
}
我很茫然。我不知道实际上出了什么问题,到目前为止,我所有的尝试都是无法解决的。非常感谢任何帮助。
P上。 S。:是的,我确实意识到原子操作和信号量的使用对于CUDA应用来说并不理想。但在这种情况下,到目前为止我还不知道如何以不同的方式实现这一点,而且我的项目处于一个绝对固定的截止日期,接近真正的快速,所以这将是必须的。
答案 0 :(得分:1)
您需要确保在信号量受保护的关键部分内完全执行列表操作,而不使用获取信号量之前的过时数据。
将l->next
和mmd.freeCells
声明为volatile,或通过原子函数(atomicExch()
)对其进行操作。
或者,您可以使用具有合适缓存运算符的内联汇编。使用mov.cg
进行加载应该足以确保在__threadfence()
之前没有使用本地缓存的值以及signal()
,以确保在信号量到达之前写入已达到全局内存释放。确保使用asm volatile(...)
,或者再次使用编译器可以将整个内联asm移出临界区。