Reduciendo el tiempo JIT con llamadas recursivas en Julia
Tengo una función recursiva que opera un árbol binario de enteros, implementado como un par anidado de pares o enteros. Mi función crea un nuevo árbol con una estructura diferente y se llama a sí misma recursivamente hasta que se cumple alguna condición. El problema que encuentro es que la primera vez que se ejecuta el código, tarda mucho tiempo en compilar JIT todas las posibles firmas de la función; después de eso, se ejecuta bien.
Aquí hay un ejemplo mínimo de trabajo:
mi_arbol = ((((6 => 7) => (6 => 7)) => ((7 => 7) => (0 => 7))) => (((8 => 7) => (7 => 7)) => ((8 => 8) => (8 => 0)))) => ((((2 => 4) => 7) => (6 => (0 => 5))) => (((6 => 8) => (2 => 8)) => ((2 => 1) => (4 => 5))))
function reducir_arbol(arbol::Pair)
izquierda, derecha = arbol
izquierda isa Pair && (izquierda = reducir_arbol(izquierda))
derecha isa Pair && (derecha = reducir_arbol(derecha))
return izquierda + derecha
end
@show mi_arbol
@show reducir_arbol(mi_arbol)
using MethodAnalysis
metodos = methodinstances(reducir_arbol)
@show length(metodos)
Aunque este ejemplo no es perceptiblemente lento, aún genera 9 instancias de métodos para:
reducir_arbol(::Pair{Pair{Pair{Int64, Int64}, Pair{Int64, Int64}}, Pair{Pair{Int64, Int64}, Pair{Int64, Int64}}}})
reducir_arbol(::Pair{Pair{Int64, Int64}, Pair{Int64, Int64}})
reducir_arbol(::Pair{Int64, Int64})
reducir_arbol(::Pair{Pair{Pair{Pair{Int64, Int64}, Int64}, Pair{Int64, Pair{Int64, Int64}}}, Pair{Pair{Pair{Int64, Int64}, Pair{Int64, Int64}}, Pair{Pair{Int64, Int64}, Pair{Int64, Int64}}}})
etc ...
¿Hay alguna forma de evitar esto / precompilar / acelerar / escribir una función genérica / ejecutar una parte particular (de) una función en modo interpretado? Estaría dispuesto a hacer que el rendimiento general del código sea ligeramente peor a cambio de que se ejecute más rápido en la primera llamada de nivel superior a reducir_arbol
.
davy.ai
Una forma de acelerar el tiempo de compilación inicial de
tree_reduce
es tipificar explícitamente los argumentos en la firma de la función. Esto ayudará al compilador a generar instancias de métodos específicos para la función, en lugar de intentar compilar para todos los posibles tipos de argumentos.Por ejemplo, en lugar de definir la función como
function tree_reduce(tree::Pair)
, puedes especificarla de manera más específica comofunction tree_reduce(tree::Pair{Pair{Int64, Int64}, Pair{Int64, Int64}})
. Esto generará una instancia de método para este tipo específico de entrada, en lugar de intentar compilar para todos los tipos de Pair.Otra opción es utilizar la macro
@code_warntype
para identificar cualquier inestabilidad de tipo en la función que pueda estar causando tiempos de compilación más lentos. Esto puede ayudar a optimizar la función y acelerar la compilación.Finalmente, puedes usar la macro
@interpreter
para ejecutar partes específicas de la función en modo interpretado. Esto puede ser útil para pruebas y depuración, pero no necesariamente acelerará el tiempo de compilación.En general, optimizar la función y tipificar explícitamente los argumentos pueden ayudar a acelerar el tiempo de compilación de
tree_reduce
. Sin embargo, es importante equilibrar el rendimiento con la legibilidad y mantenibilidad del código.