MAESTRO 프로세서에서 OpenMP를 사용하는 일부 매트릭스 - 매트릭스 곱셈 벤치 마크 코드를 최적화하려고합니다. MAESTRO는 7x7 구성으로 2 차원 배열로 배열 된 49 개의 프로세서를 가지고 있습니다. 각 코어에는 고유 한 L1 및 L2 캐시가 있습니다. 보드 레이아웃은 http://i.imgur.com/naCWTuK.png입니다.NUMA 아키텍처에서 다른 데이터 형식의 OpenMP 성능
내 주요 질문 : 다른 데이터 형식 (char 대 short 대 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 비트 정수 행렬 곱셈 기준 :
여기서 I는 수신 된베이스 코드의 행렬 곱셈 기준의 단편이다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 아키텍처를 사용하는 프로세서를 사용한 첫 번째 경험이므로 아직 배우면서 '최적화'가 불투명합니다. 여하튼, I는 동일한 조건 (스레드의 개수 및 배열의 크기) 모두와 함께 상기 코드의 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는 클러스터 당 7 개의 노드와 총 7 개의 클러스터를 의미하기 때문에이 칩이 7X7 NUMA라고 말하는 것은 잘못된 것입니다. 이 칩에는 4 개의 외장 컨트롤러 만 있습니다. – user3528438
이 칩의 코어는 실제로 꽤 큰 L2 캐시를 가지고 있으므로 데이터 세트가 충분히 크지 않다면 더 작은 데이터 유형을 사용하면 전체 너비 유형으로의 변환과 전체 폭 유형에서의 변환에 많은 시간을 낭비하게됩니다. 데이터를 압착해도 성능이 향상되지 않으면 데이터를 처리 할 필요가 없다는 의미입니다. – user3528438
매트릭스의 크기는 얼마입니까? btw이 내 도움이 당신 http://lemire.me/blog/2013/09/13/are-8-bit-or-16-bit-counters-faster-than-32-bit-counters/ – dreamcrash