If you define:
int my_array[10][10][10];
One thing that's required is the meaning of the indices with respect to storage. my_array[1][2][3]
is adjacent in memory to my_array[1][2][4]
, but not to my_array[1][3][3]
. my_array[2][2][3]
is further away still. This can affect performance as you increment one index or another - incrementing the last index is generally faster since you get more cache hits.
Example:
const int K = 400;
int my_array[K][K][K];
int main() {
for (int i = 0; i < K; ++i) {
for (int j = 0; j < K; ++j) {
for (int k = 0; k < K; ++k) {
#ifdef FAST
my_array[i][j][k] = 12;
#else
my_array[k][j][i] = 12;
#endif
}
}
}
}
Output:
$ g++ looporder.cpp -o looporder && time ./looporder
real 0m2.500s
user 0m2.249s
sys 0m0.155s
$ g++ looporder.cpp -o looporder -DFAST && time ./looporder
real 0m0.516s
user 0m0.327s
sys 0m0.124s
$ g++ looporder.cpp -o looporder -O3 && time ./looporder
real 0m2.234s
user 0m2.140s
sys 0m0.093s
$ g++ looporder.cpp -o looporder -DFAST -O3 && time ./looporder
real 0m0.250s
user 0m0.171s
sys 0m0.108s
What the indices mean in terms of what's actually stored there is up to you - as you say it's just a question of "which way up" your cube is. So generally you choose them so that whatever you're incrementing in your innermost loop, is the last dimension.