为什么从__global__函数中引用外部存储器会搞砸一切?

时间:2012-05-30 05:32:42

标签: c++ memory cuda gpu nvidia

我正在用CUDA编写一些代码(确切地说是霍夫曼算法,但它与案例完全无关)。我有一个文件 Paralellel.cu 有两个函数:一个( WriteDictionary )是普通函数,第二个( wrtDict )是一个在CUDA GPU中运行的特殊CUDA _ 全局 _ 功能。以下是这些功能的主体:

//I know body of this function looks kinda not-related 
//   to program main topic, but it's just for tests.
__global__ void wrtDict(Node** nodes, unsigned char* str)
{
    int i = threadIdx.x;

    Node* n = nodes[i];
    char c = n->character;

    str[6 * i] = 1;//c;                         !!!
    str[6 * i + 1] = 2;

    str[6 * i + 2] = 0;
    str[6 * i + 3] = 0;
    str[6 * i + 4] = 0;
    str[6 * i + 5] = 0;
}

我知道这两个第一行似乎毫无意义,因为我在这里不使用 Node 类的 n 这个对象,但只是让它们暂时使用。还有一个标有“!!!”的超级秘密评论。这是 WriteDictionary

void WriteDictionary(NodeList* nodeList, unsigned char* str)
{
    Node** nodes = nodeList->elements;   
    int N = nodeList->getCount();

    Node** cudaNodes;
    unsigned char* cudaStr;

    cudaMalloc((void**)&cudaStr, 6 * N * sizeof(unsigned char));
    cudaMalloc((void**)&cudaNodes, N * sizeof(Node*));

    cudaMemcpy(cudaStr, str, 6 * N * sizeof(char), cudaMemcpyHostToDevice); 
    cudaMemcpy(cudaNodes, nodes, N * sizeof(Node*), cudaMemcpyHostToDevice);

    dim3 block(1);
    dim3 thread(N);

    std::cout << N << "\n";

    wrtDict<<<block,thread>>>(cudaNodes, cudaStr);

    cudaMemcpy(str, cudaStr, 6 * N * sizeof(unsigned char), cudaMemcpyDeviceToHost);


    cudaFree(cudaNodes);
    cudaFree(cudaStr);
}

可以看出,函数 WriteDictionary 是CUDA与程序其余部分之间的代理。我的类中有一堆对象 Node 位于我的对象中的 Node * array 元素所指向的普通内存中的某个地方节点列表即可。目前,它足以了解 Node ,它有一个公共字段 char 字符。现在, char * str 将填充一些测试数据。它包含6 * N 为字符分配的内存,其中 N = 元素数组中所有元素的计数。所以我在CUDA中为6 * N chars N Node 指针分配了一个内存空间。然后我在那里复制我的 Node 指针,它们仍然指向一个普通的内存。我在运行这个功能。在函数 wrtDict 中,我将字符提取到 char c 变量中,这次没有尝试将其放入输出数组 str < / em>的

所以,当我写一个输出数组的内容 str (在 WriteDictionary 函数之外)时,我得到了完全正确的答案,即:

1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0

是的,在这里我们有39个正确的六个字符(以十六进制显示)。但是当我们在 wrtDict 函数中略微更改我们的超级机密评论时,就像这样:

__global__ void wrtDict(Node** nodes, unsigned char* str)
{
    int i = threadIdx.x;

    Node* n = nodes[i];
    char c = n->character;

    str[6 * i] = c;//1;                         !!!
    str[6 * i + 1] = 2;

    str[6 * i + 2] = 0;
    str[6 * i + 3] = 0;
    str[6 * i + 4] = 0;
    str[6 * i + 5] = 0;
}
我们会看到奇怪的事情。我现在期望每六个中的第一个 char 是数组指向的 Node 中的一个字符 - 每个字符都不同。或者,即使它失败了,我也希望每六个中只有第一个 char 被搞砸,但其余部分完好无损: 2 0 0 0 0 。但不是!当我这样做时,一切都会变得混乱,现在输出数组 str 的内容如下所示:

70 21 67 b7 70 21  67 b7  0  0  0  0 
 0  0  0  0 18 d7  85  8 b8 d7 85  8 
78 d7 85  8 38 d9  85  8 d8 d7 85  8 
f8 d5 85  8 58 d6  85  8 d8 d5 85  8 
78 d6 85  8 b8 d6  85  8 98 d7 85  8 
98 d6 85  8 38 d6  85  8 d8 d6 85  8 
38 d5 85  8 18 d6  85  8 f8 d6 85  8 
58 d9 85  8 f8 d7  85  8 78 d9 85  8 
98 d9 85  8 d8 d4  85  8 b8 d8 85  8 
38 d8 85  8 38 d7  85  8 78 d8 85  8 
f8 d8 85  8 d8 d8  85  8 18 d5 85  8 
61 20 75 6c 74 72  69 63 65 73 20 6d 
6f 6c 65 73 74 69  65 20 73 69 74 20 
61 6d 65 74 20 69  64 20 73 61 70 69 
65 6e 2e 20 4d 61  75 72 69 73 20 73 
61 70 69 65 6e 20  65 73 74 2c 20 64 
69 67 6e 69 73 73  69 6d 20 61 63 20 
70 6f 72 74 61 20  75 74 2c 20 76 75 
6c 70 75 74 61 74  65 20 61 63 20 61 
6e 74 65 2e 20 46 

我现在问 - 为什么?是因为我试图从CUDA GPU中获得普通内存吗?我正在收到一个警告,可能正是这个案子,说:

Cannot tell what pointer points to, assuming global memory space

我已经搜索了这个,只发现了这个,CUDA它正在达到一个普通的记忆,因为无法找到到达的地方,并且应该忽略99.99%的这个警告。所以我忽略它,认为它会没事,但事实并非如此 - 我的情况是0.01%吗?

我该如何解决这个问题?我知道我可以将 Nodes 复制到CUDA,而不是指向它们,但是我认为复制它们会花费我更多的时间,而不是保留对它们内部所做的事情的保护。我还可以从每个节点中提取字符,将它们全部放入数组中然后将其复制到CUDA,但是 - 与前一个语句中的问题相同。

我完全不知道该怎么做,更糟糕的是,我学院的CUDA项目截止日期是今天,apx。下午17点(我只是没有足够的时间早点去做,该死的......)。

PS。如果它有帮助:我正在编译使用非常简单(没有任何开关)命令:

nvcc -o huff ArchiveManager.cpp IOManager.cpp Node.cpp NodeList.cpp Program.cpp Paraleller.cu

2 个答案:

答案 0 :(得分:4)

这是一个可怕的问题,请参阅talonmies的评论。

  1. 检查每个 CUDA API调用的错误值。启动内核后,您将在cudaMemcpy上收到启动失败消息
  2. 运行cuda-memcheck以帮助调试错误(基本上是分段错误)
  3. 意识到您要从GPU取消引用(未映射)指针到主机内存中,您需要复制节点,而不仅仅是指向节点的指针

答案 1 :(得分:1)

您也可以从cuda-gdb内部运行程序。 cuda-gdb会告诉你你遇到了什么错误。另外,在cuda-gdb的开头,执行“set cuda memcheck on”,它将在cuda-gdb中打开memcheck。

在最新的cuda-gdb版本(截至今天为5.0)中,如果您没有检查API调用的返回码并且这些API调用失败,您还可以看到警告。