• 【openACC教程】编写第一个OpenACC程序


    openACC教程】编写第一个OpenACC程序

    我们接下来介绍几个例子,我们鼓励你对每个例子都做一个尝试。这些事例程序可以在PGI的官方网站上获得http://www.pgroup.com/lit/samples/pgi_accelerator_examples.tar

    我们的第一个例子从一个简单的程序开始。这个程序是把一个浮点向量送到GPU上,然后乘以2.再把结果返回。
    整个程序是:

    #include <stdio.h>

    #include <stdlib.h>

    #include <assert.h>

    int main( int argc, char* argv[] )

    {

    int n; /* size of the vector */

    float *a; /* the vector */

    float *restrict r; /* the results */

    float *e; /* expected results */

    int i;

    if( argc > 1 )

        n = atoi( argv[1] );

    else

        n = 100000;

    if( n <= 0 ) n = 100000;

    a = (float*)malloc(n*sizeof(float));

    r = (float*)malloc(n*sizeof(float));

    e = (float*)malloc(n*sizeof(float));

    /* initialize */

    for( i = 0; i < n; ++i ) a[i] = (float)(i+1);

    #pragma acc kernels loop

    for( i = 0; i < n; ++i ) r[i] = a[i]*2.0f;

    /* compute on the host to compare */

    for( i = 0; i < n; ++i ) e[i] = a[i]*2.0f;

    /* check the results */

    for( i = 0; i < n; ++i )

        assert( r[i] == e[i] );

    printf( “%d iterations completed\n”, n );

    return 0;

    }

    请注意,在对指针r的声明中使用了 restrict 关键字我们很快就会知道为什么.
    还请注意,定义浮点常量2.0f 而不是2.0 . 在默认情况下,C语言的浮点常量是双精度。表达式a*2将会作为双精度计算,就好像(float)((double)a * 2.0). 为了避免这样,定义浮点常量,或者使用编译器选项-Mfcon,这样告诉编译器将浮点指针常量默认为通常的浮点来处理。
    我们把进入GPU的循环之前放了一个kernals loop指令。这是告诉编译器去在查找循环的并行性,将数据传输到GPU上,在GPU上进行计算,然后将数据返回。对于这个程序,很简单。只需要输入下面的命令:

    % pgcc -o acc_c1.exe acc_c1.c -acc -Minfo  

     

    请注意-acc-Minfo指令。-acc可以在编译器里启动OpenACC指令,默认情况下,PGI编译器认为目标加速区域在一个NVIDIA GPU上。我们会在后面的例子里演示其他的选项。
    -Minfo指令就是从编译器上获得信息。我们将在我们的例子里解释这些信息代表的意思。当你在调试性能的时候,你会非常想了解这些信息的意思。


    如果一切都安装正确,你就会从pgcc上获得以下信息:

       main:

    24, Generating copyout(r[:n])

        Generating copyin(a[:n])

        Generating compute capability 1.0 binary

         Generating compute capability 2.0 binary

    25, Loop is parallelizable

         Accelerator kernel generated

    25, #pragma acc loop gang, vector(256)

    /* blockIdx.x threadIdx.x */  

     

    让我解释几个信息,首先是:
    Generating copyout(r[:n])
    就是告诉你在GPU上分配了数组r, 在循环执行后,把数据从GPU上复制回主机上。
    Generating copyin(a[:n])
    就是告诉你编译器认为数组为循环的输入,因此数组an个元素需要从CPU上复制到GPU上。
    Loop is paralleizable
    就是告诉你编译器分析循环体的参数后认为所有的迭代可以并行执行。我们增加了restrict关键词在指针r的声明,否则编译器不能确保ar指向不同的mermory
    Accelerator kernal generated
    这是告诉你,编译器已经成功地把循环体转化成GPU的一个内核。这个内核是GPU自己的函数,由编译器产生,将会被程序调用,并在GPU上并行执行。

    这样,你准备运行程序。假设你是在GPU设备上执行,只需要输入执行文件的名字,acc_c1.exe.如果你得到一个信息
    libcuda.so not found, exiting
    那么你可能是没有在它默认的位置安装CUDA驱动。你可能不得不设置环境变量LD_LIBRARY_PATH.
    程序运行结束,你应该会看到一个结果:
    100000 iterations completed

    你如何知道哪些在GPU上执行?你可以设置环境变量ACC_NOTIFY1.
    csh: setenv ACC_NOTIFY 1
    bash: export ACC_NOTIFY=1
    然后重新运行程序。它会指出每次一个GPU内核执行的行数。在这个程序中,你会看到下面的结果:
    launch kernel file=acc_c1.c function=main
    line=25 device=0 grid=391 block=25
    这是告诉你内核的文件名,函数,以及行数,CUDA grid和线程块的大小。
    你可能不想给你所有的程序都设置这个环境变量,但在程序开发和测试的时候,这是个有用的办法。

  • 相关阅读:
    vscode Nodejs 调试 相关总结
    编程语言中的foo,bar到底是什么
    带T和带Z的相关时间是什么 及关于时间的一些知识
    自定义Firefox的 "切换previous标签页"快捷键, 增加"切回last标签页"快捷键
    开始使用Firefox
    Fork-Join 原理深入分析(二)
    Fork-Join分治编程介绍(一)
    Executor框架(七)Future 接口、FutureTask类
    Executor框架(六)CompletionService 接口
    Executor框架(五)Executors工厂类
  • 原文地址:https://www.cnblogs.com/gpus/p/2565579.html
Copyright © 2020-2023  润新知