まず、数値に関するパフォーマンスの比較を行う場合、リストは最良の選択ではありません。高速配列用のベクトルパッケージのようなパッケージを試してください。
そして、ループ融合のおかげで、Haskellでさらにうまくやれることに注意してください。create関数を列挙型として記述することにより、コンパイラーはcreateステップとfoldループを組み合わせて、中間データ構造を割り当てない単一のループにすることができます。このような一般的な融合を行う能力は、GHCHaskellに固有のものです。
ベクトルライブラリ(ストリームベースのループ融合)を使用します。
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
}
2つのループがあることに注意してください。(遅延)リストを生成する作成と、それを消費するフォールドです。怠惰のおかげで、そのリストのコストは安いので、それは立派に実行されます:
$ 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倍になります。また、ループの数が半分になると、実行速度はほぼ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
これは、純度の保証が提供するパワーの良い例です。コンパイラーは、コードの再調整に非常に積極的になる可能性があります。