zoukankan      html  css  js  c++  java
  • CUDA杂谈

    这一年都在编写CUDA的程序,用了很多优化的手段,发现大部分其实还是官方的指南里面的手段

    https://docs.nvidia.com/cuda/cuda-c-best-practices-guide/
    https://developer.download.nvidia.cn/compute/cuda/1.1-Beta/x86_website/projects/reduction/doc/reduction.pdf

    至于代码,多看CUDA自带的example就好了,挺好的代码

    学会使用trust库

    后面才发现有这个库的,具体效率没看过,但是发现很多我手写的代码trust里面都有,下次可以用用。

    内存方面

    CUDA里面,用nsight一看,有大部分时间都花在cudaMalloc,cudaMemcpy这一类的函数里面,所以如何减少这些函数调用是很重要的

    减少cudaMalloc,cudaMemcpy的调用

    cudaMalloc是很耗时的一个操作,我在优化雷达数据计算的时候,一份数据用CPU算要80ms,但是用GPU算只要10,如果用GTX1080ti的话也才6ms左右,但是单单cudaMalloc就用了10几ms.特别在多线程下,这个问题特别严重
    所以不要轻易回收申请的GPU内存,能复用就复用,能实现一个简单的内存池最好

    多线程同时操作内存
    void func()
    {
    	char *p = nullptr;
    	cudaMalloc((void**)&p, 1024 * 1024 * 10);
    	cudaFree(p);
    
    }
    
    int main()
    {
    	std::thread t(func);
    	std::thread t2(func);
    	std::thread t3(func);
    	t.join();
    	t2.join();
    	t3.join();
    }
    

    上面的代码同时开启3个线程,申请了三块显存, 可以看到,每个malloc的申请都在100ms之间.我估计应该是产生竞争了

    后来写代码发现,是每个线程在第一次执行CUDA的代码的时候,会比较慢。修改了代码,在代码前面加入create stream,发现createstream比较慢,而cudaMalloc快了点,但是依然有4-10ms的消耗,所以我估计还是有一定的竞争
    而每个线程第一次调用CUDA函数会很慢是因为CUDA的每个线程需要初始化一些东西吧,找了版本也没找到相关的说明,有人说是context,但是从nsight里面看到,大部分的context都是一样的


    将代码修改成串行申请内存,可以看到内存申请只有第一个比较耗时,第二个第三个都是很快的

    void func()
    {
    	char *p = nullptr;
    	cudaMalloc((void**)&p, 1024 * 1024 * 10);
    	cudaFree(p);
    }
    
    int main()
    {
    	func();
    	func();
    	func();
    }
    

    拷贝内存

    static const int malloc_size = 1024 * 1024 * 10;
    void func()
    {
    	cudaStream_t stream;
    	cudaStreamCreate(&stream);
    
    	char *gpu_mem = nullptr;
    	char *host_mem = (char*)malloc(malloc_size);
    	cudaMalloc((void**)&gpu_mem, malloc_size);
    	cudaMemcpy(gpu_mem, host_mem, malloc_size, cudaMemcpyHostToDevice);
    	cudaMemcpy(host_mem, gpu_mem, malloc_size, cudaMemcpyDeviceToHost);
    	cudaFree(gpu_mem);
    	free(host_mem);
    }
    

    开多线程拷贝

    单线程拷贝内存

    可以看到单线程的内存拷贝比较稳定,而多线程在2-10ms之间,不知道是不是因为多线程操作内存引起的部分等待

    网上说 可以使用stream进行异步传输,于是修改代码,使用async的memcpy

    	cudaStream_t stream;
    	cudaStreamCreate(&stream);
    
    	char *gpu_mem = nullptr;
    	char *host_mem = (char*)malloc(malloc_size);
    	cudaMalloc((void**)&gpu_mem, malloc_size);
    	cudaMemcpyAsync(gpu_mem, host_mem, malloc_size, cudaMemcpyHostToDevice,stream);
    	cudaMemcpyAsync(host_mem, gpu_mem, malloc_size, cudaMemcpyDeviceToHost,stream);
    	cudaFree(gpu_mem);
    	free(host_mem);
    

    最终结果,表示没啥鸟用,速度慢还是慢,从几张图的开始时间和结束时间来看,也没有看出什么异步的优势

    pinned 内存

    申请pinned内存耗时

    拷贝pinned内存与拷贝非pinned内存对比

    从上面图中可以看出,拷贝pinn内存基本是拷贝非pinn内存速度的7-9倍左右,效率提升还是很可观的

    zero copy, shared memory

    zero copy对我来讲几乎没什么用的东西,太过频繁在CPU和GPU写和读内存反而导致速度下降得很严重,最终被放弃了

    shared memory对我来讲用处并不大,而且要修改太多的代码。我们代码里面主要时间还是花在GPU和CPU的交互,即使修改成使用shared memory最终估计也就优化几ms,没必要那么折腾

    IO优化

    IO优化主要是在三方面,知乎上的答案讲得很清楚了

    gpu的io问题一直是比较严重的瓶颈,我自己总结有三种解决方案。
    1.优化算法,选择更有效的存储方式,减少显存的使用。只拷贝必要数据,优化数据结构等等。
    2.使用cuda内存优化技巧。比如显存整块申请和拷贝,使用pinned memory,shared memory,constant memory,使用流来掩盖内存延迟等等。
    3.硬件解决方案,放弃pci-e,使用ibm的power架构和p100,直接用nv-link进行内存传输。这个方案是一劳永逸的,但是要准备好money,一整套下来很贵的。第三个方案一般整不起,前两个方案是算法和语言技巧方面,与算法本身有很大的关系。个人经验上来讲方案1提升的潜力比较大。
    作者:杨伟光
    链接:https://www.zhihu.com/question/52472621/answer/130949946
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    https://www.cnblogs.com/1024incn/p/5891051.html

    最重要的

    • 学会使用nvprof工具,优化这个才是关键
    • 了解自己的程序是否适合做并行化,不了解自己的程序是否适合并行化的话,多读几本多线程相关的书籍,或者CPU内存操作优化的书籍,其实两者的优化思路都差不多.
    • 注意一点,如果不熟悉CUDA的话,特别是那种执行本来只需要几百毫秒的算法,一部小心写出来的代码可能比CPU版本还慢,我们的雷达处理器就是一个很好的例子。最早要优化运算,CPU版本是200ms,我同事写出来的CUDA代码居然要300ms,后来经过我的优化,最后是60ms,主要是内存操作的优化
  • 相关阅读:
    Android Studio 开发
    Jsp编写的页面如何适应手机浏览器页面
    电影
    Oracle 拆分列为多行 Splitting string into multiple rows in Oracle
    sql server 2008 自动备份
    WINGIDE 激活失败
    python安装 错误 “User installations are disabled via policy on the machine”
    ble编程-外设发送数据到中心
    iOS开发-NSString去掉所有换行及空格
    ios9 字符串与UTF-8 互相转换
  • 原文地址:https://www.cnblogs.com/linyilong3/p/11061680.html
Copyright © 2011-2022 走看看