Przeprowadziłem test porównawczy porównujący funkcję rekurencyjną z rekurencyjną funkcją lambda przy użyciu std::function<>
metody przechwytywania. Z pełną optymalizacją włączoną w wersji Clang 4.1, wersja lambda działała znacznie wolniej.
#include <iostream>
#include <functional>
#include <chrono>
uint64_t sum1(int n) {
return (n <= 1) ? 1 : n + sum1(n - 1);
}
std::function<uint64_t(int)> sum2 = [&] (int n) {
return (n <= 1) ? 1 : n + sum2(n - 1);
};
auto const ITERATIONS = 10000;
auto const DEPTH = 100000;
template <class Func, class Input>
void benchmark(Func&& func, Input&& input) {
auto t1 = std::chrono::high_resolution_clock::now();
for (auto i = 0; i != ITERATIONS; ++i) {
func(input);
}
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
std::cout << "Duration: " << duration << std::endl;
}
int main() {
benchmark(sum1, DEPTH);
benchmark(sum2, DEPTH);
}
Daje wyniki:
Duration: 0 // regular function
Duration: 4027 // lambda function
(Uwaga: potwierdziłem również wersją, która pobierała dane wejściowe z cin, aby wyeliminować ocenę czasu kompilacji)
Clang generuje również ostrzeżenie kompilatora:
main.cc:10:29: warning: variable 'sum2' is uninitialized when used within its own initialization [-Wuninitialized]
Co jest oczekiwane i bezpieczne, ale należy to zauważyć.
Wspaniale jest mieć rozwiązanie w naszych paskach narzędzi, ale myślę, że język będzie wymagał lepszego sposobu obsługi tego przypadku, jeśli wydajność ma być porównywalna z obecnymi metodami.
Uwaga:
Jak zauważył komentator, wydaje się, że najnowsza wersja VC ++ znalazła sposób na zoptymalizowanie tego do punktu równej wydajności. Może w końcu nie potrzebujemy lepszego sposobu, aby sobie z tym poradzić (z wyjątkiem cukru syntaktycznego).
Ponadto, jak wskazały inne posty SO w ostatnich tygodniach, wydajność std::function<>
sama w sobie może być przyczyną spowolnienia w porównaniu do bezpośredniego wywoływania funkcji, przynajmniej gdy przechwytywanie lambda jest zbyt duże, aby zmieścić się w niektórych zoptymalizowanych bibliotekach std::function
zastosowań przestrzeni dla małych funktorów (Myślę, że trochę lubię różne optymalizacje krótkich ciągów?).