zoukankan      html  css  js  c++  java
  • CUDA:Supercomputing for the Masses (用于大量数据的超级计算)-第十节

    原文链接

    第十节:CUDPP, 强大的数据平行CUDA库
    Rob Farber 是西北太平洋国家实验室(Pacific Northwest National Laboratory)的高级科研人员。他在多个国家级的实验室进行大型并行运算的研究,并且是几个新创企业的合伙人。大家可以发邮件到rmfarber@gmail.com与他沟通和交流。

    在关于CUDA(Compute Unified Device Architecture,即计算统一设备架构的简称)的系列文章的第9节,我探讨了如何使用CUDA拓展高等级语言(如Python)。在本小节中,我 要讨论CUDPP,也就是CUDA Data Parallel Primitives Library数据平行基元库。CUDPP是一个快速成熟起来的包,执行一些不是那么明显的算法以有效使用GPU进行基本的数据平行运算,如归类,流数据 结实化,和构建数据结构如树和求和面积表。我在这里探讨CUDPP,是因为它可能提供一些所需的功能以加速你的某个项目的发展。

    我还介绍了创建“PLAN计划”的概念,用这个编程格式提供一个基于问题说明和目地硬件

    的 优化执行配置。尽管不是一个优化编译器,对计划PLAN的使用可以极大增强程序员的能力,为CUDA启动的多个类型的GPU创建有效软件――此外,还可在 通用库框架范围之内,提供能力为特定的问题选定特定的优化代码。例如,NVIDIA cuFFT library在恰当时候可决定采用两个FFT算法中更为有效的一种。PLAN对CUDA或这一系列文章而言不是全新的概念了,它是一个常用的设计格式, 经受得住时间考验。

     为什么使用CUDPP?

    大部分人有一个库和方法的工具箱,我们用来进行一个操作。 简而言之,这些库提供了基本指令,我们可以用来快捷地执行一些计算任务。归类只是个样例,简易有效,可调用如qsort() 例行程序,以排序次序返回数据结构。NVIDIA cuBLAS和cuFFT库为一些不那么简单的任务提供相似的功能,如编程EFT和优化的BLAS功能。


    CUDPP使用相同的理念提供优化的“同类中最好的“方法以执行基元运算,如平行前缀求和(扫描),平行排序(数字),平行减少和其它能够有效执行稀疏矩阵-向量相乘的方法,和其它运算。


    平 行前缀扫描是一个基元,帮助有效解决平行问题,在这些平行问题里,每个输出都要求对输入有全局认识。例如,前缀求和(也就是扫描,前缀减少,或部分和)是 对列表进行的运算,每个结果列表里的元素都是,根据索引,把操作数列表的元素相加获得的。这看上去就是一个串行操作,因为每个结果都取决于所有之前的值, 如下所示:


    定义:所有前缀求和运算取二进制关联运算符和n元素数组

    given: [a0, a1, ..., an-1],returns:[a0, (a0 a1), ..., (a0  a1  ...  ( an-1)].

    样例: 如 是相加,那么在n元素数组上进行所有前缀求和运算

    given [3, 1, 7, 0, 4, 1, 6, 3]returns [3, 4, 11, 11, 15, 16, 22, 25].

    上 述所有前缀求和有许多用法,包括,但是不限于,归类,词法分析,字符串比较,多项式求值,流数据结实化和构建平行柱状图和数据结构(图形,树等)。不同类 型的调查文章提供了更多广泛的,详细的应用,如Guy Blelloch的 Prefix Sums and Their Applications。
    显然,扫描的依序构建(可在CPU上单线程运行)是微不足道的。我们在输入数组里循环所有的元素,然后将数组的前元素值添加到输出数组的前元素的总和里,然后把和值写入输出数组的现有元素里。 

    1 void scan( float* output, float* input, int length)
    2 {
    3    output[0] = 0; // since this is a prescan, not a scan
    4    for(int j = 1; j < length; ++j) {
    5       output[j] = input[j-1] + output[j-1];
    6       }
    7 }

     代码为长度为n的数组执行n次加法运算-n是所需加法的最小数,以生成扫描数组。如果扫描的平行版有效率,这就意味着相对于序列版来说,平行版没有 执行更多的加法运算(或操作)。换言之,两个执行都会有相同的复杂度,O(n)。CUDPP 声称要获得O(n)扫描运行时间,以澄清CUDPP的值,因为创建平行执行并非无关重要的。如需了解更多信息,请参与Shubhabrata Sengupta et al的Scan Primitives for GPU Computing。 

    CUDDP为1.0版提供:

    分段扫描-平行执行多个可变长度扫描的算法。对算法如平行快速排序,平行稀疏矩阵-向量相乘和其它算法都有用;

    平行稀疏矩阵-向量相乘(基于分段扫描)。稀疏矩阵操作很重要,因为这样GPU可以在空间内,以高效的计算方式,使用多个零(如稀疏矩阵)在矩阵上工作。因为大部分值都是零,大部分工作都可避免。类似地,也没必要浪费空间储存零。

    改进的扫描算法,称为“warp扫描”,其性能提高,但代码更简单;

    扫描和分段扫描现在支持加法,相乘,最大和最小运算子;

    支持包容性扫描和分段扫描;

    改进的,更为有用的cudppCompact()接口;

    支持.反向协议;

    支持CUDA2.0;

    给予Mac OS X和Windows Vista更多支持

    Nt 线程配置和GPU资源,再加上PLAN是存储和重新使用这些配置的便捷方法。此外,cuFFT优化也可以被采用,不过取决于所请求的FFT是不是二次方 幂。广泛应用的FFTW项目也利用了PLAN概念。FFTW广泛应用于各类平台。因为诸多原因,当开发需要在大量GPU构架上运行的通用解决方案 时,PLANS是一个有用的工具。

    这个简单的CUDPP示例需要创建一个PLAN,以对目标GPU上的numElements元素进行正向专用浮 动求和扫描。通过填写CUDPPConfiguration struct,将之传递给规划,得以完成该任务。在本例中,告知规划算法(CUDPP_SCAN), 数据类型(CUDPP_FLOAT), 运算 (CUDPP_ADD), 和选项 (CUDPP_OPTION_FORWARD, CUDPP_OPTION_EXCLUSIVE)。方法cudppPlan和该配置被调用(配有最大数量的元素进行扫描numElements)。最后, 规划被告知我们仅希望通过传递1和0,为numRows和 rowPitch参数扫描单维数组。CUDPP文件向cudppPlan()提供了更多有关参数的细节。 

       CUDPPConfiguration config;
        config.op = CUDPP_ADD;
        config.datatype = CUDPP_FLOAT;
        config.algorithm = CUDPP_SCAN;
        config.options = CUDPP_OPTION_FORWARD | CUDPP_OPTION_EXCLUSIVE;
        
        CUDPPHandle scanplan = 0;
        CUDPPResult result = cudppPlan(&scanplan, config, numElements, 1, 0);  
        if (CUDPP_SUCCESS != result)
        {
            printf("Error creating CUDPPPlan
    ");
            exit(-1);
        }
    

      调用cudppPlan成功后,向计划对象inscanplan返回句柄(指针)。CUDPP然后开始运行,调用cudppScan(), 规划句柄,输出和输入设备数组,以及需扫描元素数都被传递给它。

    1  // Run the scan
    2     cudppScan (scanplan, d_odata, d_idata, numElements);

    然后, 使用cudaMemcpy将扫描结果从d_odata复制回主机。通过计算CPU上的参考解决方案(通过computeSumScanGold()),验证GPU结果,然后对比CPU和GPU结果以确保正确性。

     1 // allocate mem for the result on host side
     2     float* h_odata = (float*) malloc( memSize);
     3     // copy result from device to host
     4     CUDA_SAFE_CALL( cudaMemcpy( h_odata, d_odata, memSize,
     5                                 cudaMemcpyDeviceToHost) );
     6     // compute reference solution
     7     float* reference = (float*) malloc( memSize);
     8     computeSumScanGold( reference, h_idata, numElements, config);
     9     // check result
    10     CUTBoolean res = cutComparef( reference, h_odata, numElements);
    11     printf( "Test %s
    ", (1 == res) ? "PASSED" : "FAILED");

    最后, 调用cudppDestroyPlan() 清理内存空间。主机然后使用using free() 和 cudaFree分别释放本地和设备数组,退出应用程序,因为simpleCUDPP已经完毕。

    1 result = cudppDestroyPlan (scanplan);
    2 if (CUDPP_SUCCESS != result)
    3 {
    4     printf("Error destroying CUDPPPlan
    ");
    5     exit(-1);
    6 }

    稀疏矩阵-向量相乘

    CUDPP包含许多其它强大的功能,但是本文未做讨论。例如,使用CUDPP进行稀疏矩阵-向量相乘的简单测试代码是sptest.cu。从http://www.nada.kth.se/~tomaso/gpu08/sptest.cu.可进行下载。你可使用以下代码编译和运行: 

    # nvcc -I cudpp_1.0a/cudpp/include -o sptest sptest.cu 
           -L cudpp_1.0a/lib -lcudpp
    # ./sptest
  • 相关阅读:
    修改VNC的分辨率
    How to use WinSCP with public key authentication
    CentOS-7-x86_64-DVD-1511.iso
    6.828
    Tampermonkey版Vimium
    servlet+jsp完成简单登录
    Servlet知识点小结
    HTTP基础知识点小结
    LeetCode 371两数之和
    LeetCode53 最大子序列问题
  • 原文地址:https://www.cnblogs.com/liangliangdetianxia/p/3981848.html
Copyright © 2011-2022 走看看