Во-первых, если вы проводите сравнение производительности для числовых данных, списки - не лучший выбор. Попробуйте пакет типа vector для быстрых массивов.
И обратите внимание, что в Haskell вы можете добиться еще больших успехов благодаря циклическому синтезу Записав функцию create в качестве перечисления, компилятор может объединить шаг создания и цикл сгиба в один цикл, который не выделяет промежуточных структур данных. Способность делать общий синтез таким образом уникальна для GHC Haskell.
Я буду использовать векторную библиотеку (потоковое объединение циклов):
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))
Теперь, прежде чем, с вашим кодом, компилятор не может удалить все списки, и мы заканчиваем с внутренним циклом как:
$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
}
Обратите внимание, что есть два цикла: создайте генерирующий (ленивый) список и фолд, потребляющий его. Из-за лени стоимость этого списка дешева, поэтому он работает в приличной форме:
$ time ./C
4.000004999999896e14
./C 0.06s user 0.00s system 98% cpu 0.058 total
Однако, при слиянии мы получаем только один цикл!
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 сократил наши шаги создания и тестирования в один цикл без использования списков. Всего 2 дубля в регистрах.
И с вдвое меньшим количеством циклов, он работает почти вдвое быстрее:
$ 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
Это хороший пример силы, которую обеспечивают гарантии чистоты - компилятор может быть очень агрессивным при переписывании вашего кода.