所以情况就是这样。
我有一个运行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实际上并未同步。
所以有人知道这个的原因和/或是否有办法让线程障碍正常工作?
任何帮助将不胜感激。提前谢谢。
答案 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
。