cuda __syncthreads()在我的代码

时间:2015-09-28 09:19:45

标签: c++ cuda

所以情况就是这样。

我有一个运行while循环的线程块,当且仅当这些线程满足某些条件时,我才需要继续循环。为此,我使用共享变量作为continue标志,标志在每次迭代开始时由线程#0清除,后跟__syncthreads(),并且如果继续,可以在迭代期间由任何线程设置条件得到满足。然后在下一次迭代的检查点之前放置另一个对__syncthreads()的调用,以确保线程同步。内核基本上是这样的:

__global__ void foo(void* data) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x || threadIdx.y || threadIdx.z) {
            blockContinueFlag = 0;
        }
        __syncthreads(); //synch1
        //some data manipulations...
        if(some predicate) {
            blockContinueFlag = true;
        }
        //some data manipulations...
        __syncthreads(); //synch2
    } while (blockContinueFlag);
}

问题是屏障synch2似乎在我的代码中不起作用,有时即使某些线程满足继续条件,内核也会终止(我通过检查主机端返回的数据来了解这一点)。为了进一步检查这一点,我在do-while循环之后设置了一个断点,如下面的代码,有时blockContinueFlag被称为true(我只能假设块在某些线程之前退出循环可以设置blockContinueFlag)。

__global__ void foo(void* data) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x || threadIdx.y || threadIdx.z) {
            blockContinueFlag = 0;
        }
        __syncthreads(); //synch1
        //some data manipulations...
        if(some predicate) {
            blockContinueFlag = true;
        }
        //some data manipulations...
        __syncthreads(); //synch2
    } while (blockContinueFlag);
    //a break point is set here
}

我记得从cuda手册中读到,如果对所有线程的谓词评估相同,则条件子句中允许__syncthreads(),这应该是这种情况。

我有另一个简化版本的代码,只是为了说明这一点。

__global__ void foo(int* data, int kernelSize, int threshold) {
    __shared__ int blockContinueFlag;
    do {
        if (threadIdx.x == 0) {
            blockContinueFlag = 0;
        }
        __syncthreads();
        if (threadIdx.x < kernelSize)  {
            data[threadIdx.x]--;
            for (int i = 0; i < threadIdx.x; i++);
            if (data[threadIdx.x] > threshold)
                blockContinueFlag = true;
        }
        __syncthreads();
    } while (blockContinueFlag);
}

int main()
{
    int hostData[1024], *deviceData;
    for (int i = 0; i < 1024; i++)
        hostData[i] = i;
    cudaMalloc(&deviceData, 1024 * sizeof(int));
    cudaMemcpy(deviceData, hostData, 1024 * sizeof(int), cudaMemcpyHostToDevice);
    foo << <1, 1024 >> >(deviceData, 512, 0);
    cudaDeviceSynchronize();
    cudaMemcpy(hostData, deviceData, 1024 * sizeof(int), cudaMemcpyDeviceToHost);
    fprintf(stderr, cudaGetErrorString(cudaGetLastError()));
    return 0;

}

hostData[]的期望值在{-511, -510, -509, ..., 0, 512, 513, 514,..., 1023}结尾处为main(),有时是实际情况。但在某些情况下,它会在VS 2013调试模式中生成以下值

[0]: -95
[1]: -94
...
[29]: -66
[30]: -65
[31]: -64
[32]: 31
[33]: 32
[34]: 33
...
[61]: 60
[62]: 61
[63]: 62
[64]: -31
[65]: -30
[66]: -29
...
[92]: -3
[93]: -2
[94]: -1
[95]: 0
[96]: 95
[97]: 96
[98]: 97
...

,这表明warp实际上并未同步。

所以有人知道这个的原因和/或是否有办法让线程障碍正常工作?

任何帮助将不胜感激。提前谢谢。

2 个答案:

答案 0 :(得分:3)

所以这是我的解决方案,其中包含一个Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeAsUpIndicator(R.drawable.gallery_page_button_back); @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if(id == android.R.id.home) { this.finish(); return true; } //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } 而不是三个__syncthreads_or()

__syncthreads()

在实践中,这比三个同步线快一点。

再次感谢您的帖子。

答案 1 :(得分:2)

第一个示例是检查条件并清除syncthreads之间相同代码片段中的标志。这是一种读写后的危险。 为了更好地解释您的问题,让我像这样重写您的示例:

__syncthreads()

在这个例子中,检查标志和循环中断更详细,但它本质上是相同的代码(加上最开始的冗余检查)。

在此示例中,以及在代码中,线程0可以在线程33(另一个warp)执行检查之前检查循环条件清除标志。这会引起分歧,所有的邪恶都会消失。

要修复 - 您需要在清除标记之前添加另一个ChangeType