reference: 시스템 프로그래밍 수업 / 최종무 교수님
일반적으로 캐시는 코어와 밀접한 메모리 공간을 뜻하지만, 메인 메모리 또한 디스크의 딴의 관점에서 캐시로 볼 수 있다.
(Model)
cf) Intel Core i7 캐시 계층 구조
void multiplication_ijk(int n, int (*A)[N], int (*B)[N]){
int i, j, k, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(i=0; i<N; i++){
for(j=0; j<N; j++){
sum = 0;
for(k=0; k<N; k++)
sum += A[i][k]*B[k][j];
C[i][j] = sum;
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1;
gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[ijk]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
void multiplication_jik(int n, int (*A)[N], int (*B)[N]){
int i, j, k, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(j=0; j<N; j++){
for(i=0; i<N; i++){
sum = 0;
for(k=0; k<N; k++)
sum += A[i][k]*B[k][j];
C[i][j] = sum;
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1;
gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[jik]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
version_ijk와 jik의 실행 시간
- 배열 B의 접근은 행우선 접근이므로 캐시 히트율 측면에서 나쁘다.
void multiplication_jki(int n, int (*A)[N], int (*B)[N]){
int i, j, k, r, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(j=0; j<N; j++){
for(k=0; k<N; k++){
r = B[k][j];
for(i=0; i<N; i++)
C[i][j] += A[i][k]*r;
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1; gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[jki]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
void multiplication_kji(int n, int (*A)[N], int (*B)[N]){
int i, j, k, r, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(k=0; k<N; k++){
for(j=0; j<N; j++){
r = B[k][j];
for(i=0; i<N; i++)
C[i][j] += A[i][k]*r;
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1; gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[kji]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
version_jki와 kji의 실행 시간
배열 A에서 read, C에서 write 모두 행우선 접근 -> 캐시 히트율이 더 낮아진다. 결국 가장 안좋은 성능을 보인다.
void multiplication_kij(int n, int (*A)[N], int (*B)[N]){
int i, j, k, r, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(k=0; k<N; k++){
for(i=0; i<N; i++){
r = A[i][k];
for(j=0; j<N; j++)
C[i][j] += r*B[k][j];
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1; gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[kij]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
void multiplication_ikj(int n, int (*A)[N], int (*B)[N]){
int i, j, k, r, sum;
struct timeval stime, etime, gap;
int (*C)[N];
C = (int(*)[N])malloc(sizeof(int)*N*N);
gettimeofday(&stime, NULL);
for(i=0; i<N; i++){
for(k=0; k<N; k++){
r = A[i][k];
for(j=0; j<N; j++)
C[i][j] += r*B[k][j];
}
}
gettimeofday(&etime, NULL);
gap.tv_sec = etime.tv_sec - stime.tv_sec;
gap.tv_usec = etime.tv_usec - stime.tv_usec;
if (gap.tv_usec < 0) {
gap.tv_sec = gap.tv_sec - 1; gap.tv_usec = gap.tv_usec + 1000000;
}
printf("[ikj]: Elapsed time %ldsec :%ldusec\n", gap.tv_sec, gap.tv_usec);
free(C);
}
version_kij와 ikj의 실행 시간
배열 B, C 모두 열우선 접근 -> 가장 성능이 좋다.