基于C++与CUDA的N卡GPU并行程序——在python中使用pyCUDA编写GPU程序

  官网文档

简单示例

导入包
import pycuda.autoinit
import pycuda.driver as drv
import numpy
from pycuda.compiler import SourceModule
初始化数据变量
a = numpy.random.randn(400).astype(numpy.float32)
b = numpy.random.randn(400).astype(numpy.float32)
dest = numpy.zeros_like(a)
编写GPU内核函数
mod = SourceModule("""
__global__ void multiply_them(float *dest, float *a, float *b)
{
  const int i = threadIdx.x;
  dest[i] = a[i] * b[i];
}
""")
调用GPU内核函数
multiply_them = mod.get_function("multiply_them")
multiply_them(
        drv.Out(dest), drv.In(a), drv.In(b),
        block=(400,1,1), grid=(1,1))

其中的block=(400,1,1)和grid=(1,1)表示设定线程块和线程网格.

复制数据

drv.Out()表示从GPU设备复制数据到主机端,drv.In()表示从主机端复制数据到GU设备,除此外还有函数drv.InOut(),drv.to_device(),drv.from_device().也有另外的数据复制函数drv.memcpy_htod()

import numpy
a = numpy.random.randn(4,4)
a = a.astype(numpy.float32)
a_gpu = cuda.mem_alloc(a.nbytes)
cuda.memcpy_htod(a_gpu, a)

上述过程也可以简化成

import pycuda.gpuarray as gpuarray
a_gpu = gpuarray.to_gpu(numpy.random.randn(4,4).astype(numpy.float32))

CUDA结构体

固定长度

  首先在C代码中定义CUDA结构体,如果结构体中的数据是固定长度的,比如就只有几个标量,那么可以直接定义C结构体并定义数据变量,如下所示

struct Light{//单点光源
    vec center, color;int num;
    Light(){}
    void setLight(const vec &m_center, const vec &m_color){center = m_center, color = m_color;}
};
struct DataBlock{
    Light *light, *cpu_light;
};
int main(){
    DataBlock data;
    //创建CPU主机端的结构体数组
    data.cpu_light = new Light[lightNum];//坐标x,y,z,颜色值r,g,b
    //设置CPU主机端的结构体数据变量
    data.cpu_light[lightNum].setLight(vec(inputData[0],inputData[1],inputData[2]), 
                vec(inputData[3],inputData[4],inputData[5]));
    //开辟GPU设备端结构体数组的内存
    HANDLE_ERROR( cudaMalloc( (void**)&data.light, sizeof(Light)*lightNum ) );
    //将CPU主机端数据复制到GPU设备端
    HANDLE_ERROR( cudaMemcpy( data.light, data.cpu_light, sizeof(Light)*lightNum, cudaMemcpyHostToDevice ) );
}
可变长度

  如果,结构体中的数据不是固定长度的,比如要存储图片,但是不同的图片的大小和分辨率都是不一样的,所以数据长度也都不同,这时就不能这样简单定义结构体了.而是在结构体中定义一个指针,然后使用这个固定长度的指针去开辟数据长度会变化的内存,代码如下所示

struct Image{
    float *imageData, *cpu_imageData;int width, height;int num;
    Image(){}
    ~Image(){cudaFree(imageData);}
    void setImage(const char *fileName){
        unsigned error, w, h;
        unsigned char *imageRead;
        error = lodepng_decode32_file(&imageRead, &w, &h, fileName);
        if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
        width = w, height = h;
        cpu_imageData = (float *)malloc(sizeof(float) * w * h * 4);
        HANDLE_ERROR( cudaMalloc((void**)&imageData, sizeof(float) * w * h * 4) );
        //复制转换图像数据
        for(int i = 0; i < w * h * 4; i++){
            cpu_imageData[i] = (float)(imageRead[i]) / 255;}
        HANDLE_ERROR( cudaMemcpy( imageData, cpu_imageData, sizeof(float)*w*h*4, cudaMemcpyHostToDevice ) );
        free(imageRead);
        free(cpu_imageData);
    }
};
struct DataBlock{
    Image *image, *cpu_image;
}
int main(){
    DataBlock data;
    //创建CPU主机端的结构体数组
    data.cpu_image = new Image[imageNum];//图片
    //设置CPU主机端的结构体数据变量
    data.cpu_image[imageNum].setImage(str);
    //开辟GPU设备端结构体数组的内存
    HANDLE_ERROR( cudaMalloc( (void**)&data.image, sizeof(Image)*imageNum ) );
    //将CPU主机端数据复制到GPU设备端
    HANDLE_ERROR( cudaMemcpy( data.image, data.cpu_image,sizeof(Image)*imageNum, cudaMemcpyHostToDevice ) );
}

pyCUDA结构体

  对一个数据长度可变的数组进行元素值翻倍,首先定义数组元素翻倍的内核函数

mod = SourceModule("""
    struct DoubleOperation {
        int datalen, __padding; // so 64-bit ptrs can be aligned
        float *ptr;
    };

    __global__ void double_array(DoubleOperation *a) {
        a = &a[blockIdx.x];
        for (int idx = threadIdx.x; idx < a->datalen; idx += blockDim.x) {
            a->ptr[idx] *= 2;
        }
    }
    """)

接着需要在python中定义两个结构体对象,每个对象又有可变长度的数组.

class DoubleOpStruct:
    mem_size = 8 + numpy.intp(0).nbytes
    def __init__(self, array, struct_arr_ptr):
        self.data = cuda.to_device(array)
        self.shape, self.dtype = array.shape, array.dtype
        cuda.memcpy_htod(int(struct_arr_ptr), numpy.getbuffer(numpy.int32(array.size)))
        cuda.memcpy_htod(int(struct_arr_ptr) + 8, numpy.getbuffer(numpy.intp(int(self.data))))
    def __str__(self):
        return str(cuda.from_device(self.data, self.shape, self.dtype))
#定义两个结构体,分配内存和指针
struct_arr = cuda.mem_alloc(2 * DoubleOpStruct.mem_size)
do2_ptr = int(struct_arr) + DoubleOpStruct.mem_size
#分别定义两个结构体对象的可变长度的数组
array1 = DoubleOpStruct(numpy.array([1, 2, 3], dtype=numpy.float32), struct_arr)
array2 = DoubleOpStruct(numpy.array([0, 4], dtype=numpy.float32), do2_ptr)

最后,调用数组元素翻倍内核函数

#对两个数组都进行翻倍
func = mod.get_function("double_array")
func(struct_arr, block = (32, 1, 1), grid=(2, 1))
print("doubled arrays", array1, array2)
#只对第二个数组进行翻倍
func(numpy.intp(do2_ptr), block = (32, 1, 1), grid=(1, 1))
print("doubled second only", array1, array2, "\n")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值