【问题标题】:CUDA threads appear to be out of syncCUDA 线程似乎不同步
【发布时间】:2013-09-24 03:27:50
【问题描述】:

我有一个问题,即使我使用的是同步线程,一个线程似乎落后于其他线程。以下摘录摘自一个大型程序,我已经尽可能多地删减了它,但它仍然重现了我的问题。我发现在运行此代码时,test4 变量不会为所有线程返回相同的值。我的理解是,使用 TEST_FLAG 变量它应该引导所有线程进入if (TEST_FLAG == 2) 条件,因此数组 test4 中的每个元素都应该返回一个值 43。但是我发现所有元素都返回 43,除了返回的线程 0 0. 看起来好像不是所有线程都到达相同的同步线程。我进行了多次测试,发现删除更多代码(例如for (l=0; l<1; ++l) 循环)可以解决问题,但我不明白为什么。任何关于为什么我的线程不都返回相同值的帮助将不胜感激。

import numpy as np
import pycuda.driver as drv
import pycuda.compiler
import pycuda.autoinit
import pycuda.gpuarray as gpuarray
import pycuda.cumath as cumath
from pycuda.compiler import SourceModule

gpu_code=SourceModule("""
    __global__ void test_sync(double *test4, double *test5)
    {
        __shared__ double rad_loc[2], boundary[2], boundary_limb_edge[2];
        __shared__ int TEST_FLAG;
        int l;


        if (blockIdx.x != 0)
        {
            return;
        }

        if(threadIdx.x == 0)
        {
            TEST_FLAG = 2;
            boundary[0] = 1;
        }

        test4[threadIdx.x] = 0;
        test5[threadIdx.x] = 0;

        if (threadIdx.x == 0)
        {
            rad_loc[0] = 0.0;
        }
        __syncthreads();

        for (l=0; l<1; ++l)
        {
            __syncthreads();
            if (rad_loc[0] > 0.0)
            {
                test5[threadIdx.x] += 1;

                if ((int)boundary[0] == -1)
                {
                    __syncthreads();
                    continue;
                }
            }
            else
            {
                if (threadIdx.x == 0)
                {
                    boundary_limb_edge[0] = 0.0;
                }
            }
            __syncthreads();

            if (TEST_FLAG == 2)
            {
                test4[threadIdx.x] = 43;
                __syncthreads();

                TEST_FLAG = 99;
            }
            __syncthreads();

            return;
        }
        return;
    }

    """)


test_sync = gpu_code.get_function("test_sync")

DATA_ROWS=[100,100]

blockshape_data_mags    = (int(64),1, 1)
gridshape_data_mags     = (int(sum(DATA_ROWS)), 1)

test4 = np.zeros([1*blockshape_data_mags[0]], np.float64)
test5 = np.zeros([1*blockshape_data_mags[0]], np.float64)

test_sync(drv.InOut(test4), drv.InOut(test5), block=blockshape_data_mags, grid=gridshape_data_mags)

print test4
print test5

【问题讨论】:

  • __synchthreads() 可能在条件语句中没有用户定义的行为,即使所有线程都在同一个代码块中。 stackoverflow.com/questions/12519573/… 给出了一个例子
  • 我的印象是,如果所有线程都没有遵循相同的分支,那么只有在您链接的示例中才会出现问题。在我的示例中,所有线程都遵循相同的路径,所以我不希望同步线程有任何问题。从B.6. Synchronization Functions of the CUDA programming guide __syncthreads() 允许在条件代码中使用,但前提是条件在整个线程块中的计算结果相同,否则代码执行可能会挂起或产生意外的副作用
  • 能否请您提供一个完整的复制器,而不仅仅是内核?我不知道你的启动配置是什么,以及其他细节,我不想猜测。这绝对是奇怪的代码。例如,您在初始化之前在条件中使用 boundary[0] 的值。请注意,SO 期望:“有关您编写的代码问题的问题必须在问题本身中描述特定问题 - 并包括有效的代码来重现它。有关指导,请参阅 SSCCE.org。”
  • 我在 Tesla C2075 卡上的 python 2.7.3 中使用 pycuda(版本 2011,2,2)运行它。我正在使用一块(64,1,1),网格(200,0,0)运行它。但它们对结果没有任何影响,test4 和 5 是长度等于线程数的数组。我已经用整个 python 脚本替换了上面的代码,包括对缺少边界 [0] 初始化的修正,我一定是不小心从原始代码中删除了。我希望这个信息帮助。如果您需要更多信息,请告诉我。
  • 我相信我已经复制了这个问题(虽然我没有使用 python)并将进一步研究它。同时,我相信如果您可以告诉 pyCUDA 将 -G 传递给 nvcc 编译器,您可能能够解决这个问题。我不知道该怎么做,但我认为应该很容易在某个地方找到答案。使用-G 开关,您的代码可能会运行得更慢。

标签: cuda


【解决方案1】:

正如 Yuuta 所提到的,__syncthreads() 的行为没有在条件语句中定义。因此,有它可能/可能不会按预期工作。您可能需要重新编写代码以避免将__syncthreads() 放入您的 if 条件中。

您可以查看this answerthis paper 了解有关__syncthreads() 的更多信息。

注意它是块级屏障也很重要。您不能使用__syncthreads() 同步不同的块。块必须通过内核调用同步。

【讨论】:

  • 正如我在回复 Yuuta 时提到的,CUDA 编程指南指出(B.6 节)。 “__syncthreads() 允许在条件代码中使用,但前提是条件在整个线程块中的计算结果相同,否则代码执行可能会挂起或产生意外的副作用。”上面的示例代码确实在整个块中进行相同的评估,因此没有线程分歧。这在您提到我的论文中也得到了重申,该论文侧重于线程分歧的情况。除非我误解了。
  • 我生成了一个更简单的测试用例,它重现了相同的问题,并且在条件中没有 __syncthreads() 的实例,所以我同意这不是问题。
【解决方案2】:

您的问题在于语句 TEST_FLAG=99。对于其中一个线程,它在线程 0 进入条件块之前执行,并为您提供未定义的行为。如果我注释掉 TEST_FLAG=99,代码会按预期运行。

【讨论】:

  • 我意识到如果没有TEST_FLAG=99,线程都会返回相同的值,这就是我在那里有一个测试标志的原因。它用于识别由于某种原因,并非所有线程似乎都在TEST_FLAG=99 上方的行上同步,这应该会影响所有线程进入if (TEST_FLAG == 2) 条件语句并将一个数字写入 test4 然后同步,在 TEST_FLAG 共享变量更改之前。
  • 我同意所有线程都应该进入 if(TESTFLAG==2)if (TEST_FLAG == 2) 块。但是,当我在这个块之前添加以下打印语句:printf("testflag %u %u\n", threadIdx.x, TEST_FLAG);,它表明线程 0 的 TEST_FLAG 设置为 99,而所有其他线程都设置为 2,所以最终线程 0 没有进入块。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 2012-07-14
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-29
相关资源
最近更新 更多