Me encontré con una optimización de código inesperada recientemente, y quería comprobar si mi interpretación de lo que estaba observando era correcta. El siguiente es un ejemplo muy simplificado de la situación:F # generalización y rendimiento automáticos
let demo =
let swap fst snd i =
if i = fst then snd else
if i = snd then fst else
i
[ for i in 1 .. 10000 -> swap 1 i i ]
let demo2 =
let swap (fst: int) snd i =
if i = fst then snd else
if i = snd then fst else
i
[ for i in 1 .. 10000 -> swap 1 i i ]
La única diferencia entre los 2 bloques de código es que en el segundo caso, declaro explícitamente los argumentos de intercambio como enteros. Sin embargo, cuando ejecuto los 2 fragmentos en fsi con #time, obtengo:
Caso 1 Real: 00: 00: 00.011, CPU: 00: 00: 00,000, GC gen0: 0, gen1: 0, gen2: 0
real Caso 2: 00: 00: 00,004, CPU: 00: 00: 00,015, GC Gen0: 0, Gen1: 0, Gen2: 0
es decir, el segundo fragmento se ejecuta 3 veces más rápido que el primero. La diferencia absoluta de rendimiento aquí obviamente no es un problema, pero si tuviera que usar la función de intercambio mucho, se acumularía.
Mi suposición es que el motivo del rendimiento alcanzado es que, en el primer caso, swap es genérico y "requiere igualdad", y verifica si int lo admite, mientras que el segundo caso no tiene que verificar nada. ¿Es esta la razón por la que esto está sucediendo o me estoy perdiendo algo más? Y, en términos más generales, ¿debería considerar la generalización automática como una espada de doble filo, es decir, una característica increíble que puede tener efectos inesperados en el rendimiento?
Gracias por el puntero a la otra pregunta, de hecho muy similar. Entonces, si lo entiendo correctamente, al marcar una función como en línea dice "mientras esta función es genérica, crea una versión específica basada en los tipos utilizados donde se llama". ¿Hay alguna razón para no marcar cada función de estilo matemático como en línea? – Mathias
O, dicho de otra manera, ¿puedes explicar un poco sobre "no genera demasiado código", que no entiendo del todo? – Mathias
@Mathias La palabra clave 'inline' significa que el compilador reemplazará la llamada a la función con su implementación. Esto significa que el código de la función se repetirá una vez por cada llamada. Para funciones realmente largas, esto podría hacer que el ensamblaje sea más grande (o generar métodos más largos que requieran mucho tiempo para JIT). Es por eso que recomendé usar esto solo para funciones más cortas: las funciones en su ejemplo me parecen muy cortas. –