benchmarking - Haskell: How to benchmark a computation accurately with deepseq/force -
i have web server written in haskell computes data in multiple steps.
i want accurately measure , display how long each action takes.
in presence of laziness, way this?
note "benchmarking" not quite right terminology since only want measure time in production system , not sample many runs. know case can use criterion.
you can use force
control.deepseq
evaluate data structure (and demand , measure computation).
one problem forcing large data structure takes time itself!
this because deepseq
(used force
) walk down algebraic data type tree, visiting every node (but not doing it).
when perform cheap operation each node, such map (*2) mylist
, , try measure how long takes, overhead can become significant, messing measurements.
import control.deepseq import control.exception (evaluate) import data.time (diffutctime, getcurrenttime) -- | measures how long computation takes, printing both time , -- overhead of `force` stdout. forces *twice*. benchmarkforce :: nfdata => string -> io -> io benchmarkforce msg action = before <- getcurrenttime -- force first time measure computation + forcing result <- evaluate . force =<< action after <- getcurrenttime -- force again see how long forcing takes _ <- evaluate . force $ result afteragain <- getcurrenttime putstrln $ msg ++ ": " ++ show (difftimems before after) ++ " ms" ++ " (force time: " ++ show (difftimems after afteragain) ++ " ms)" return result -- time difference `t2 - t1` in milliseconds difftimems t1 t2 = realtofrac (t2 `diffutctime` t1) * 1000.0 :: double
the first evaluate . force
run make sure action
, return value evaluated entirely.
by doing second force
run on result, can measure how overhead added first traversal.
this of course comes @ expense of 2 traversals; being able measure how time deepseq
wastes requires waste time twice.
here example measure pure functions that:
main :: io () main = l <- benchmarkforce "create list" $ return [1..10000000 :: integer] _ <- benchmarkforce "double each list element" $ return $ map (*2) l _ <- benchmarkforce "map id l" $ return $ map id l return ()
(of course works functions in io.)
the output:
create list: 1091.936 ms (force time: 71.33200000000001 ms) double each list element: 1416.0569999999998 ms (force time: 96.808 ms) map id l: 484.493 ms (force time: 67.232 ms)
as can see, force
creates around 13% overhead in map id l
case.
Comments
Post a Comment