Skip to content

Instantly share code, notes, and snippets.

@sighingnow
Created May 3, 2018 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sighingnow/fdc38489e4eab6dabb2b1c39f079cefe to your computer and use it in GitHub Desktop.
Save sighingnow/fdc38489e4eab6dabb2b1c39f079cefe to your computer and use it in GitHub Desktop.
Benchmark on several implementations of `numericEnumFromThen` and `numericEnumFromThenTo`.
{-# LANGUAGE BangPatterns #-}
import Criterion.Main
import Weigh
numericEnumFromThen :: (Fractional a) => a -> a -> [a]
numericEnumFromThen n m = n `seq` m `seq` (n : numericEnumFromThen m (m+m-n))
numericEnumFromThenTo :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo e1 e2 e3
= takeWhile predicate (numericEnumFromThen e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
-------------------------------------------------------------------------------
numericEnumFromThen' :: (Fractional a) => a -> a -> [a]
numericEnumFromThen' n m = go 0
where
step = m - n
go !k = let n' = n + k * step
in n' `seq` (n' : go (k + 1))
numericEnumFromThenTo' :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo' e1 e2 e3
= takeWhile predicate (numericEnumFromThen' e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
-------------------------------------------------------------------------------
numericEnumFromThen'' :: (Fractional a) => a -> a -> [a]
numericEnumFromThen'' n m = go 0
where
step = m - n
-- See Note [Numeric Stability of Enumerating Floating Numbers]
go !k = let !n' = n + k * step
in n' `seq` (n' : go (k + 1))
numericEnumFromThenTo'' :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo'' e1 e2 e3
= takeWhile predicate (numericEnumFromThen'' e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
--------------------------------------------------------------------------------
numericEnumFromThen''' :: (Fractional a) => a -> a -> [a]
numericEnumFromThen''' n m = go 0 (m - n)
where
-- See Note [Numeric Stability of Enumerating Floating Numbers]
go !k !step = let n' = n + k * step
in n' `seq` (n' : go (k + 1) step)
numericEnumFromThenTo''' :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo''' e1 e2 e3
= takeWhile predicate (numericEnumFromThen''' e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
--------------------------------------------------------------------------------
numericEnumFromThen'''' :: (Fractional a) => a -> a -> [a]
numericEnumFromThen'''' n m = go 0 (m - n)
where
-- See Note [Numeric Stability of Enumerating Floating Numbers]
go !k !step = let !n' = n + k * step
in n' `seq` (n' : go (k + 1) step)
numericEnumFromThenTo'''' :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo'''' e1 e2 e3
= takeWhile predicate (numericEnumFromThen'''' e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
--------------------------------------------------------------------------------
-- norm :: [Double] -> Double
-- norm = sqrt . sum . map (\x -> x*x)
-- -- main :: IO ()
-- -- main = do
-- -- print (norm (numericEnumFromThenTo''' 0 1 10000000) > 100)
-- -- -- print (norm (numericEnumFromThenTo' 0 1 10000000) > 100)
main :: IO ()
main = benchTime
-- main = benchMem
benchTime :: IO ()
benchTime = defaultMain [
bgroup "enum" [
bench "step-in-where" $ whnf (sum . numericEnumFromThenTo' 0 1) (10000000 :: Double)
, bench "step-in-where-bang-let" $ whnf (sum . numericEnumFromThenTo'' 0 1) (10000000 :: Double)
, bench "step-pass-around" $ whnf (sum . numericEnumFromThenTo''' 0 1) (10000000 :: Double)
, bench "step-pass-around-bang-let" $ whnf (sum . numericEnumFromThenTo'''' 0 1) (10000000 :: Double)
, bench "original-version" $ whnf (sum . numericEnumFromThenTo 0 1) (10000000 :: Double)
]
]
benchMem :: IO ()
benchMem = mainWith $ do
func "step-in-where" (sum . numericEnumFromThenTo' 0 1) (10000000 :: Double)
func "step-in-where-bang-let" (sum . numericEnumFromThenTo'' 0 1) (10000000 :: Double)
func "step-pass-around" (sum . numericEnumFromThenTo''' 0 1) (10000000 :: Double)
func "step-pass-around-bang-let" (sum . numericEnumFromThenTo'''' 0 1) (10000000 :: Double)
func "original-version" (sum . numericEnumFromThenTo 0 1) (10000000 :: Double)
Case Allocated GCs
step-in-where 640,000,200 610
step-in-where-bang-let 640,000,200 610
step-pass-around 720,000,216 691
step-pass-around-bang-let 720,000,216 691
original-version 720,000,216 691
benchmarking enum/step-in-where
time 50.38 ms (50.00 ms .. 50.75 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 50.90 ms (50.65 ms .. 51.53 ms)
std dev 532.0 us (180.1 us .. 792.3 us)
variance introduced by outliers: 11% (moderately inflated)
benchmarking enum/step-in-where-bang-let
time 43.10 ms (41.89 ms .. 44.46 ms)
0.998 R² (0.994 R² .. 1.000 R²)
mean 43.60 ms (43.20 ms .. 44.57 ms)
std dev 927.7 us (294.4 us .. 1.541 ms)
benchmarking enum/step-pass-around
time 45.99 ms (45.51 ms .. 46.66 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 46.53 ms (46.20 ms .. 47.25 ms)
std dev 711.8 us (256.3 us .. 1.100 ms)
variance introduced by outliers: 11% (moderately inflated)
benchmarking enum/step-pass-around-bang-let
time 43.59 ms (43.07 ms .. 44.33 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 43.56 ms (43.32 ms .. 43.79 ms)
std dev 371.4 us (249.2 us .. 617.4 us)
benchmarking enum/original-version
time 42.78 ms (42.09 ms .. 43.17 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 43.22 ms (42.92 ms .. 43.61 ms)
std dev 520.9 us (356.3 us .. 692.5 us)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment