Ejecuté un punto de referencia comparando una función recursiva frente a una función lambda recursiva utilizando el std::function<>
método de captura. Con las optimizaciones completas habilitadas en la versión 4.1 de clang, la versión lambda se ejecutó significativamente más lenta.
#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);
}
Produce resultados:
Duration: 0 // regular function
Duration: 4027 // lambda function
(Nota: también confirmó con una versión que tomó las entradas de cin, para eliminar la evaluación del tiempo de compilación)
Clang también produce una advertencia de compilación:
main.cc:10:29: warning: variable 'sum2' is uninitialized when used within its own initialization [-Wuninitialized]
Lo cual es esperado y seguro, pero debe tenerse en cuenta.
Es genial tener una solución en nuestros cinturones de herramientas, pero creo que el lenguaje necesitará una mejor manera de manejar este caso si el rendimiento es comparable a los métodos actuales.
Nota:
Como señaló un comentarista, parece que la última versión de VC ++ ha encontrado una manera de optimizar esto hasta el punto de un rendimiento igual. Tal vez no necesitemos una mejor manera de manejar esto, después de todo (excepto el azúcar sintáctico).
Además, como algunas otras publicaciones de SO han descrito en las últimas semanas, el rendimiento en std::function<>
sí mismo puede ser la causa de la desaceleración frente a la función de llamada directamente, al menos cuando la captura lambda es demasiado grande para caber en algunos std::function
usos de espacio optimizados para bibliotecas para pequeños functores (¿Supongo que te gustan las diversas optimizaciones de cadenas cortas?).