随着信息处理的爆炸增长,传统使用CPU计算已经无法满足计算作业增长的需求,GPU的出现为批量作业提供了新的契机。GPU计算拥有很类库,比如CUDA、OpenCL等,但是可以发现CUDA是其中相对比较成熟的一个,也是目前应用最为广发的一种。于此同时开发语言的飞速发展,呈现百花齐放状态,C#语言简单高效,所以本文讲述如何使用C#调用CUDA进行GPU计算。

前言

最近几年计算机有了巨大发展,各种开发语言百花齐放,但是笔者还是钟爱C#语言,尤其是目前微软的生态开放,.NET 技术的跨平台加上方便高效的Visual Studio IDE,使得C#语言的占比不断增加。

【B站小飞机】C#调用CUDA实现GPU计算

本文使用的是C#语言调用C++开发的CUDA类库实现GPU计算,基本条件如下:

选项 内容
操作系统 Windows 10
CUDA版本 10.1
Visual Studio 2019 Enterprise

上述的环境是笔者的环境,如有异同按实际条件。

方法论

本小结介绍使用C++语言开发CUDA程序的共享链接库的方法以及如何使用C#语言调用CUDA类库。

创建C++类库

首先打开Visual Studio,然后创建C++的动态链接库项目,注意一定是动态链接库,请勿使用静态链接库,可能会有错误。

项目创建完毕之后可以看到下面的界面:

创建一个和外界交互的cudaApi.h文件、两个和CUDA相关的文件CudaKernel.cuhCudaKernel.cu,当然可以缺省为一个文件,但是本着约定大于配置的原则,建议使用两个文件,一个是CUDA的源码文件一个是CUDA的头文件,在头文件中声明函数,源码文件对函数进行实现,之后效果如下:

然后配置一下工程,需要注意的是建议全局是x64环境,必须保证C++的动态链接库项目和C#的项目是同一种类型,一般FX(.NET Framework)默认的是Any CPU,不要使用这个选项,直接指定x64或者x86。

右键选中项目,【生成依赖】 --> 【自定义生成】 --> 【勾选CUDA10】(当然不排除你的事CUDA9或者CUDA8)

然后选择两个CUDA的核文件,选择属性,选中CUDA C/C++

最后选中项目右键,找到【连接器】--> 【输入】,在类库中输入cudart.lib,点击确定。

作为初期实验,建议先复制我的代码,先理解这个流程之后再自己自定义创建CUDA类库。

  1. //cudaApi.h
  2. #pragma once
  3. #define CUDADD_API __declspec(dllexport)
  4. extern "C" {
  5. //CUDA API
  6. CUDADD_API int arrayAdd(int* a, int* b, int* c, int size);
  7. }
  1. //CudaKernel.cuh
  2. #include "cudaApi.h"
  3. #include "cuda_runtime.h"
  4. #include "device_launch_parameters.h"
  5. __global__ void addKernel(int* c, const int* a, const int* b);
  6. CUDADD_API int arrayAdd(int* a, int* b, int* c, int size);

CUDA核心实现代码\(^{[1]}\):

  1. //CudaKernel.cu
  2. #include "CudaKernel.cuh"
  3. __global__ void addKernel(int* c, const int* a, const int* b)
  4. {
  5. int i = threadIdx.x;
  6. c[i] = a[i] + b[i];
  7. }
  8. CUDADD_API int arrayAdd(int c[], int a[], int b[], int size)
  9. {
  10. int result = -1;
  11. int* dev_a = 0;
  12. int* dev_b = 0;
  13. int* dev_c = 0;
  14. cudaError_t cudaStatus;
  15. cudaStatus = cudaSetDevice(0);
  16. if (cudaStatus != cudaSuccess) {
  17. result = 1;
  18. goto Error;
  19. }
  20. cudaStatus = cudaMalloc((void**)& dev_c, size * sizeof(int));
  21. if (cudaStatus != cudaSuccess) {
  22. result = 2;
  23. goto Error;
  24. }
  25. cudaStatus = cudaMalloc((void**)& dev_a, size * sizeof(int));
  26. if (cudaStatus != cudaSuccess) {
  27. result = 3;
  28. goto Error;
  29. }
  30. cudaStatus = cudaMalloc((void**)& dev_b, size * sizeof(int));
  31. if (cudaStatus != cudaSuccess) {
  32. result = 4;
  33. goto Error;
  34. }
  35. cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
  36. if (cudaStatus != cudaSuccess) {
  37. result = 5;
  38. goto Error;
  39. }
  40. cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
  41. if (cudaStatus != cudaSuccess) {
  42. result = 6;
  43. goto Error;
  44. }
  45. addKernel << <10, size >> > (dev_c, dev_a, dev_b);
  46. cudaStatus = cudaDeviceSynchronize();
  47. if (cudaStatus != cudaSuccess) {
  48. result = 7;
  49. goto Error;
  50. }
  51. cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
  52. if (cudaStatus != cudaSuccess) {
  53. result = 8;
  54. goto Error;
  55. }
  56. result = 0;
  57. cudaStatus = cudaDeviceReset();
  58. if (cudaStatus != cudaSuccess) {
  59. return 9;
  60. }
  61. Error:
  62. cudaFree(dev_c);
  63. cudaFree(dev_a);
  64. cudaFree(dev_b);
  65. return result;
  66. }

重新生成程序如果不报错就可以成功生成一个CUDA的DLL动态链接库。

到此为止CUDA的动态链接库编写完成,开始编写C#程序,本程序使用的是.NET Core 2.2,如果你的电脑没有此环境可以自己创建一个C#的控制台项目,代码均相同。

创建C#类库

创建C#项目不再赘述,直接创建控制台项目即可完成测试,下面提供一个我的C#的一个类作为参考:

  1. //CudaRunner.cs
  2. class CudaRunner
  3. {
  4. public void Run()
  5. {
  6. var a = new int[] { 1, 2, 3, 45, 456, 454, 1, 4, 65, 32, 456, 1, 56, 32, 512, 3, 5416, 86, 54, 4236, 12, 113, 321 };
  7. var b = new int[] { 1, 2, 3, 45, 456, 454, 1, 4, 65, 32, 456, 1, 56, 32, 512, 3, 5416, 86, 54, 4236, 12, 113, 321 };
  8. var c = new int[a.Length];
  9. arrayAdd(c, a, b, a.Length);
  10. for (int i = 0; i < c.Length; i++)
  11. {
  12. Console.WriteLine("{0} + {1} = {2}", a[i], b[i], c[i]);
  13. }
  14. }
  15. //.NET Framework 可以相对路径,.NET Core 就需要用绝对路径,或者自己配置
  16. [DllImport(@"C:\Users\muxuan\source\repos\CudaSharpDemo\x64\Debug\CudaSharp.dll", CallingConvention = CallingConvention.StdCall)]
  17. public static extern int arrayAdd(int[] c, int[] a, int[] b, int size);
  18. }

然后在控制台的Program.cs中调用一下即可,再次强调,这里不要使用Any CPU!!!

实验环节

实验环节很简单,如果你是用的我的程序可以直接运行查看效果,这是一个矩阵的加法。

结论与总结

到上面的实验环节基本程序部分已经结束,但是你可能会发现使用GPU计算这些数据的速度并没有CPU的快速,原因在哪呢?归结一下两个原因\(^{[2]}\):

  1. 计算量太小,不够复杂。GPU的优势是核心较多,每一个核心的计算时间越长,整体的时间差异就会越明显,比如每一个核心计算数耗时是1ms,有1000个计算大概需要耗时1s左右,但是对于GPU(1060)超过1000个CUDA核心时,将在1ms内计算完毕,CPU的理论时间消耗是GPU的1000倍左右。

  2. GPU设备初始化。初始化时间主要来自几个部分:设备初始化、数据拷贝,其中数据初始化耗时是不可控的,属于硬件底层,数据拷贝是指从CPU拷贝数据到GPU计算数据,然后GPU将数据重新拷贝到CPU的过程,这个过程耗时可控也是主导部分。

参考文献

[1] C与CUDA混合编程的配置问题

[2] 新手问下关于CUDA效率的问题

附录

源码:蓝奏云CSDN

C#使用CUDA的更多相关文章

  1. CUDA[2] Hello,World

    Section 0:Hello,World 这次我们亲自尝试一下如何用粗(CU)大(DA)写程序 CUDA最新版本是7.5,然而即使是最新版本也不兼容VS2015 ...推荐使用VS2012 进入VS ...

  2. CUDA[1] Introductory

    Section 0 :Induction of CUDA CUDA是啥?CUDA®: A General-Purpose Parallel Computing Platform and Program ...

  3. Couldn't open CUDA library cublas64_80.dll etc. tensorflow-gpu on windows

    I c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\stream_executor\dso_load ...

  4. ubuntu 16.04 + N驱动安装 +CUDA+Qt5 + opencv

    Nvidia driver installation(after download XX.run installation file) 1. ctrl+Alt+F1   //go to virtual ...

  5. 手把手教你搭建深度学习平台——避坑安装theano+CUDA

    python有多混乱我就不多说了.这个混论不仅是指整个python市场混乱,更混乱的还有python的各种附加依赖包.为了一劳永逸解决python的各种依赖包对深度学习造成的影响,本文中采用pytho ...

  6. [CUDA] CUDA to DL

    又是一枚祖国的骚年,阅览做做笔记:http://www.cnblogs.com/neopenx/p/4643705.html 这里只是一些基础知识.帮助理解DL tool的实现. “这也是深度学习带来 ...

  7. 基于Ubuntu14.04系统的nvidia tesla K40驱动和cuda 7.5安装笔记

    基于Ubuntu14.04系统的nvidia tesla K40驱动和cuda 7.5安装笔记 飞翔的蜘蛛人 注1:本人新手,文章中不准确的地方,欢迎批评指正 注2:知识储备应达到Linux入门级水平 ...

  8. CUDA程序设计(一)

    为什么需要GPU 几年前我启动并主导了一个项目,当时还在谷歌,这个项目叫谷歌大脑.该项目利用谷歌的计算基础设施来构建神经网络. 规模大概比之前的神经网络扩大了一百倍,我们的方法是用约一千台电脑.这确实 ...

  9. 使用 CUDA范例精解通用GPU编程 配套程序的方法

    用vs新建一个cuda的项目,然后将系统自动生成的那个.cu里头的内容,除了头文件引用外,全部替代成先有代码的内容. 然后程序就能跑了. 因为新建的是cuda的项目,所以所有的头文件和库的引用系统都会 ...

  10. CUDA代码移植

    如果CUDA的代码移植,一个是要 include文件夹对不对,这个是.h文件能否找到的关键,另一个就是lib,这个是.lib文件能否找到的关键.具体检查地方,见下头. include: lib:

随机推荐

  1. FireFox版本大于36.0,Yslow不能正常使用解决方案

    Yslow暂时不支持firefox 36.0及以上版本你可以使用它的书签版本.访问这里 http://yslow.org/mobile/把页面最后的那个 Desktop Bookmarklet Ins ...

  2. mouseenter([[data],fn])

    mouseenter([[data],fn]) 概述 当鼠标指针穿过元素时,会发生 mouseenter 事件.该事件大多数时候会与mouseleave 事件一起使用.广州大理石机械构件 与 mous ...

  3. 002_Python基础学习网站

    (一)电脑端:Python 基础教程 (二)手机端:Python 基础教程

  4. Celery和Flask

    第一章:Celery 第二章:Flask登录 第三章:flask简介 第四章:flask应用启动流程 第五章:路由第六章:上下文 第七章:请求 第八章:响应 第九章:session

  5. 【概率论】3-7:多变量分布(Multivariate Distributions Part II)

    title: [概率论]3-7:多变量分布(Multivariate Distributions Part II) categories: Mathematic Probability keyword ...

  6. 爬虫之操作excel

    几种常用模块的使用方法 注释:Excel 2003 即XLS文件有大小限制即65536行256列,所以不支持大文件,而Excel 2007以上即XLSX文件的限制则为1048576行16384列 下面 ...

  7. Jmeter linux 运行

    一.在Linux服务器先安装sdk 1.先从客户端下载jdk1.8.0_144.tar.gz,再上传到服务器 2.解压:tar -xzf jdk1.8.0_144.tar.gz,生成文件夹 jdk1. ...

  8. 二十、网络ifconfig 、ip 、netstat、ss之二

    ip 网络层协议 ip地址 点分十进制分为4段,范围 0-255 ip分类 A 占据1段,最左侧一段第一位固定为0 0 000 0000 - 0 111 1111  0 - 127:其中0为网络,12 ...

  9. JavaWeb_(Spring框架)认识Spring中的aop

    1.aop思想介绍(面向切面编程):将纵向重复代码,横向抽取解决,简称:横切 2.Spring中的aop:无需我们自己写动态代理的代码,spring可以将容器中管理对象生成动态代理对象,前提是我们对他 ...

  10. Ocelot 网关 和 consul 服务发现

    服务发现 Consul 一.安装和启动 下载 [Consul](https://www.consul.io/downloads.html) 下载完成后,解压,只有一个consul.exe,把目录添加到 ...