I am experimenting the row- vs column-major traversal. The general idea is that row-major traversal should be faster due to caching/vectorization/etc. Here is my test code:
// gcc -o main.out main.c -O3
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
size_t dim[] = {10, 50, 100, 200, 500, 1000, 5000, 10000, 20000};
struct timespec ts;
uint32_t* arr_ptr;
double delta, t0;
printf(
"Dim\tArraySize(KB)\tRow-Major Time\tRM Sample\tCol-Major Time\tCM Sample\n"
);
for (int i = 0; i < sizeof(dim) / sizeof(dim[0]); ++i) {
size_t d = dim[i];
printf("%5lu,\t%11lu,\t", d, d * d * sizeof(uint32_t) / 1024);
arr_ptr = (uint32_t*)malloc(d * d * sizeof(uint32_t));
memset(arr_ptr, 0, d * d * sizeof(uint32_t));
timespec_get(&ts, TIME_UTC);
t0 = ts.tv_sec + ts.tv_nsec / 1000.0 / 1000.0 / 1000.0;
for (int j = 0; j < d; ++j) {
for (int k = 0; k < d; ++k) {
*(arr_ptr + j * d + k) += (j + k);
}
}
timespec_get(&ts, TIME_UTC);
delta = ts.tv_sec + ts.tv_nsec / 1000.0 / 1000.0 / 1000.0 - t0;
printf("%0.9lf,\t%8u,\t", delta, *(arr_ptr + ts.tv_sec % d * d + ts.tv_nsec % d));
free(arr_ptr);
arr_ptr = (uint32_t*)malloc(d * d * sizeof(uint32_t));
memset(arr_ptr, 0, d * d * sizeof(uint32_t));
timespec_get(&ts, TIME_UTC);
t0 = ts.tv_sec + ts.tv_nsec / 1000.0 / 1000.0 / 1000.0;
for (int j = 0; j < d; ++j) {
for (int k = 0; k < d; ++k) {
*(arr_ptr + k * d + j) += (j + k);
}
}
timespec_get(&ts, TIME_UTC);
delta = ts.tv_sec + ts.tv_nsec / 1000.0 / 1000.0 / 1000.0 - t0;
printf("%0.9lf,\t%8u\n", delta, *(arr_ptr + ts.tv_sec % d * d + ts.tv_nsec % d));
free(arr_ptr);
}
return 0;
}
The result is as follows:
Dim ArraySize(KB) Row-Major Time RM Sample Col-Major Time CM Sample
10, 0, 0.000000238, 10, 0.000000238, 18
20, 1, 0.000000238, 19, 0.000000477, 40
50, 9, 0.000002384, 31, 0.000001431, 122
100, 39, 0.000013113, 96, 0.000005245, 272
200, 156, 0.000077486, 263, 0.000042200, 240
500, 976, 0.000452757, 558, 0.000420809, 362
1000, 3906, 0.001549959, 1095, 0.001713991, 1030
2000, 15625, 0.005422354, 3281, 0.006698370, 3571
5000, 97656, 0.036512375, 5439, 0.053869963, 4949
10000, 390625, 0.148355007, 7462, 1.049520969, 6046
20000, 1562500, 0.768568516, 22097, 7.388022661, 32356
The general theory holds only after the dimension reaches 500--before that, column-major traversal keeps outperforming row-major traversal. This seems not random--I ran the test multiple times with different gcc parameters, such as -O3 -ffast-math
and -O3 -fno-tree-vectorize
, the performance gap appears to be stable. But why?