Cet exemple utilise une version simplifiée de la fonction de mesure de temps présentée dans ../Sujets/AuSecours/Mesurer-le-temps.html et compile avec C++ 20 (ce n'est pas essentiel, mais ça simplifie l'écriture).
#include <iostream>
#include <chrono>
#include <cmath>
#include <algorithm>
#include <vector>
#include <random>
#include <type_traits>
#include <utility>
using namespace std;
using namespace std::chrono;
template <class F, class ... Args>
auto test(F f, Args &&... args) {
auto pre = high_resolution_clock::now();
auto res = f(std::forward<Args>(args)...);
auto post = high_resolution_clock::now();
return pair{ res, post - pre };
}
double racine_fonction(double x) {
return sqrt(x);
}
struct racine_foncteur {
double operator()(double x) const {
return sqrt(x);
}
};
int main() {
enum { N = 50'000'000 };
vector<double> v(N);
random_device rd;
mt19937 prng{ rd() };
uniform_real_distribution<> distrib; // 0.0 .. 1.0
clog.rdbuf(nullptr);
generate(begin(v), end(v), [&]() { return distrib(prng); });
cout << "Test sur des fonctions..." << flush;
auto [r0,dt0] = test([v]() mutable {
transform(begin(v), end(v), begin(v), racine_fonction);
return v.back();
});
cout << "Temps ecoule : "
<< duration_cast<milliseconds>(dt0) << '\n';
cout << "Test sur des foncteurs..." << flush;
auto [r1,dt1] = test([v]() mutable {
transform(begin(v), end(v), begin(v), racine_foncteur{});
return v.back();
});
cout << "Temps ecoule : "
<< duration_cast<milliseconds>(dt1) << '\n';
// « effet de bord » pour empêcher l'elimination des calculs
clog << r0 << ' ' << r1;
}
Quelques résultats suivent. Portez attention aux temps relatifs pour un même compilateur, mais ne comparez pas les compilateurs entre eux, car les exécutions sont faites sur des machines distinctes (note : le code que vous trouverez sur ces liens est la version C++ 17 de celui montré plus haut, mais les seules différences tiennent à l'affichage du temps écoulé) :
Wandbox (lien) | Coliru (lien) | Visual Studio 2019 (Release, 32 bits) |
---|---|---|
|
|
|
En pratique, l'enjeu ici est qu'un bon optimiseur pourra souvent réaliser des optimisations semblables avec des fonctions et avec des foncteurs, mais à code exécuté équivalent il est probable que le recours à un foncteur plutôt qu'à une fonction ne soit pas un désavantage. L'enjeu est la capacité pour un compilateur de réaliser un function inlining : dans un contexte de programmation générique, offrir plus d'information au compilateur quant aux types impliqués accroît ses capacités de remplacer le code appelant par le code appelé.