Cython与CUDA之Gather
技术背景
Cython是Python的一个超集,可以使用Pythonic的语法写出接近于C语言的性能,可以用于将Python编程过程中遇到的Bottleneck模块改写成Cython以达到加速的效果。前面写过一些关于Cython加速计算的文章。又因为Cython编译过程中会先转为C语言代码,然后再编译为动态链接库或者可执行文件,所以很自然的可以在Cython中调用C语言函数。用这种方法,还可以直接调用CUDA C函数。在这篇文章中,我们要使用Cython结合CUDA C的方法来实现一个CUDA版本的Gather函数,从一个数组中根据索引数组,输出对应的数组。相当于numpy中的result=source[index]。
接口头文件
我们定义一个cuda_index.cuh的头文件,用于指定C函数接口形式:
#include <stdio.h>
extern "C" int Gather(float *source, int *index, float *res, int N, int M);
其中source是原始数组,index是索引数组,res是结果数组,N是索引的维度,M是原始数组的维度。
异常捕获头文件
这里使用的是前面一篇CUDA异常捕获中用到的头文件error.cuh
#pragma once
#include <stdio.h>
#define CHECK(call) do{const cudaError_t error_code = call; if (error_code != cudaSuccess){printf("CUDA Error:\n"); printf(" File: %s\n", __FILE__); printf(" Line: %d\n", __LINE__); printf(" Error code: %d\n", error_code); printf(" Error text: %s\n", cudaGetErrorString(error_code)); exit(1);}} while (0)
通过这个宏,我们可以在运行CUDA核函数的时候捕获其异常。
CUDA Gather函数
CUDA实现Gather函数cuda_index.cu还是比较简单的,就是一个简单的Kernel函数再加一个管理DeviceMemory的C函数就可以了:
// nvcc -shared ./cuda_index.cu -Xcompiler -fPIC -o ./libcuindex.so
#include <stdio.h>
#include "cuda_index.cuh"
#include "error.cuh"
void __global__ GatherKernel(float *source, int *index, float *res, int N){
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < N){
res[idx] = source[index[idx]];
}
}
extern "C" int Gather(float *source, int *index, float *res, int N, int M){
float *souce_device, *res_device;
int *index_device;
CHECK(cudaMalloc((void **)&souce_device, M * sizeof(float)));
CHECK(cudaMalloc((void **)&res_device, N * sizeof(float)));
CHECK(cudaMalloc((void **)&index_device, N * sizeof(int)));
CHECK(cudaMemcpy(souce_device, source, M * sizeof(float), cudaMemcpyHostToDevice));
CHECK(cudaMemcpy(res_device, res, N * sizeof(float), cudaMemcpyHostToDevice));
CHECK(cudaMemcpy(index_device, index, N * sizeof(int), cudaMemcpyHostToDevice));
int block_size = 1024;
int grid_size = (N + block_size - 1) / block_size;
GatherKernel<<<grid_size, block_size>>>(souce_device, index_device, res_device, N);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
CHECK(cudaMemcpy(res, res_device, N * sizeof(float), cudaMemcpyDeviceToHost));
CHECK(cudaFree(souce_device));
CHECK(cudaFree(index_device));
CHECK(cudaDeviceSynchronize());
CHECK(cudaFree(res_device));
CHECK(cudaDeviceReset());
return 1;
}
Cython调用接口
假定我们有一个numpy.ndarray形式的数组需要进行索引,当然我们也可以用现成的AI框架来直接实现,例如mindspore.Tensor(numpy.ndarray)。只是这里我们用Cython来做一个直接对接CUDA函数的接口wrapper.pyx,理论上可以对数组做一些更加细致的操作。
# cythonize -i -f wrapper.pyx
import numpy as np
cimport numpy as np
cimport cython
cdef extern from "<dlfcn.h>" nogil:
void *dlopen(const char *, int)
char *dlerror()
void *dlsym(void *, const char *)
int dlclose(void *)
enum:
RTLD_LAZY
ctypedef int (*GatherFunc)(float *source, int *index, float *res, int N, int M) noexcept nogil
cdef void* handle = dlopen('/path/to/libcuindex.so', RTLD_LAZY)
@cython.boundscheck(False)
@cython.wraparound(False)
cpdef float[:] cuda_gather(float[:] x, int[:] idx):
cdef:
GatherFunc Gather
int success
int N = idx.shape[0]
int M = x.shape[0]
float[:] res = np.zeros((N, ), dtype=np.float32)
Gather = <GatherFunc>dlsym(handle, "Gather")
success = Gather(&x[0], &idx[0], &res[0], N, M)
return res
while not True:
dlclose(handle)
这里所使用到的动态链接库libcuindex.so就是编译好的CUDA模块,要使用绝对路径会比较保险。
Python调用函数
我们最上层的函数还是通过Python脚本test_gather.py来调用,借助其简洁的语法和大量的第三方接口:
import numpy as np
np.random.seed(0)
from wrapper import cuda_gather
M = 1024 * 1024 * 128
N = 1024 * 1024
x = np.random.random((M,)).astype(np.float32)
idx = np.random.randint(0, M, (N,)).astype(np.int32)
res = np.asarray(cuda_gather(x, idx))
print (res.shape)
print ((res==x[idx]).sum())
这里的wrapper就是我们的Cython文件的包名。
运行流程
在编辑好上述的这些相关文件之后,我们需要按照这样的一个流程来进行使用:首先将CUDA相关模块编译成一个动态链接库libxxx.so,然后使用Cython加载这个动态链接库,再将Cython的封装模块编译成一个动态链接库供Python调用,最后直接执行Python任务即可。相关步骤所对应的终端指令如下:
$ nvcc -shared ./cuda_index.cu -Xcompiler -fPIC -o ./libcuindex.so
$ cythonize -i -f wrapper.pyx
$ python3 test_gather.py
运行输出的结果如下:
(1048576,)
1048576
如果你使用nvitop在监测GPU资源的占用的话,运行过程中就可以看到GPU显存的一些波动。最后输出的结果跟numpy的索引函数直接对比是一致的,也就是说我们的输出结果是正确的。
报错处理
如果在运行的过程中有提示Numpy的相关lib找不到的问题,可以参考这篇文章进行处理。
总结概要
本文使用了Cython作为封装函数,封装一个CUDA C实现的Gather算子,然后通过Python去调用,用这种方法实现一个比较Pythonic的CUDA Gather函数的实现和调用。
版权声明
本文首发链接为:https://www.cnblogs.com/dechinphy/p/cycuda-gather.html
作者ID:DechinPhy
更多原著文章:https://www.cnblogs.com/dechinphy/
请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html
Cython与CUDA之Gather的更多相关文章
- 机器学习Python包
随着机器学习的逐日升温,各种相关开源包也是层出不群,面对如此多种类的工具包,该如何选择,有的甚至还知之甚少或者不知呢,本文简单汇总了一下当下使用比较多的Python版本机器学习工具包,供大家参看,还很 ...
- 从Theano到Lasagne:基于Python的深度学习的框架和库
从Theano到Lasagne:基于Python的深度学习的框架和库 摘要:最近,深度神经网络以“Deep Dreams”形式在网站中如雨后春笋般出现,或是像谷歌研究原创论文中描述的那样:Incept ...
- Faster-rcnn实现目标检测
Faster-rcnn实现目标检测 前言:本文浅谈目标检测的概念,发展过程以及RCNN系列的发展.为了实现基于Faster-RCNN算法的目标检测,初步了解了RCNN和Fast-RCNN实现目标检 ...
- Caffe + Ubuntu 14.04 64bit + 无CUDA(linux下安装caffe(无cuda)以及python接口)
安装Caffe指导书 环境: Linux 64位 显卡为Intel + AMD,非英伟达显卡 无GPU 一. 安装准备工作 1. 以管理员身份登录 在左上角点击图标,搜索terminal(即终端),以 ...
- CUDA从入门到精通
http://blog.csdn.net/augusdi/article/details/12833235 CUDA从入门到精通(零):写在前面 在老板的要求下.本博主从2012年上高性能计算课程開始 ...
- ubuntu14.04安装cuda
1 装系统时候注意,另外14.04要好于12.04,自带了无线驱动 ubuntu14.04安装完不要update 2 安装cuda和cudnn http://blog.csdn.net/l297969 ...
- Caffe + Ubuntu 15.04 + CUDA 7.0 安装以及配置
作为小码农的我,昨天就在装这个东东了,主要参考第一篇博文,但是过程发现很多问题,经过反反复复,千锤百炼,终于柳暗花明,我把这个caffe给搞定了,是故,我发布出来,后之来者,欲将有感于斯文~ 本分分为 ...
- CUDA从入门到精通 - Augusdi的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/augusdi/article/details/12833235 CUDA从入门到精通 - Augusdi的专栏 - 博客频道 - CSDN.NET CUDA ...
- 【转载】Caffe + Ubuntu 14.04 + CUDA 6.5 新手安装配置指南
洋洋洒洒一大篇,就没截图了,这几天一直在折腾这个东西,实在没办法,不想用Linux但是,为了Caffe,只能如此了,安装这些东西,遇到很多问题,每个问题都要折磨很久,大概第一次就是这样的.想想,之后应 ...
- 记一次CUDA编程任务
这个月6号开始,着手解决一个具有实际意义的计算任务.任务数据有9879896条,每条包含30个整数,任务是计算每两条数据之间的斯皮尔相关系数及其P值.原始数据只有500+MB,因此我并不认为这是个多么 ...
随机推荐
- NoSQL 述评
作为主库的 nosql 只有 CockroachDB.TiKV 以及 MongoDB(从4.0后事务似乎可用了),CockrouchDB 已经收费,另外 YugabyteDB 也可选,但大家的反馈都不 ...
- docker保存、导入、导出和加载tar及其tar.gz
一.操作tar包1.save和load命令save命令 docker save [options] images [images...]示例 : docker save -o nginx.tar ng ...
- server.error.include-message
使用的thymeleaf模板引擎,默认前端无法获取message和exception 想要在前端获取到message和exception,配置一下配置 server.error.include-exc ...
- Swagger注解-@ApiModel 和 @ApiModelProperty
@ApiModel 使用场景 在实体类上边使用,标记类时swagger的解析类 概述 提供有关swagger模型的其它信息,类将在操作中用作类型时自动内省 属性 属性名称 数据类型 默认值 说明 va ...
- vertx 获取请求参数
表单登录(GET) <form action="/login"> <input type="text" name="user ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 — Vision Kit(2)
1.问题描述: 人脸活体检测返回上一页App由沉浸式变为非沉浸式多了上下安全区域. 解决方案: 检测结束后需要自己去设置沉浸式配置. 2.问题描述: Vision Kit文字识别是本地识别,还是上传至 ...
- Python的OCR工具pytesseract解决TesseractNotFoundError: tesseract is not installed or it's not in your PATH. See README file for more information环境变量问题
pytesseract是基于Python的OCR工具, 底层使用的是Google的Tesseract-OCR 引擎,支持识别图片中的文字,支持jpeg, png, gif, bmp, tiff等图片格 ...
- Qt编写的项目作品21-网络请求客户端/服务器
一.实现原理 http请求就是tcp通信,所以第一步实例化QTcpServer类监听端口,并绑定newConnection信号槽. 一旦有新的连接,交给专门的解包类处理,将对应的数据解包,http请求 ...
- 从零开始的Python世界生活——语法基础先导篇(Python小白零基础光速入门上手)
从零开始的Python世界生活--语法基础先导篇(Python小白零基础光速入门上手) 1. 准备阶段 1.1 下载并安装Python 1.1.1 下载步骤: 访问Python官方网站:点击这里下载P ...
- 解决Playwright访问https证书问题
# 参数说明 ignore_https_errors=True 访问https地址解决安全证书 viewport={"width": 1920, "height" ...