基本CUDA代码的奇怪行为。

时间:2011-11-27 23:44:11

标签: c cuda

我无法理解以下简单CUDA代码的输出。代码所做的就是分配两个整数数组:一个在主机上,一个在设备上,每个都是16号。然后将设备数组元素设置为整数值3,然后将这些值复制到host_array中,其中所有元素都是然后打印出来。

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
  int num_elements = 16;
  int num_bytes = num_elements * sizeof(int);

  int *device_array = 0;
  int *host_array = 0;

  // malloc host memory
  host_array = (int*)malloc(num_bytes);

  // cudaMalloc device memory
  cudaMalloc((void**)&device_array, num_bytes);

  // Constant out the device array with cudaMemset
  cudaMemset(device_array, 3, num_bytes);

  // copy the contents of the device array to the host
  cudaMemcpy(host_array, device_array, num_bytes, cudaMemcpyDeviceToHost);

  // print out the result element by element
  for(int i = 0; i < num_elements; ++i)
    printf("%i\n", *(host_array+i));

  // use free to deallocate the host array
  free(host_array);

  // use cudaFree to deallocate the device array
  cudaFree(device_array);

  return 0;
}

该程序的输出是50529027逐行打印16次。

50529027
50529027
50529027
..
..
..
50529027
50529027

这个号码来自哪里?当我在cudaMemset调用中将0替换为0时,我得到了正确的行为。即 0行逐行打印16次。

我使用CUDA 4.0在Ubuntu 10.10上用nvcc test.cu编译了代码

4 个答案:

答案 0 :(得分:7)

我不是cuda专家,但50529027是十六进制的0x03030303。这意味着cudaMemset会将数组中的每个byte设置为3,而不是每个int。鉴于cuda memset的签名(传递要设置的字节数)和memset操作的一般语义,这并不奇怪。

编辑:至于你(我猜)如何实现你想要的隐含问题,我认为你必须编写一个循环并初始化每个数组元素。

答案 1 :(得分:4)

正如其他人所指出的,cudaMeset的工作方式与标准C memset类似 - 它设置了字节值。来自CUDA文档:

cudaError_t cudaMemset( void * devPtr, int value, size_t count)
  

填充devPtr指向的内存区域的第一个计数字节   使用常量字节值 value

如果要设置字大小值,最好的解决方案是使用自己的memset内核,可能是这样的:

template<typename T>
__global__ void myMemset(T * x, T value, size_t count )
{
    size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
    size_t stride = blockDim.x * gridDim.x;

    for(int i=tid; i<count; i+=stride) {
        x[i] = value;
    }
}

可以使用足够的块来启动,以覆盖GPU中MP的数量,并且每个线程将根据需要执行尽可能多的迭代来填充内存分配。写入将被合并,因此性能不应太差。如果你愿意的话,这也可以适应CUDA的矢量类型。

答案 2 :(得分:1)

memset设置字节,整数是4个字节..所以你得到的是50529027十进制,这是十六进制的0x3030303 ...换句话说 - 你使用它错了,它无关与CUDA。

答案 3 :(得分:1)

这是一个经典的memset缺点;它仅适用于8位大小的数据类型,即 char 。这意味着它设置(可能)3到总内存的每8位。您可以通过一个简单的C ++代码来确认:

int main ()  
{    
    int x=16;
    size_t bytes = x*sizeof(int);

    int *M = (int*)malloc(bytes);
    memset(M,3,bytes);


    for (int i = 0; i < x; ++i) { 
        printf("%d\n", M[i]); 
    }    

    return 0;
}

memset适用于所有数据类型的唯一情况是将其设置为0.(它将每个字节设置为0,因此将所有数据设置为0)。如果您将数据类型更改为 char ,您将看到所需的输出。 cudaMemset是memset的ditto副本,唯一的区别是它在输入中需要一个GPU指针。

因此memset或cudaMemset可能会将每个字节设置为由第三个参数定义的整个内存空间的整数值(在您的情况下为3),无论数据类型如何。

提示

Google: 50529027二进制,您将得到答案:)