OpenMP并行化(Block Matrix Mult)

时间:2017-04-18 22:35:07

标签: c matrix openmp matrix-multiplication

我试图实现块矩阵乘法并使其更加并行化。

这是我的代码:

int i,j,jj,k,kk;
float sum;
int en = 4 * (2048/4);
    #pragma omp parallel for collapse(2) 
for(i=0;i<2048;i++) {
    for(j=0;j<2048;j++) {
        C[i][j]=0;
    }
}
for (kk=0;kk<en;kk+=4) {
    for(jj=0;jj<en;jj+=4) {
        for(i=0;i<2048;i++) {
            for(j=jj;j<jj+4;j++) {
                sum = C[i][j];
                for(k=kk;k<kk+4;k++) {
                    sum+=A[i][k]*B[k][j];
                }
                C[i][j] = sum;
            }
        }
    }
}

我一直在使用OpenMP,但仍然没有运气确定在最短的时间内完成这项工作的最佳方法。

2 个答案:

答案 0 :(得分:2)

从矩阵乘法中获得良好的性能是一项重要工作。因为&#34;最好的代码是我不必写的代码,所以更好地利用你的时间就是了解如何使用BLAS库。

如果您使用的是X86处理器,则可免费获得英特尔数学核心库(MKL),并包含优化的并行化矩阵乘法运算。 https://software.intel.com/en-us/articles/free-mkl

(FWIW,我为英特尔工作,但没有为MKL工作: - ))

答案 1 :(得分:0)

我最近又开始研究密集矩阵乘法(GEMM)。事实证明,Clang编译器非常擅长优化GEMM而不需要任何内在函数(GCC仍然需要内在函数)。以下代码获得了我的四核/八硬件线程Skylake系统的峰值FLOPS的60%。它使用块矩阵乘法。

超线程性能更差,因此您确保只使用等于内核数量的线程并绑定线程以防止线程迁移。

headers = {
            'Authorization': 'orgId=<ORG_ID>',
            'Content-Type': 'application/json',
          }
data = ' {"startTime": "%s",
          "endTime": "%s", 
          "granularity": 2, ' \
          '"selector": {"orderBy":[{"field":"localSpend","sortOrder":"DESCENDING"}], ' \
          '"fields": ["localSpend"], "pagination": { "offset": 0, "limit": 1000 } }, ' \
          '"groupBy": ["COUNTRY_CODE"], "returnRowTotals": false, "returnRecordsWithNoMetrics": false }' % (date_report, date_report) 
url = 'https://api.searchads.apple.com/api/v1/reports/campaigns/%s/searchterms' % (your_campaign_id)
r = requests.post(url, headers=headers, data=data,
                                      cert=('<path to crt file>',
                                            '<path to key file>'))

然后像这样编译

export OMP_PROC_BIND=true
export OMP_NUM_THREADS=4

代码

clang -Ofast -march=native -fopenmp -Wall gemm_so.c

GEMM每次迭代无限循环10次,并将FLOPS的低,中,高比率打印到peak_FLOPS,最后打印FLOPS中位数。

您需要调整以下行

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <omp.h>
#include <x86intrin.h>

#define SM 80

typedef __attribute((aligned(64))) float * restrict fast_float;

static void reorder2(fast_float a, fast_float b, int n) {
  for(int i=0; i<SM; i++) memcpy(&b[i*SM], &a[i*n], sizeof(float)*SM);
}

static void kernel(fast_float a, fast_float b, fast_float c, int n) {
  for(int i=0; i<SM; i++) {
    for(int k=0; k<SM; k++) {
      for(int j=0; j<SM; j++) {
        c[i*n + j] += a[i*n + k]*b[k*SM + j];
      }
    }
  }
}

void gemm(fast_float a, fast_float b, fast_float c, int n) {
  int bk = n/SM;

  #pragma omp parallel
  {
    float *b2 = _mm_malloc(sizeof(float)*SM*SM, 64);
    #pragma omp for collapse(3)
    for(int i=0; i<bk; i++) {
      for(int j=0; j<bk; j++) {
        for(int k=0; k<bk; k++) {
          reorder2(&b[SM*(k*n + j)], b2, n);
          kernel(&a[SM*(i*n+k)], b2, &c[SM*(i*n+j)], n);
        }
      }
    }
    _mm_free(b2);
  }
}

static int doublecmp(const void *x, const void *y) { return *(double*)x < *(double*)y ? -1 : *(double*)x > *(double*)y; }

double median(double *x, int n) {
  qsort(x, n, sizeof(double), doublecmp);
  return 0.5f*(x[n/2] + x[(n-1)/2]);
}

int main(void) {
  int cores = 4;
  double frequency = 3.1; // i7-6700HQ turbo 4 cores
  double peak = 32*cores*frequency;

  int n = SM*10*2;

  int mem = sizeof(float) * n * n;
  float *a = _mm_malloc(mem, 64);
  float *b = _mm_malloc(mem, 64);
  float *c = _mm_malloc(mem, 64);

  memset(a, 1, mem), memset(b, 1, mem);

  printf("%dx%d matrix\n", n, n);
  printf("memory of matrices: %.2f MB\n", 3.0*mem*1E-6);
  printf("peak SP GFLOPS %.2f\n", peak);
  puts("");

  while(1) {
    int r = 10;
    double times[r];
    for(int j=0; j<r; j++) {
      times[j] = -omp_get_wtime();
      gemm(a, b, c, n);
      times[j] += omp_get_wtime();
    }

    double flop = 2.0*1E-9*n*n*n;  //GFLOP
    double time_mid = median(times, r);
    double flops_low  = flop/times[r-1], flops_mid  = flop/time_mid, flops_high = flop/times[0];
    printf("%.2f %.2f %.2f %.2f\n", 100*flops_low/peak, 100*flops_mid/peak, 100*flops_high/peak, flops_high);
  }
}

到物理内核的数量,所有内核的频率(如果启用了turbo),以及每个内核的浮动指针操作数,对于Core2-Ivy Bridge为int cores = 4; double frequency = 3.1; // i7-6700HQ turbo 4 cores double peak = 32*cores*frequency; ,对于Haswell为16 -Kaby Lake,以及{Xeon Phi Knights Landing 32

使用NUMA系统时,此代码可能效率较低。它与Knight Landing的表现差不多(我刚开始研究这个问题)。

相关问题