The following code:
import Control.Exception
import Data.List
updateAverage :: (Fractional t) => (t, t) -> t -> (t, t)
updateAverage (old_value, old_counter) x =
let new_counter = old_counter + 1
in
assert(new_counter /= 0)
old_value `seq` (old_value + (x - old_value) / new_counter, new_counter)
average values = fst (foldl' updateAverage (0.0, 0.0) values) -- version I
main = do
let v = [1 .. 1000000]
let a = average v
putStrLn (show a)
becomes faster (compilation options: ghc.exe -O3
) when I replace the definition of average
function with
average = fst . foldl' updateAverage (0.0, 0.0) -- version II
What could be the reason for this? I thought that the differences between these two lines are basically syntax. Is the second version (without free variable values
) easier for the compiler to optimize?
Funnily enough, when compiled without optimization, version I becomes faster.
Timing results:
options: -O3
version I: 0.280s version II: 0.212s
options: (no optimization)
version I: 0.42s version II: 0.44s
Measured using time
shell command in Cygwin.
Timing results with type=Double:
Double:
options: -O3
version I: 0.22s version II:: 0.212s
options: (no optimization)
version I: 0.34s version II: 0.35s
More info: I'm using the compiler
> $ ghc -v Glasgow Haskell Compiler, Version 7.0.4, for Haskell 98,
> stage 2 booted by GHC version 6.12.2 Using binary package database:
> C:\Program Files\Haskell
> Platform\2011.4.0.0\lib\package.conf.d\package.cache wired-in package
> ghc-prim mapped to ghc-prim-0.2.0.0-e1f7c380581d61d42b0360d440cc35ed
> wired-in package integer-gmp mapped to
> integer-gmp-0.2.0.3-91607778cf3ae8f3948a50062b4f8479 wired-in package
> base mapped to base-4.3.1.0-f520cd232cc386346843c4a12b63f44b wired-in
> package rts mapped to builtin_rts wired-in package template-haskell
> mapped to template-haskell-2.5.0.0-7d9b1443ac5ab69e5ed705a487990deb
> wired-in package dph-seq not found. wired-in package dph-par not
> found. Hsc static flags: -static
> *** Deleting temp files: Deleting:
> *** Deleting temp dirs: Deleting: ghc.exe: no input files Usage: For basic information, try the `--help' option.
under Cygwin.*
I conjecture that something might be going on with stream fusion or loop fusion. Perhaps there is a rewrite rule buried deep in the Prelude that is firing in one case or not in the other. Or, because you don't say how much faster, you could simply be seeing cache effects.
If you want to know more, learn to fish:
Use ghc -ddump-simpl
to see the code that's actually being generated and compare it.
Use valgrind to count the number of instructions being executed.
If nothing else, these tools will give you enough information that you can ask a more focused, detailed question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With