En primer lugar, si está haciendo comparaciones de rendimiento para cosas numéricas, las listas no son la mejor opción. Pruebe un paquete como el vector para arreglos rápidos.
Y tenga en cuenta que puede hacerlo aún mejor en Haskell, gracias a la fusión de bucle. Al escribir la función de creación como una enumeración, el compilador puede combinar el paso de creación y el de plegado, en un solo bucle que no asigna estructuras de datos intermedias. La capacidad de hacer una fusión general como esta es exclusiva de GHC Haskell.
Voy a usar la biblioteca de vectores (fusión bucle basada en secuencias):
import qualified Data.Vector as V
test = V.foldl (\ a b -> a + b * sqrt b) 0
create n = (V.enumFromTo 1 n)
main = print (test (create 1000000))
Ahora, antes de que, con su código, el compilador no es capaz de eliminar todas las listas, y terminamos con un interior como:
$wlgo :: Double# -> [Double] -> Double#
$wlgo =
\ (ww_sww :: Double#) (w_swy :: [Double]) ->
case w_swy of _ {
[] -> ww_sww;
: x_aoY xs_aoZ ->
case x_aoY of _ { D# x1_aql ->
$wlgo
(+##
ww_sww (*## x1_aql (sqrtDouble# x1_aql)))
xs_aoZ
}
}
$wcreate :: Double# -> [Double]
$wcreate =
\ (ww_swp :: Double#) ->
case ==## ww_swp 0.0 of _ {
False ->
:
@ Double
(D# ww_swp)
($wcreate (-## ww_swp 1.0));
True -> [] @ Double
}
Observe cómo hay dos bucles: cree la generación de una lista (diferida) y la vez que la consuma. Gracias a la pereza, el costo de esa lista es barato, por lo que se ejecuta en un respetable:
$ time ./C
4.000004999999896e14
./C 0.06s user 0.00s system 98% cpu 0.058 total
Bajo la fusión, sin embargo, tenemos en cambio un solo bucle solamente!
main_$s$wfoldlM_loop :: Double# -> Double# -> Double#
main_$s$wfoldlM_loop =
\ (sc_sYc :: Double#) (sc1_sYd :: Double#) ->
case <=## sc_sYc 1000000.5 of _ {
False -> sc1_sYd;
True ->
main_$s$wfoldlM_loop
(+## sc_sYc 1.0)
(+##
sc1_sYd (*## sc_sYc (sqrtDouble# sc_sYc)))
GHC redujo nuestros pasos de creación y prueba en un solo bucle sin utilizar listas. Solo 2 dobles en registros. Y con la mitad de los bucles, que recorre casi dos veces más rápido:
$ ghc D.hs -Odph -fvia-C -optc-O3 -optc-march=native -fexcess-precision --make
$ time ./D
4.000005000001039e14
./D 0.04s user 0.00s system 95% cpu 0.038 total
Este es un buen ejemplo del poder que las garantías de pureza proporcionan - el compilador puede ser muy agresivos en Reording su código.
OCaml tiene 'lazy' y' Lazy.force'. Solía ser la implementación obvia del lado ML que cualquiera podía escribir con una referencia a un cierre. Soporte especial fue integrado en el tiempo de ejecución en 3.0? y más soporte (coincidencia de patrones) en 3.11.0. Hoy en día, el GC elimina la indirección un tiempo después de que la suspensión ha sido forzada. http://ralyx.inria.fr/2008/Raweb/gallium/uid48.html –