美文网首页读书谈技术
使用 Go 和 C 的 CUDA 无压力编程

使用 Go 和 C 的 CUDA 无压力编程

作者: 技术的游戏 | 来源:发表于2022-11-18 17:21 被阅读0次
image.png

关于 CUDA

NVIDIAAMD等图形芯片制造商的图形处理器 (GPU) 销量一直在飙升,这主要归功于加密货币矿工和机器学习应用程序,它们在游戏和模拟之外发现了这些图形处理器的用途。这主要是因为 GPU 提供了通用处理器所没有的并行能力,而这些能力恰好非常适合大规模哈希和矩阵计算等操作,这些操作是挖掘和机器学习工作负载的基础。

NVIDIA 的CUDA为可用于数值计算的图形处理器提供了大规模并行架构。典型的通用 Intel 处理器可能有 4 个或 8 个内核,而 NVIDIA GPU 可能有数千个 CUDA 内核和一个支持在数千个线程上并行处理的管道,从而大大加快处理速度。这可用于大大减少机器学习应用程序中的训练时间,从而增加在调整模型时可以运行的实验和迭代次数。

image.png

CUDA 和并行处理的挑战之一是它需要使用专门的技术和技能,因此它的使用受到限制。近年来,这个领域变得越来越民主化,编程技能有限的人也可以更容易地使用该技术来获取它并优化他们的应用程序,以利用 GPU 提供的大规模并行能力。

Go 和 CUDA

使用 GoCUDA进行编程比使用其他语言要复杂一些。尽管有一些优秀的软件包,例如 mumax,但文档很差,缺少示例并且难以使用。

CUDA 适用于 C,因此最好的选择是使用 Command cgo 并使用您的 Cuda Kernel 调用外部函数 。这就是我将在此示例中执行的操作,其中我使用 CUDA将两个矩阵相乘。

Kernel

这里有一个 简单 的内核,它具有内核函数和一个要在外部调用的辅助函数。请注意,我使用了 extern C ,因为这是 cgo 调用函数的方式:

#include <stdio.h>
#include <cuda.h>

__global__ void vecmul(float *A, float* B, float *C, int size)
{
    // Row and Column indexes: 
    int row = blockIdx.y*blockDim.y+threadIdx.y;
    int col = blockIdx.x*blockDim.x+threadIdx.x;

    // Are they bellow the maximum?
    if (col < size && row < size) {
       float result = 0;
       for(int ix=0;ix<size;ix++) {
          result += A[row*size+ix]*B[ix*size+col];
       }
       C[row*size+col] = result;
    }
}

extern "C" {
    void maxmul(float *A, float* B, float *C, int size) {
        int total = size*size;

        // Allocate device memory:
        float* gpu_A;
        float* gpu_B;
        float* gpu_C;
        int msize = total * sizeof(float);
        cudaMalloc((void**)&gpu_A, msize);
        cudaMemcpy(gpu_A,A,msize,cudaMemcpyHostToDevice);
        cudaMalloc((void**)&gpu_B, msize);
        cudaMemcpy(gpu_B,B,msize,cudaMemcpyHostToDevice);
        cudaMalloc((void**)&gpu_C,msize);

        // Blocks & grids:
        dim3 blocks(size,size);
        dim3 grid(1,1);

        // Call the kernel:
        vecmul<<<grid,blocks>>>(gpu_A,gpu_B,gpu_C,size);

        // Get the result Matrix:
        cudaMemcpy(C,gpu_C,msize,cudaMemcpyDeviceToHost);

        //Free device matrices
        cudaFree(gpu_A);
        cudaFree(gpu_B);
        cudaFree(gpu_C);
    }
}

vecmul() 函数是内核,而 maxmul() 函数是助手。它的作用是在 GPU 中分配内存,复制参数,调用内核,复制结果。值通过引用传递。

Go 代码

程序 maxmul.go 调用 辅助 函数并显示结果:

package main

/*
void maxmul(float *A, float* B, float *C, int size);
#cgo LDFLAGS: -L. -L./ -lmaxmul
*/
import "C"

import "fmt"

func Maxmul(a []C.float, b []C.float, c []C.float, size int) {
    C.maxmul(&a[0], &b[0], &c[0], C.int(size))
}

func main() {
    //in := []C.float{1.23, 4.56}
    //C.test(&in[0]) // C 1.230000 4.560000
    a := []C.float{-1,2,4,0,5,3,6,2,1}
    b := []C.float{3,0,2,3,4,5,4,7,2}
    var c []C.float = make([]C.float, 9)
    Maxmul(a,b,c,3)
    fmt.Println(c)
}

在导入 C 包之前,允许以纯 C代码 (extern C) 调用外部函数的,我传递了 cgo 的配置,指明了函数 C 的原型、lib 的路径及其名称。

我必须在 Go 代码中创建一个包装函数来调用外部函数,以使事情变得更容易。它只是将引用传递给数组(第一个位置的地址)和数组大小(在本例中为 3x3 = 9)。在 CUDA 中,我们使用 平面 矩阵。

我使用 C.float 类型创建包含我的数组的 slices(转换为向量)。 然后我调用了这个函数。 请注意,我传递了每行(或列)的大小。

编译

要编译 C 代码,请使用以下命令:

nvcc --ptxas-options=-v --compiler-options '-fPIC' -o libmaxmul.so --shared maxmul.cu

您需要安装 CUDA 和 Nvidia 驱动程序!

然后只需使用以下命令运行 Go 代码:

go run maxmul.go
...
[19 36 16 27 41 31 28 15 24]

而这就是矩阵相乘的结果!

完整的源代码在这里:https://github.com/cleuton/golang-network/tree/master/english/cuda/nostress

总结

  1. Cgo 让代码变得羞涩难懂,加大了维护成本和学习成本。
  2. 个人观点:Gpu 的使用发展还不成熟,以后会诞生新的编程语言来原生支持 或者 Go 语言充分利用 CPU 多核的性能,对于 GPU 多核的支持还不到位。
  3. 有份比较古老的文档,感兴趣的可以看下。使用 Go 进行科学 GPU 计算:https://archive.fosdem.org/2014/schedule/event/hpc_devroom_go/attachments/slides/486/export/events/attachments/hpc_devroom_go/slides/486/FOSDEM14_HPC_devroom_14_GoCUDA.pdf

Have a nice day, Happy coding.

相关文章

网友评论

    本文标题:使用 Go 和 C 的 CUDA 无压力编程

    本文链接:https://www.haomeiwen.com/subject/kctvxdtx.html