代码之家  ›  专栏  ›  技术社区  ›  fabmilo

优化一维卷积

  •  1
  • fabmilo  · 技术社区  · 14 年前

    但是用g++和-O3编译性能较差。

    不是家庭作业。

    #include<iostream>
    #include<cstdlib>
    #include<sys/time.h>
    
    void print_matrix( int height, int width, float *matrix){
        for (int j=0; j < height; j++){
          for (int i=0; i < width; i++){
            std::cout << matrix[j * width + i] << ",";
        }
          std::cout << std::endl;
      }
    }
    
    void fill_matrix( int height, int width,  float *matrix){
        for (int j=0; j < height; j++){
          for (int i=0; i < width; i++){
            matrix[j * width + i] = ((float)rand() / (float)RAND_MAX) ;
        }
      }
    }
    
    #define RESTRICT __restrict__
    
    void dx_matrix( int height, int width, float * RESTRICT in_matrix,  float * RESTRICT out_matrix, float *min, float *max){
      //init min,max
      *min = *max = -1.F * in_matrix[0] + in_matrix[1]; 
    
        for (int j=0; j < height; j++){
          float* row = in_matrix + j * width;
          for (int i=1; i < width-1; i++){
            float res = -1.F * row[i-1] + row[i+1]; /* -1.F * value + 0.F * value + 1.F * value; */ 
            if (res > *max ) *max = res;
            if (res < *min ) *min = res;
            out_matrix[j * width + i] = res;
          }
        }
    }
    
    void dy_matrix( int height, int width, float * RESTRICT in_matrix,  float * RESTRICT out_matrix, float *min, float *max){
      //init min,max
      *min = *max = -1.F * in_matrix[0] + in_matrix[ width + 1]; 
    
      for (int j=1; j < height-1; j++){
          for (int i=0; i < width; i++){
            float res = -1.F * in_matrix[ (j-1) * width + i] + in_matrix[ (j+1) * width + i] ;
            if (res > *max ) *max = res;
            if (res < *min ) *min = res;
            out_matrix[j * width + i] =  res;
          }
        }
    }
    
    double now (void)                                                                                          
    {                                                                                                                    
      struct timeval tv;                                                                                               
      gettimeofday(&tv, NULL);                                                                                         
      return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
    }
    
    
    int main(int argc, char **argv){
    
      int width, height;
      float *in_matrix;
      float *out_matrix;
    
      if(argc < 3){
        std::cout  << argv[0] << "usage: width height " << std::endl;
        return -1;
      }
    
      srand(123);
    
      width = atoi(argv[1]);
      height = atoi(argv[2]);
    
      std::cout << "Width:"<< width << " Height:" << height << std::endl;
    
      if (width < 3){
        std::cout << "Width too short " << std::endl;
        return -1;
      }
      if (height < 3){
        std::cout << "Height too short " << std::endl;
        return -1;
      }
    
      in_matrix = (float *) malloc( height * width * sizeof(float));
      out_matrix = (float *) malloc( height * width * sizeof(float));
    
      fill_matrix(height, width, in_matrix);
      //print_matrix(height, width, in_matrix);
    
      float min, max;
    
      double a = now();
      dx_matrix(height, width, in_matrix, out_matrix, &min, &max);
      std::cout << "dx min:" << min << " max:" << max << std::endl;
    
      dy_matrix(height, width, in_matrix, out_matrix, &min, &max);
      double b = now();
      std::cout << "dy min:" << min << " max:" << max << std::endl;
      std::cout << "time: " << b-a << " sec" << std::endl;
    
    
      return 0;
    }
    
    5 回复  |  直到 14 年前
        1
  •  1
  •   Eugene Smith    14 年前

    首先,我将重写dy循环,去掉“[(j-1)*width+I]”和“in_matrix[(j+1)*width+I]”,然后执行如下操作:

      float* p, *q, *out;
     p = &in_matrix[(j-1)*width];
     q = &in_matrix[(j+1)*width];
     out = &out_matrix[j*width];
      for (int i=0; i < width; i++){ 
            float res = -1.F * p[i] + q[i] ; 
            if (res > *max ) *max = res; 
            if (res < *min ) *min = res; 
            out[i] =  res; 
          } 
    

    做“q[i]-p[i]”而不是“-1.f*p[i]+q[i]”会稍微快一点,但是,同样的,编译器可能足够聪明,可以在你背后做这件事。

    整个过程将从SSE2和多线程中受益匪浅。我打赌从SSE2开始至少要加速3倍。可以使用OpenMP添加多线程,只需几行代码。

        2
  •  2
  •   celion    14 年前

    if (res > *max ) *max = res;
    if (res < *min ) *min = res;
    

    最大值和最小值必须写入内存。添加 限制

    //Setup
    float tempMin = ...
    float tempMax = ...
    ...
        // Inner loop
        tempMin = (res < tempMin) ? res : tempMin;
        tempMax = (res > tempMax) ? res : tempMax;
    ...
    // End
    *min = tempMin;
    *max = tempMax;
    
        3
  •  1
  •   No one in particular    14 年前

    编译器可能会注意到这一点,但在进出作用域运算符{}时,您正在堆栈上创建/释放许多变量。而不是:

    for (int j=0; j < height; j++){ 
          float* row = in_matrix + j * width; 
          for (int i=1; i < width-1; i++){ 
            float res = -1.F * row[i-1] + row[i+1];
    

    怎么样:

    int i, j;
    float *row;
    float res;
    
    for (j=0; j < height; j++){ 
          row = in_matrix + j * width; 
          for (i=1; i < width-1; i++){ 
            res = -1.F * row[i-1] + row[i+1];
    
        4
  •  1
  •   Justin Peel    14 年前

    好吧,编译器可能会处理这些问题,但这里有几个小问题:

    float res = -1.F * row[i-1] + row[i+1];
    

    可能是:

    float res = row[i+1] - row[i-1];
    

    b) 这:

    if (res > *max ) *max = res;
    if (res < *min ) *min = res;
    

    可以做成

    if (res > *max ) *max = res;
    else if (res < *min ) *min = res;
    

    在其他地方。如果第一个是真的,那么第二个不可能是真的,所以我们不要检查它。

    还有一件事。要最小化乘法,请更改

    for (int j=1; j < height-1; j++){
      for (int i=0; i < width; i++){
        float res = -1.F * in_matrix[ (j-1) * width + i] + in_matrix[ (j+1) * width + i] ;
    

    int h = 0;
    int width2 = 2 * width;
    for (int j=1; j < height-1; j++){
      h += width;
      for (int i=h; i < h + width; i++){
        float res = in_matrix[i + width2] - in_matrix[i];
    

    在循环的最后

        out_matrix[i + width] =  res;
    

    *min = *max = -1.F * in_matrix[0] + in_matrix[ width + 1 ];
    

    应该是公正的 in_matrix[ width ] 最后。

        5
  •  1
  •   Michael Anderson    14 年前

    在OS X上使用clang和g++编译器的版本分析-O3和-O2,我发现

      matrix[j * width + i] = ((float)rand() / (float)RAND_MAX) ;
    

    40%的时间花在了线上的dx矩阵上。

      out_matrix[j * width + i] = row[i+1] -row[i-1];
    

    大约9%的时间花在dx_矩阵中的条件句上。。我把它们分成一个单独的循环,看看是否有帮助,但没有什么改变。

    这是10公里乘10公里矩阵跑(大约1.6秒)

    注意,如果使用不同的编译器、不同的操作系统等,结果可能会有所不同。