我正在嘗試優化某些在MAESTRO處理器上使用OpenMP的矩陣 - 矩陣乘法基準測試代碼。 MAESTRO有49個處理器以7x7配置排列成二維陣列。每個內核都有自己的L1和L2緩存。該板的佈局可以在這裏看到:http://i.imgur.com/naCWTuK.png。NUMA體系結構上不同數據類型的OpenMP性能
我的主要問題是:不同的數據類型(char vs short vs int等)會直接影響基於NUMA的處理器上的OpenMP代碼的性能嗎?如果是這樣,有沒有辦法緩解呢?以下是我對這個問題的解釋。
我得到了一組研究小組用來衡量給定處理器性能的基準。基準測試導致了其他處理器的性能提升,但是他們遇到了在MAESTRO上運行它們時看不到相同類型結果的問題。下面是從我收到的基本碼的矩陣乘法的基準的一個片段:從頭文件
相關宏(MAESTRO是64位):
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <cblas.h>
#include <omp.h>
//set data types
#ifdef ARCH64
//64-bit architectures
#define INT8_TYPE char
#define INT16_TYPE short
#define INT32_TYPE int
#define INT64_TYPE long
#else
//32-bit architectures
#define INT8_TYPE char
#define INT16_TYPE short
#define INT32_TYPE long
#define INT64_TYPE long long
#endif
#define SPFP_TYPE float
#define DPFP_TYPE double
//setup timer
//us resolution
#define TIME_STRUCT struct timeval
#define TIME_GET(time) gettimeofday((time),NULL)
#define TIME_DOUBLE(time) (time).tv_sec+1E-6*(time).tv_usec
#define TIME_RUNTIME(start,end) TIME_DOUBLE(end)-TIME_DOUBLE(start)
//select random seed method
#ifdef FIXED_SEED
//fixed
#define SEED 376134299
#else
//based on system time
#define SEED time(NULL)
#endif
32位整數矩陣乘法基準:
double matrix_matrix_mult_int32(int size,int threads)
{
//initialize index variables, random number generator, and timer
int i,j,k;
srand(SEED);
TIME_STRUCT start,end;
//allocate memory for matrices
INT32_TYPE *A=malloc(sizeof(INT32_TYPE)*(size*size));
INT32_TYPE *B=malloc(sizeof(INT32_TYPE)*(size*size));
INT64_TYPE *C=malloc(sizeof(INT64_TYPE)*(size*size));
//initialize input matrices to random numbers
//initialize output matrix to zeros
for(i=0;i<(size*size);i++)
{
A[i]=rand();
B[i]=rand();
C[i]=0;
}
//serial operation
if(threads==1)
{
//start timer
TIME_GET(&start);
//computation
for(i=0;i<size;i++)
{
for(k=0;k<size;k++)
{
for(j=0;j<size;j++)
{
C[i*size+j]+=A[i*size+k]*B[k*size+j];
}
}
}
//end timer
TIME_GET(&end);
}
//parallel operation
else
{
//start timer
TIME_GET(&start);
//parallelize with OpenMP
#pragma omp parallel for num_threads(threads) private(i,j,k)
for(i=0;i<size;i++)
{
for(k=0;k<size;k++)
{
for(j=0;j<size;j++)
{
C[i*size+j]+=A[i*size+k]*B[k*size+j];
}
}
}
//end timer
TIME_GET(&end);
}
//free memory
free(C);
free(B);
free(A);
//compute and return runtime
return TIME_RUNTIME(start,end);
}
連續運行上述基準比使用OpenMP運行性能更好。我的任務是優化MAESTRO的基準以獲得更好的性能。使用下面的代碼,我能得到的性能提升:
double matrix_matrix_mult_int32(int size,int threads)
{
//initialize index variables, random number generator, and timer
int i,j,k;
srand(SEED);
TIME_STRUCT start,end;
//allocate memory for matrices
alloc_attr_t attrA = ALLOC_INIT;
alloc_attr_t attrB = ALLOC_INIT;
alloc_attr_t attrC = ALLOC_INIT;
alloc_set_home(&attrA, ALLOC_HOME_INCOHERENT);
alloc_set_home(&attrB, ALLOC_HOME_INCOHERENT);
alloc_set_home(&attrC, ALLOC_HOME_TASK);
INT32_TYPE *A=alloc_map(&attrA, sizeof(INT32_TYPE)*(size*size));
INT32_TYPE *B=alloc_map(&attrB, sizeof(INT32_TYPE)*(size*size));
INT64_TYPE *C=alloc_map(&attrC, sizeof(INT64_TYPE)*(size*size));
#pragma omp parallel for num_threads(threads) private(i)
for(i=0;i<(size*size);i++)
{
A[i] = rand();
B[i] = rand();
C[i] = 0;
tmc_mem_flush(&A[i], sizeof(A[i]));
tmc_mem_flush(&B[i], sizeof(B[i]));
tmc_mem_inv(&A[i], sizeof(A[i]));
tmc_mem_inv(&B[i], sizeof(B[i]));
}
//serial operation
if(threads==1)
{
//start timer
TIME_GET(&start);
//computation
for(i=0;i<size;i++)
{
for(k=0;k<size;k++)
{
for(j=0;j<size;j++)
{
C[i*size+j]+=A[i*size+k]*B[k*size+j];
}
}
}
TIME_GET(&end);
}
else
{
TIME_GET(&start);
#pragma omp parallel for num_threads(threads) private(i,j,k) schedule(dynamic)
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
for(k=0;k<size;k++)
{
C[i*size+j] +=A[i*size+k]*B[k*size+j];
}
}
}
TIME_GET(&end);
}
alloc_unmap(C, sizeof(INT64_TYPE)*(size*size));
alloc_unmap(B, sizeof(INT32_TYPE)*(size*size));
alloc_unmap(A, sizeof(INT32_TYPE)*(size*size));
//compute and return runtime
return TIME_RUNTIME(start,end);
}
製作語無倫次兩個輸入數組的緩存和使用OpenMP與動態調度幫助我得到的並行性能超越了串行性能。這是我第一次使用NUMA架構的處理器,所以我的「優化」很輕,因爲我還在學習。反正,我嘗試使用相同的優化與上面的代碼的8位整數版本與所有的在相同的條件(線程的數目和數組的大小):
double matrix_matrix_mult_int8(int size,int threads)
{
//initialize index variables, random number generator, and timer
int i,j,k;
srand(SEED);
TIME_STRUCT start,end;
//allocate memory for matrices
alloc_attr_t attrA = ALLOC_INIT;
alloc_attr_t attrB = ALLOC_INIT;
alloc_attr_t attrC = ALLOC_INIT;
alloc_set_home(&attrA, ALLOC_HOME_INCOHERENT);
alloc_set_home(&attrB, ALLOC_HOME_INCOHERENT);
alloc_set_home(&attrC, ALLOC_HOME_TASK);
INT8_TYPE *A=alloc_map(&attrA, sizeof(INT8_TYPE)*(size*size));
INT8_TYPE *B=alloc_map(&attrB, sizeof(INT8_TYPE)*(size*size));
INT16_TYPE *C=alloc_map(&attrC, sizeof(INT16_TYPE)*(size*size));
#pragma omp parallel for num_threads(threads) private(i)
for(i=0;i<(size*size);i++)
{
A[i] = rand();
B[i] = rand();
C[i] = 0;
tmc_mem_flush(&A[i], sizeof(A[i]));
tmc_mem_flush(&B[i], sizeof(B[i]));
tmc_mem_inv(&A[i], sizeof(A[i]));
tmc_mem_inv(&B[i], sizeof(B[i]));
}
//serial operation
if(threads==1)
{
//start timer
TIME_GET(&start);
//computation
for(i=0;i<size;i++)
{
for(k=0;k<size;k++)
{
for(j=0;j<size;j++)
{
C[i*size+j]+=A[i*size+k]*B[k*size+j];
}
}
}
TIME_GET(&end);
}
else
{
TIME_GET(&start);
#pragma omp parallel for num_threads(threads) private(i,j,k) schedule(dynamic)
for(i=0;i<size;i++)
{
for(j=0;j<size;j++)
{
for(k=0;k<size;k++)
{
C[i*size+j] +=A[i*size+k]*B[k*size+j];
}
}
}
TIME_GET(&end);
}
alloc_unmap(C, sizeof(INT16_TYPE)*(size*size));
alloc_unmap(B, sizeof(INT8_TYPE)*(size*size));
alloc_unmap(A, sizeof(INT8_TYPE)*(size*size));
//compute and return runtime
return TIME_RUNTIME(start,end);
}
然而,8比特的OpenMP版本導致時間比32位OpenMP版本慢。 8位版本的執行速度不應該比32位版本快嗎?造成這種差異的原因是什麼?有哪些可能的事情可以緩解呢?它可能與我正在使用的數組的數據類型有關嗎?
說這個芯片是7X7 NUMA是誤導的,因爲7x7 NUMA意味着每個羣集7個節點和7個羣集。該芯片顯然只有4個外部控制器。 – user3528438
這個芯片中的內核實際上有一個相當大的二級緩存,所以如果你的數據集不夠大,那麼使用較小的數據類型會浪費很多時間進行全寬類型轉換。如果擠壓數據不會提高你的表現,那就意味着沒有必要這樣做。 – user3528438
矩陣的大小是多少?順便說一句,這我幫你http://lemire.me/blog/2013/09/13/are-8-bit-or-16-bit-counters-faster-than-32-bit-counters/ – dreamcrash