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,因此我并不认为这是个多么 ...
随机推荐
- Flutter & Xcode15-beta 冲突
安装了Xcode15-beta后运行 Flutter 一直报有两个相同的文件冲突,这时候指定一下 Xcode-beta 的位置就好了 sudo xcode-select --switch /Appli ...
- 中电金信:GienTech动态| 获奖、合作、与伙伴共谋数字化转型…
-- -- GienTech动态 -- -- 中电金信携"源启"亮相第十二届中国电子信息博览会 4月11日,为期三天的"第十二届中国电子信息博览会" ...
- django生命周期流程图与django路由层
目录 一.django请求生命周期流程图 二.django路由层 1.路由匹配 2.转换器 3.正则匹配 不同版本的区别 正则匹配斜杠导致的区别 4.正则匹配的无名有名分组 分组匹配 无名分组 有名分 ...
- 从 $PGDATA 到文件组:深入解析 PostgreSQL 与 SQL Server 的存储策略
从 $PGDATA 到文件组:深入解析 PostgreSQL 与 SQL Server 的存储策略 在数据库领域,数据存储和管理的效率与可靠性是决定系统性能.可扩展性和易于管理的关键因素.Postgr ...
- Qt编写地图综合应用27-点聚合
一.前言 在地图上查询结果通常以标记点的形式展现,但是如果标记点较多,不仅会大大增加客户端的渲染时间,让客户端变得很卡,而且会让人产生密集恐惧症.为了解决这一问题,我们需要一种手段能在用户有限的可视区 ...
- [转]Pelco-D协议使用
1.Pelco-D协议格式如下图所示: 2. 通用示例为:水平向右控制 FF address 00 02 Hspeed 00 checksum水平向左控制 FF address 00 04 Hspee ...
- [转]v-mode 提示错误 v-model directives require the attribute value which is valid as LHS.
v-mode总是提示错误 v-model directives require the attribute value which is valid as LHS. 为什么调用过滤总是提示这个错呢? ...
- Diary -「NOI 2022」尘降
又一次,以这样一种身份来到国赛赛场.起跑线延长出赛场外,我将于此开始又一场已知"无用"的竞技. 虚无中 我的尘埃盲目漂泊摇晃 时间回到数个月前的省选,\(600\) 分的总 ...
- Diary & Solution Set - 多校度假
目录 \(\mathscr{Summary}\sim6.14\) \(\mathscr{Contest}\) \((3/3)\) \(\mathscr{A}.\) 区间第 \(k\) 小 \(\mat ...
- react 计算衍生数据
import React from 'react' import { connect } from 'react-redux' import TodoList from '../components/ ...