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,主要是内存操作的优化
  • 相关阅读:
    C++ 的继承与虚函数 读书笔记
    C++ 类 、构造、 析构、 重载 、单例模式 学习笔记及练习
    C++ 入门随手笔记及联系
    计算机网络通信、线程、tcp、udp通信及信号量等读书笔记
    进程 信号 通信 消息队列 共享内存 进程间通信 等读书笔记及个人小练习
    C++发展概述、优缺点及应用领域
    剪枝例题大全+题解
    (可行性剪枝,上下界剪枝)「一本通 1.3 例 1」数的划分
    呜呜呜
    「一本通 1.3 例 5」weight]
  • 原文地址:https://www.cnblogs.com/linyilong3/p/11061680.html
Copyright © 2011-2022 走看看