zoukankan      html  css  js  c++  java
  • 一步步做程序优化-讲一个用于OpenACC优化的程序(转载)

    一步步做程序优化【1】讲一个用于OpenACC优化的程序

    分析下A,B,C为三个矩阵,A为m*n维,B为n*k维,C为m*k维,用A和B来计算C,计算方法是:C = alpha*A*B + beta*C。它的程序如下:

     1 // C = alpha*A*B + beta*C
     2 void mySgemm(int m, int n, int k, float alpha, float beta,
     3              float *A,  float *B, float *C)
     4 {
     5     int i, j, l;
     6     float ab;
     7     for(j = 0; j < m; j++) 
     8     {
     9         for(i = 0 ;i < k ;i++)
    10         {
    11             ab = 0.0f;
    12             for(l = 0 ;l < n ;l++)
    13             {
    14                 ab += A[j*n+l] * B[l*k+i];
    15             }
    16             C[j*k+i] = alpha*ab + beta*C[j*k+i];
    17         }
    18     }
    19 }

    这个程序修改自HMPP_Tutorial_Labs_CUDA中的lab0。

    C中的每个元素的计算是独立的,完全可以并行化。后面的系列文章将会讲述优化这个程序的整个过程。

    一步步做程序优化【2】OpenACC指令 

    这个写了很长时间了,但是一直没有顾上额。把这个版本稍微修改一下,只需要加上一个指令,我们就可以得到不错的效率奥。

    看代码吧:

     1 // C = alpha*A*B + beta*C
     2 void mySgemm(int m, int n, int k, float alpha, float beta,
     3              float *A,  float *B, float *C)
     4 {
     5     int i, j, l;
     6     float ab;
     7 #pragma acc kernels copy(A[0:m*n],B[0:m*n],C[0:m*n])
     8 #pragma acc loop independent
     9     for(j = 0; j < m; j++) 
    10     {
    11 #pragma acc loop independent
    12         for(i = 0 ;i < k ;i++)
    13         {
    14             ab = 0.0f;
    15             for(l = 0 ;l < n ;l++)
    16             {
    17                 ab += A[j*n+l] * B[l*k+i];
    18             }
    19             C[j*k+i] = alpha*ab + beta*C[j*k+i];
    20         }
    21     }
    22 }

    这样,我们只是加入了几个指导语句,剩下的事是编译器帮我们做的奥,你原先的测试程序并不需要任何改变奥。

    我之前讲过HMPP编译器的安装和使用,http://blog.csdn.net/bendanban/article/details/7662583大家可以使用HMPP编译器编译这段代码,在Linux下(安装好CUDA,HMPP之后)我们可以使用一下命令编译:

    $hmpp --codelet-required gcc your_program.c

    执行一下,你会发现速度相当的快了(你要有支持CUDA的显卡才行奥)

    大家可以写一个测试程序来调用这个函数,随便你用什么编译器,只要你可以在你的测试程序里找到本文中提供的程序,你完全可以使用高效的函数奥。

    为了得到更高的效率,我修改一下这个代码:

     1 // C = alpha*A*B + beta*C
     2 void mySgemm(int m, int n, int k, float alpha, float beta,
     3              float *A,  float *B, float *C)
     4 {
     5     int i, j, l;
     6     float ab;
     7 #pragma acc kernels copyin(A[0:m*n],B[0:m*n]) copy(C[0:m*n])
     8 #pragma acc loop independent
     9     for(j = 0; j < m; j++) 
    10     {
    11 #pragma acc loop independent
    12         for(i = 0 ;i < k ;i++)
    13         {
    14             ab = 0.0f;
    15             for(l = 0 ;l < n ;l++)
    16             {
    17                 ab += A[j*n+l] * B[l*k+i];
    18             }
    19             C[j*k+i] = alpha*ab + beta*C[j*k+i];
    20         }
    21     }
    22 }

    这样A和B两个矩阵就可只是传输到GPU上,而C传到GPU,计算结束后会倍传回来。

    在copy()中,A[0:m*n],表示从第0个元素一共计算m*n个元素,第一个是起始位置,第二个量表示数据长度。

    大家把代码拷贝走,去试试吧!!!

    一步步做程序优化【3】OpenHMPP指令(更加灵活的使用异构计算) 

    1、简介下HMPP

    HMPP指的是(Hybrid Multicore Parallel Programming),他是由CAPS(http://www.caps-entreprise.com(英文);www.caps-entreprise.com.cn(中文))  发起的一种异构计算的标准,他的出现可以大大减少我们的程序优化时间。大家可以参考我之前的几篇讲解HMPP的文章去获得HMPP的试用版。

    HMPP是一种基于编译指导语句(类似与OpenMP)的标准,它与OpenMP的区别是:OMP是基于CPU的并行标准,HMPP是基于异构平台的标准(例如CPU+GPU,CPU+MIC),它支持C和Fortran两种语言。另外HMPP编译器可以根据你的#pragma指令产生CUDA代码,也可以直接编译CUDA代码!

    总之,HMPP编译器非常强大!微笑

    2、使用HMPP以及OpenACC的一个推荐原则。

    使用HMPP是为了尽可能不改变原有代码的基础上只需要添加少量的#pragma 语句就可一获得几十甚至几千倍的加速比。当然前提是你原有的代码要可以正确的按照算法设计的目的执行才行。

    3、继续优化矩阵相乘的那段代码

    1)重新贴一边需要优化的代码:(特别注意这段代码来值CAPS,这是原始代码,我没有做实质性的修改)

      1 /* 
      2  * Copyright 2008 - 2012 CAPS entreprise. All rights reserved.
      3  */
      4 
      5 
      6 #include <getopt.h>
      7 #include <sys/time.h>
      8 #include <stdlib.h>
      9 #include <stdio.h>
     10 #include <string.h>
     11 #include <math.h>
     12 
     13 // Number of execution
     14 #define NB_RUNS 5
     15 
     16 // Size of the matrix
     17 #define SIZE 256
     18 
     19 // Initialization random value
     20 #define SRAND_VALUE 5347
     21 
     22 // Use to initialize the matrix
     23 float randFloat(float low, float high)
     24 {
     25   float t = (float)rand() / (float)RAND_MAX;
     26   return (1.0f - t) * low + t * high;
     27 }
     28 
     29 ////////////////////////////////////////////////////////////////////////////////
     30 // sgemm_codelet
     31 ////////////////////////////////////////////////////////////////////////////////
     32 void mySgemm( int m, int n, int k, float alpha, float beta,
     33                 float a[m][n],   float b[n][k], float c[m][k] )
     34 {
     35   int i,j,l; // Induction variables
     36   float ab;  // Temporary result 
     37 
     38   for( j = 0 ; j < m ; j++ ) {
     39     for( i = 0 ; i < k ; i++ ) {
     40       ab=0.0f;
     41       for( l = 0 ; l < n ; l++ ){
     42         ab += a[j][l] * b[l][i];
     43       }
     44       c[j][i] = alpha * ab + beta * c[j][i];
     45     }
     46   }
     47 }
     48 
     49 
     50 ////////////////////////////////////////////////////////////////////////////////
     51 // Main program
     52 ////////////////////////////////////////////////////////////////////////////////
     53 int main(int argc, char **argv)
     54 {
     55 
     56   int m=SIZE, n=SIZE, k = SIZE;
     57 
     58   float *my_a=NULL, *b=NULL, *c_hwa=NULL, *c_cpu=NULL;
     59   int i, j, ii;
     60   // For timer measures
     61   struct timeval tv_global_begin, tv_global_end; // global timer (all iterations)
     62   struct timeval tv_begin, tv_end;  // local timer (1 iteration)
     63 
     64   unsigned long long int best_measure_GPU = 0;
     65   unsigned long long int sum_measure_GPU  = 0;
     66 
     67   unsigned long long int best_measure_CPU = 0;
     68   unsigned long long int sum_measure_CPU  = 0;
     69 
     70   unsigned long long int global_CPU_time  = 0;
     71   unsigned long long int global_GPU_time  = 0;
     72 
     73   unsigned long long int current;
     74 
     75   float alpha, beta;
     76 
     77   double error    = 0.0;
     78   int index_i     = 0.0;
     79   int index_j     = 0.0;
     80   double valueCPU = 0.0;
     81   double valueGPU = 0.0;
     82 
     83 
     84 
     85   // Allocating CPU memory
     86   my_a = (float *)malloc(m* n * sizeof(float));
     87   my_b = (float *)malloc(n * k * sizeof(float));
     88   c_hwa = (float *)malloc(m * k * sizeof(float));
     89   c_cpu = (float *)malloc(m * k * sizeof(float));
     90 
     91   if((my_a == NULL) || (my_b == NULL) || (c_hwa == NULL) || (c_cpu == NULL)) {
     92     fprintf( stderr, "
    **** error : memory allocation failed ****
    
    ");
     93     return 1;
     94   }
     95 
     96   fprintf( stdout, "---- Initialization of the Matrices ----
    
    ");
     97   srand(SRAND_VALUE);
     98 
     99   //Generate options set
    100 
    101   for(i = 0; i < m; i++){
    102     for(j = 0; j < n; j++){
    103       my_a[i*n+j] = randFloat(0.0001f, 1.0f);
    104     }
    105   }
    106 
    107 
    108   for(i = 0; i < n; i++){
    109     for(j = 0; j < k; j++){
    110       my_b[i*k+j] = randFloat(0.0001f, 1.0f);
    111     }
    112   }
    113 
    114 
    115   for(i = 0; i < m; i++){
    116     for(j = 0; j < k; j++) {
    117       c_cpu[i*k+j] = randFloat(1.0, 20.0f);
    118       c_hwa[i*k+j] = c_cpu[i*k+j];
    119     }
    120   }
    121 
    122   alpha = 0.5;
    123   beta  = randFloat(1.0, 2.0);
    124 
    125   fprintf( stdout, "---- Running calculations ----
    ");
    126 
    127 
    128   // run sgemm on GPU (NB_RUNS iterations)
    129   printf("Run on GPU
    ");
    130 
    131   // Start timer
    132   gettimeofday(&tv_global_begin, NULL);
    133 
    134 
    135   for( i=0; i<NB_RUNS; i++ ) {
    136     printf("%d ",i);
    137     gettimeofday(&tv_begin, NULL);
    138 
    139     mySgemm( m, n, k, alpha, beta, my_a, my_b, c_hwa );
    140     gettimeofday(&tv_end, NULL);
    141 
    142     current = (tv_end.tv_sec-tv_begin.tv_sec)*1e6 + tv_end.tv_usec-tv_begin.tv_usec;
    143 
    144     if( ( best_measure_GPU == 0 ) || ( best_measure_GPU > current ) ){
    145       best_measure_GPU = current;
    146     }
    147     sum_measure_GPU += current;
    148   }
    149 
    150 
    151 
    152   gettimeofday(&tv_global_end, NULL);
    153   global_GPU_time = (tv_global_end.tv_sec-tv_global_begin.tv_sec)*1e6 + tv_global_end.tv_usec-tv_global_begin.tv_usec;
    154   // run sgemm on CPU (NB_RUNS iterations)
    155   printf("
    
    Run on CPU
    ");
    156 
    157   // Start timer
    158   gettimeofday(&tv_global_begin, NULL);
    159 
    160   for( i=0; i<NB_RUNS; i++ ) {
    161     printf("%d ",i);
    162     gettimeofday(&tv_begin, NULL);
    163 
    164     mySgemm( m, n, k, alpha, beta, my_a, my_b, c_cpu );
    165 
    166     gettimeofday(&tv_end, NULL);
    167     current = (tv_end.tv_sec-tv_begin.tv_sec)*1e6 + tv_end.tv_usec-tv_begin.tv_usec;
    168 
    169     if( ( best_measure_CPU == 0 ) || ( best_measure_CPU > current ) ){
    170          best_measure_CPU = current;
    171     }
    172     sum_measure_CPU += current;
    173   }
    174 
    175   gettimeofday(&tv_global_end, NULL);
    176   global_CPU_time = (tv_global_end.tv_sec-tv_global_begin.tv_sec)*1e6 + tv_global_end.tv_usec-tv_global_begin.tv_usec;
    177 
    178 
    179   // Compute error between GPU and CPU    
    180   for( ii = 0; ii < m; ii++){
    181     for(j = 0; j < k; j++){
    182       double lerror = fabs((c_hwa[ii*k+j]-c_cpu[ii*k+j])/c_cpu[ii*k+j]);
    183       if (lerror > error) {
    184         error = lerror;
    185         valueCPU = c_cpu[ii*k+j];
    186         valueGPU = c_hwa[ii*k+j];
    187         index_i = ii;
    188         index_j = j;
    189       }
    190     }
    191   }
    192 
    193   if (error > 2e-06) {
    194     fprintf( stdout, "
    
    The error is %e with index %d:%d @ %e (CPU) / %e (GPU)
    ", error, index_i, index_j, valueCPU, valueGPU);
    195     fprintf( stdout, "The error is is too big!
    ");
    196     return -1;
    197   }
    198 
    199 
    200   fprintf( stdout, "
    
    ---- Results ----");
    201   fprintf( stdout, "
    ");
    202   fprintf( stdout, "Sizes of matrices: M:%i  N:%i  K:%i
    
    ", m, n, k);
    203   fprintf( stdout, "Best HWA time    : %f ms
    ", best_measure_GPU / 1e3 );
    204   fprintf( stdout, "Mean HWA time    : %f ms
    ", sum_measure_GPU / NB_RUNS / 1e3);
    205   fprintf( stdout, "
    ");
    206   fprintf( stdout, "Best CPU time    : %f ms
    ", best_measure_CPU / 1e3 );
    207   fprintf( stdout, "Mean CPU time    : %f ms
    ", sum_measure_CPU / NB_RUNS / 1e3);
    208   fprintf( stdout, "
    ");
    209   fprintf( stdout, "Global HWA time  : %f ms
    ", global_GPU_time / 1e3 );
    210   fprintf( stdout, "Global CPU time  : %f ms
    ", global_CPU_time / 1e3 );
    211   fprintf( stdout, "
    ");
    212   fprintf( stdout, "Speed-up         : %f (computed on the best time)",
    213            ((float)best_measure_CPU)/best_measure_GPU);
    214 
    215 
    216   fprintf( stdout, "
    ");
    217 
    218   free(my_a);
    219   free(my_b);
    220   free(c_hwa);
    221   free(c_cpu);
    222 
    223   return 0;
    224 }

    注意上述代码中,测试了两次统一个函数的执行结果,下面加入两句简单的指令,然后编译执行下,看一下加速比情况。

    在第31与第32行插入一下语句:

    #pragma hmpp mylab codelet, target=CUDA, args[*].transfer=atcall

    在138行插入:

    #pragma hmpp mylab callsite

    编译执行:

    []$hmpp --codelet-required gcc source.c

    执行结果:

    ---- Initialization of the Matrices ----
    ---- Running calculations ---- Run on GPU 0 1 2 3 4 Run on CPU 0 1 2 3 4 ---- Results ---- Sizes of matrices: M:256 N:256 K:256 Best HWA time : 1.436000 ms Mean HWA time : 21.837000 ms Best CPU time : 86.995000 ms Mean CPU time : 87.583000 ms Global HWA time : 109.192000 ms Global CPU time : 437.922000 ms Speed-up : 60.581478 (computed on the best time)

    加速比是60倍多!我只是键入了两行指令而已!! 

    当然HMPP并没有到这里这么简单,它提供了很多指令,指令学习并不难,也就是说我们不用直接学习CUDA或者OpenCL就可以很方便的使用GPU的计算资源了。种种好处 只有在你试用之后才能知道的奥。

    后面的博客我还会讲解更多的指令,还有一些有意思的细节。欢迎大家关注奥!

  • 相关阅读:
    Linux kernel 拒绝服务漏洞
    WordPress Checkout插件跨站脚本漏洞和任意文件上传漏洞
    SpringMVC指定webapp的首页
    maven-jetty插件配置时,webdefault.xml的取得和修改
    pom.xml报错 : Missing artifact org.apache.shiro:shiro-spring:bundle:1.2.5
    maven+SSM+junit+jetty+log4j2环境配置的最佳实践
    “不让工具类能够实例化”的做法是适合的
    静态域/域的初始化、静态代码块/构造代码块的实行、构造方法的调用 顺序
    Java Collection Framework 备忘点
    JVM备忘点(1.8以前)
  • 原文地址:https://www.cnblogs.com/liangliangdetianxia/p/4357368.html
Copyright © 2011-2022 走看看