Dans l'une de ses présentations, Andrei Alexandrescu suggère que, sur une plate-forme 64 bits, l'utilisation d'une indexation de tableau 32 bits est plus rapide que l'utilisation d'un pointeur brut :
Page 16 : http://www.slideshare.net/andreialexandrescu1/three-optimization-tips-for-c-15708507
Sur son compte Facebook, il est plus précis et dit : "Préférer l'indexation des tableaux aux pointeurs (cette affirmation semble s'inverser tous les dix ans)".
J'ai essayé de nombreuses choses pour trouver une différence, mais je n'ai pas réussi à construire un programme qui montre cette différence. Connaissant Andrei, je ne serais pas surpris que la différence ne dépasse pas quelques pour cent, mais je serais heureux que quelqu'un trouve un tel exemple.
Voici un test que j'ai effectué. Je choisis n = 5000, assez grand pour obtenir un timing décent et assez petit pour que tout rentre dans le cache L1. Je boucle plusieurs fois pour que la fréquence du CPU augmente.
#include <iostream>
#include <chrono>
int main(int argc, const char* argv[]) {
const int n{5000};
int* p{new int[n]};
// Warm up the cache
for (int i{0}; i < n; i++) {
p[i] += 1;
}
for (int j{0}; j < 5; j++) {
{
auto start_pointer = std::chrono::high_resolution_clock::now();
for (int* q{p}; q != p + n; ++q) {
++(*q);
}
auto end_pointer = std::chrono::high_resolution_clock::now();
auto time_pointer = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_pointer - start_pointer)
.count();
std::cout << " Pointer: " << time_pointer << std::endl;
}
{
auto start_pointer = std::chrono::high_resolution_clock::now();
for (int* q{p}; q != p + n; ++q) {
++(*q);
}
auto end_pointer = std::chrono::high_resolution_clock::now();
auto time_pointer = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_pointer - start_pointer)
.count();
std::cout << " Pointer: " << time_pointer << std::endl;
}
{
auto start_index_32 = std::chrono::high_resolution_clock::now();
for (int i{0}; i < n; i++) {
p[i] += 1;
}
auto end_index_32 = std::chrono::high_resolution_clock::now();
auto time_index_32 = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_index_32 - start_index_32)
.count();
std::cout << "Index 32: " << time_index_32 << std::endl;
}
{
auto start_index_32 = std::chrono::high_resolution_clock::now();
for (int i{0}; i < n; i++) {
p[i] += 1;
}
auto end_index_32 = std::chrono::high_resolution_clock::now();
auto time_index_32 = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_index_32 - start_index_32)
.count();
std::cout << "Index 32: " << time_index_32 << std::endl;
}
{
const std::size_t n_64{n};
auto start_index_64 = std::chrono::high_resolution_clock::now();
for (std::size_t i{0}; i < n_64; i++) {
p[i] += 1;
}
auto end_index_64 = std::chrono::high_resolution_clock::now();
auto time_index_64 = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_index_64 - start_index_64)
.count();
std::cout << "Index 64: " << time_index_64 << std::endl;
}
{
const std::size_t n_64{n};
auto start_index_64 = std::chrono::high_resolution_clock::now();
for (std::size_t i{0}; i < n_64; i++) {
p[i] += 1;
}
auto end_index_64 = std::chrono::high_resolution_clock::now();
auto time_index_64 = std::chrono::duration_cast<std::chrono::nanoseconds>(
end_index_64 - start_index_64)
.count();
std::cout << "Index 64: " << time_index_64 << std::endl;
}
std::cout << std::endl;
}
delete[] p;
return 0;
}
Voici le type de résultat que j'obtiens sur ma machine (core i7). Tout ce que j'obtiens est du "bruit".
Pointer: 883
Pointer: 485
Index 32: 436
Index 32: 380
Index 64: 372
Index 64: 429
Pointer: 330
Pointer: 316
Index 32: 336
Index 32: 321
Index 64: 337
Index 64: 318
Pointer: 311
Pointer: 314
Index 32: 318
Index 32: 319
Index 64: 316
Index 64: 301
Pointer: 306
Pointer: 325
Index 32: 323
Index 32: 313
Index 64: 318
Index 64: 305
Pointer: 311
Pointer: 319
Index 32: 313
Index 32: 324
Index 64: 315
Index 64: 303