Last active
September 26, 2017 15:39
-
-
Save eborden/6eefd88ef234c4a8540724f8dfa5a170 to your computer and use it in GitHub Desktop.
Benchmark various folding averages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main (main) where | |
import qualified Control.Foldl as Fold | |
import Criterion.Main | |
import qualified Data.List | |
import Data.Maybe | |
import Data.Semigroup | |
naiveAvg, foldMapAvg, foldlAvg, foldAvg :: [Float] -> Float | |
naiveAvg xs = Prelude.sum xs / fromIntegral (Prelude.length xs) | |
foldMapAvg = fromMaybe 0 . getAverage . foldMap averageDatum | |
foldlAvg = fromMaybe 0 . getAverage . Data.List.foldl' (\acc x -> acc <> averageDatum x) mempty | |
foldAvg = Fold.fold ((\s l -> s / fromIntegral l) <$> Fold.sum <*> Fold.length) | |
main :: IO () | |
main = defaultMain | |
[ bgroup "naiveAvg" | |
[ bench "1000000" $ nf naiveAvg [1..1000000] | |
] | |
, bgroup "foldMapAvg" | |
[ bench "1000000" $ nf foldMapAvg [1..1000000] | |
] | |
, bgroup "foldlAvg" | |
[ bench "1000000" $ nf foldlAvg [1..1000000] | |
] | |
, bgroup "foldAvg" | |
[ bench "1000000" $ nf foldAvg [1..1000000] | |
] | |
] | |
data Average n = Average { length :: !Int, sum :: !n } | |
getAverage :: (Num n, Fractional n) => Average n -> Maybe n | |
getAverage (Average l n) = | |
if l == 0 | |
then Nothing | |
else Just $ n / fromIntegral l | |
averageDatum :: n -> Average n | |
averageDatum = Average 1 | |
instance Num n => Semigroup (Average n) where | |
Average lx nx <> Average ly ny = Average (lx + ly) (nx + ny) | |
instance Num n => Monoid (Average n) where | |
mappend = (<>) | |
mempty = Average 0 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
benchmarking naiveAvg/1000000 | |
time 8.569 ms (8.523 ms .. 8.606 ms) | |
1.000 R² (1.000 R² .. 1.000 R²) | |
mean 8.586 ms (8.565 ms .. 8.625 ms) | |
std dev 78.99 μs (54.47 μs .. 126.1 μs) | |
benchmarking foldMapAvg/1000000 | |
time 48.14 ms (44.37 ms .. 50.39 ms) | |
0.984 R² (0.960 R² .. 0.996 R²) | |
mean 49.41 ms (47.33 ms .. 52.64 ms) | |
std dev 4.895 ms (3.159 ms .. 6.827 ms) | |
variance introduced by outliers: 37% (moderately inflated) | |
benchmarking foldlAvg/1000000 | |
time 4.331 ms (4.289 ms .. 4.392 ms) | |
0.999 R² (0.998 R² .. 1.000 R²) | |
mean 4.324 ms (4.306 ms .. 4.347 ms) | |
std dev 66.30 μs (46.57 μs .. 90.51 μs) | |
benchmarking foldAvg/1000000 | |
time 23.81 ms (21.19 ms .. 25.69 ms) | |
0.958 R² (0.868 R² .. 0.997 R²) | |
mean 24.46 ms (23.12 ms .. 25.22 ms) | |
std dev 2.182 ms (1.161 ms .. 3.652 ms) | |
variance introduced by outliers: 35% (moderately inflated) |
Author
eborden
commented
Sep 26, 2017
For extra fun if you utilize Average
with Fold
then you get the exact same performance as the foldl'
variant:
foldAvgWithMon = fromMaybe 0 . getAverage . Fold.fold (Average <$> Fold.length <*> Fold.sum)
benchmarking foldAvgWithMon/1000000
time 4.311 ms (4.295 ms .. 4.333 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 4.313 ms (4.296 ms .. 4.334 ms)
std dev 56.77 μs (36.97 μs .. 71.66 μs)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment