分享
  1. 首页
  2. 文章

CUDA 及其 golang 调用 - 从入门到放弃 - 1.初见

Platanuses · · 6873 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

环境:

  • NVIDIA GeForce GTX 1050
  • cuda 10.2.89 windows
  • visual studio 2017
  • windows SDK 10.0.14393.0
  • go 1.13.4 windows/amd64

我们在文件 lib.cu 中实现一个 GPU 计算的浮点数向量内积函数,以及一个 CPU 的入口函数进行数据传递和调用:

__global__ void devDot(double *x, double *y, int n, double *r) {
 double res = 0.0;
 for (int i = 0; i < n; i++) {
 res += x[i] * y[i];
 }
 *r = res;
}
extern "C" __declspec(dllexport) void dot(double *x, double *y, int n, double *r) {
 double *xd, *yd, *rd;
 int sz = sizeof(double) * n;
 cudaMalloc(&xd, sz);
 cudaMalloc(&yd, sz);
 cudaMalloc(&rd, sizeof(double));
 cudaMemcpy(xd, x, sz, cudaMemcpyHostToDevice);
 cudaMemcpy(yd, y, sz, cudaMemcpyHostToDevice);
 devDot<<<1, 1>>>(xd, yd, n, rd);
 cudaDeviceSynchronize();
 cudaMemcpy(r, rd, sizeof(double), cudaMemcpyDeviceToHost);
 cudaFree(xd);
 cudaFree(yd);
 cudaFree(rd);
}

文件后缀 cu 表示 C/C++ 的语法加上 CUDA 自己的一些扩展。其中,__global__ 表示该函数可运行于 GPU。由 cudaMalloc 申请的内存不能在 CPU 函数中访问,同样,在 GPU 函数中也只能访问由 cudaMalloc 一族的函数申请的内存。两边内存的数据传输使用 cudaMemcpydevDot 函数后面的 <<<1, 1>>> 是 CUDA 扩展的语法,所以只能用 CUDA 专用的编译器前端 nvcc 进行编译,其意义以后再表。

使用以下命令将代码编译为一个动态库(需要将 VC 编译器所在目录加入 PATH):

nvcc lib.cu -o libcuda.dll --shared

将 dll 文件复制到 main.go 同目录下,main.go如下:

package main
import (
 "math/rand"
 "syscall"
 "time"
 "unsafe"
)
type Lib struct {
 dll *syscall.DLL
 dotProc *syscall.Proc
}
func LoadLib() (*Lib, error) {
 l := &Lib{}
 var err error
 if l.dll, err = syscall.LoadDLL("libcuda.dll"); nil != err {
 return nil, err
 }
 if l.dotProc, err = l.dll.FindProc("dot"); nil != err {
 l.dll.Release()
 return nil, err
 }
 return l, nil
}
func (l *Lib) Release() {
 l.dll.Release()
}
func (l *Lib) Dot(x, y []float64) float64 {
 var r float64
 l.dotProc.Call(
 uintptr(unsafe.Pointer(&x[0])),
 uintptr(unsafe.Pointer(&y[0])),
 uintptr(len(x)),
 uintptr(unsafe.Pointer(&r)),
 )
 return r
}
const N = 1 << 20
func main() {
 lib, err := LoadLib()
 if nil != err {
 println(err.Error())
 return
 }
 defer lib.Release()
 rand.Seed(time.Now().Unix())
 x, y := make([]float64, N), make([]float64, N)
 for i := 0; i < N; i++ {
 x[i], y[i] = rand.Float64(), rand.Float64()
 }
 var r float64
 for i := 0; i < N; i++ {
 r += x[i] * y[i]
 }
 println(r)
 println(lib.Dot(x, y))
 t := time.Now()
 for i := 0; i < 100; i++ {
 var r float64
 for i := 0; i < N; i++ {
 r += x[i] * y[i]
 }
 }
 println(time.Now().Sub(t).Microseconds())
 t = time.Now()
 for i := 0; i < 100; i++ {
 lib.Dot(x, y)
 }
 println(time.Now().Sub(t).Microseconds())
}

在 golang 中使用动态加载,比较计算结果和运行时间,运行的结果基本是同样的画风,计算结果正确,但是在性能上,这种哈喽级别的 CUDA 尝试终究惨败被虐出 N 个数量级,浮点数计算的性能优势在其他性能损耗面前完全入不敷出。这就是所谓的从入门到放弃......吗?


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Platanuses

查看原文:CUDA 及其 golang 调用 - 从入门到放弃 - 1.初见

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
6873 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏