{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-} {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE NoImplicitPrelude #-} module Main where import Data.Scientific import Formatting import Options.Generic import Perf import Perf.Analysis import Protolude hiding ((%)) import qualified Data.Map as Map import qualified Data.Text as Text import qualified Data.Vector as V import qualified Data.Vector.Storable as S import qualified Data.Vector.Unboxed as U data Opts = Opts { runs :: Maybe Int -- "number of runs" , sumTo :: Maybe Int -- "sum to this number" , sumsTo :: Maybe [Int] -- "sum to these numbers" } deriving (Generic, Show) instance ParseField [Int] instance ParseRecord Opts -- | useful as a benchmark versus the `ticks` functions being imported ticksInFile :: (NFData b) => Int -> (a -> b) -> a -> IO ([Cycle], b) ticksInFile n0 f a = go f a n0 [] where go f a n ts | n <= 0 = pure (ts, f a) | otherwise = do (t,_) <- tick f a go f a (n - 1) (t:ts) -- | interposition of a lambda fLambda :: Int -> Int fLambda = \x -> foldl' (+) 0 [1 .. x] -- | monomorphic version fMono :: Int -> Int fMono x = foldl' (+) 0 [1 .. x] -- | polymorphic function version fPoly :: (Integral b) => b -> b fPoly x = foldl' (+) 0 [1 .. x] main :: IO () main = do o :: Opts <- getRecord "perf examples" let !n = fromMaybe 1000 (runs o) let !a = fromMaybe 1000 (sumTo o) let !as = [1, 10, 100, 1000 :: Int] writeFile "other/run.md" $ code [ formatInt "number of runs:" 2 n , formatInt "accumulate to:" 2 a , sformat ((right 24 ' ' %. stext)%stext) "function:" "foldl' (+) 0" ] -- | tick_ testing prewarmup <- tick_ _ <- warmup 100 onetick <- tick_ ticks' <- replicateM 10 tick_ manyticks <- replicateM 1000000 tick_ let avticks = average manyticks let qticks = deciles 5 manyticks let tick999 = percentile 0.999 manyticks let tick99999 = percentile 0.99999 manyticks let tick99 = percentile 0.99 manyticks let tick40 = percentile 0.4 manyticks writeFile "other/tick_.md" $ code [ "pre warmup: " <> show prewarmup <> " cycles" , "one tick_: " <> show onetick <> " cycles" , "next 10: " <> show ticks' , "average over 1m: " <> sformat (fixed 2) avticks <> " cycles" , "99.999% perc: " <> sformat commas (floor tick99999 :: Integer) , "99.9% perc: " <> sformat (fixed 2) tick999 , "99th perc: " <> sformat (fixed 2) tick99 , "40th perc: " <> sformat (fixed 2) tick40 , "[min, 20th, .. 80th, max]:" , mconcat (sformat (" " % prec 4) . fromFloatDigits <$> qticks) ] -- | tick tests (t, resultPrime) <- tick fMono a print resultPrime (t2,_) <- tick fMono a (t3,_) <- tick fMono a (t',_) <- tick' fMono a (tio,_) <- tickIO (pure $ fMono a) -- mystery: if tickNoinline is used at all, all tick computations slow down -- by a factor of 2 (or by a function call) -- (tn,_) <- tickNoinline fMono a t10 <- sequence $ replicate 10 (tick fMono a) t10io <- sequence $ replicate 10 (tickIO $ pure (fMono a)) -- t10n <- sequence $ replicate 10 (tickNoinline fMono a) t10' <- sequence $ replicate 10 (tick fMono a) writeFile "other/tick.md" $ code [ "sum to " <> show a , "first measure: " <> show t <> " cycles" , "second measure: " <> show t2 <> " cycles" , "third measure: " <> show t3 <> " cycles" , "tick': " <> show t' <> " cycles" , "tickIO: " <> show tio <> " cycles" -- , "tickNoinline: " <> show tn <> " cycles" , "tick * 10: " <> show (fst <$> t10) , "tickIO * 10: " <> show (fst <$> t10io) -- , "tickNoinline * 10: " <> show (fst <$> t10n) , "tick * 10: " <> show (fst <$> t10') ] -- | ticks and ticksIO (rpure, _) <- ticks n fMono a (rpurePoly, _) <- ticks n fPoly a (rpureLambda, _) <- ticks n fLambda a (rio, _) <- ticksIO n (pure $ fMono a) (rioPoly, _) <- ticksIO n (pure $ fPoly a) (rioLambda, _) <- ticksIO n (pure $ fLambda a) writeFile "other/ticks.md" $ code [ "acc = " <> show a <> " n = " <> show n , formatRunHeader , formatRun "monomorphic" 2 $ rpure , formatRun "includes lambda" 2 $ rpureLambda , formatRun "polymorphic" 2 $ rpurePoly , formatRun "ticksIO mono" 2 $ rio , formatRun "ticksIO lambda" 2 $ rioLambda , formatRun "ticksIO poly" 2 $ rioPoly ] gaps <- sequence $ (\a -> formatGap a <$> tickIO (ticks n fPoly a)) <$> as writeFile "other/ticksCost.md" $ code gaps -- | ns testing css <- fmap (fmap fst) <$> sequence ((replicateM n . tick fMono) <$> as) let r12 = "(replicateM n . tick fMono) <$> as: " <> mconcat (sformat (" " %prec 3) <$> (fromFloatDigits . percentile 0.4 <$> css)) (ts, _) <- ns (ticks n fMono) as let r13 = "ns (ticks n fMono) as: " <> mconcat (sformat (" " %prec 3) <$> (fromFloatDigits . percentile 0.4 <$> ts)) writeFile "other/tickns.md" $ code ["sum to's " <> show as, r13, r12] -- | vectors let asl :: [Int] asl = [1 .. a] let suml :: [Int] -> Int suml = foldl' (+) 0 (rlist, _) <- ticks n suml asl let sumv :: V.Vector Int -> Int sumv = V.foldl (+) 0 let asv :: V.Vector Int = V.generate a identity (rboxed, _) <- ticks n sumv asv let sums :: S.Vector Int -> Int sums = S.foldl (+) 0 let ass :: S.Vector Int = S.generate a identity (rstorable, _) <- ticks n sums ass let sumu :: U.Vector Int -> Int sumu = U.foldl (+) 0 let asu :: U.Vector Int = U.generate a identity (runboxed, _) <- ticks n sumu asu writeFile "other/vector.md" $ code [ "sum to " <> show a , formatRun "ticks list" 2 $ rlist , formatRun "ticks boxed" 2 $ rboxed , formatRun "ticks storable" 2 $ rstorable , formatRun "ticks unboxed" 2 $ runboxed ] -- WHNF (rnf, _) <- tick (fmap fMono) (Just a) (rwhnf, _) <- tick (fmap fMono) (Just a) (rnfs, _) <- ticks n (fmap fMono) (Just a) (rwhnfs, _) <- ticksWHNF n (fmap fMono) (Just a) (rnfio, _) <- tickIO (pure $ fmap fMono (Just a)) (rwhnfio, _) <- tickWHNFIO (pure $ fmap fMono (Just a)) (rnfsio, _) <- ticksIO n (pure $ fmap fMono (Just a)) (rwhnfsio, _) <- ticksWHNFIO n (pure $ fmap fMono (Just a)) writeFile "other/whnf.md" $ code [ "sum to " <> show a , formatInt "tick" 2 $ rnf , formatInt "tickWHNF" 2 $ rwhnf , formatRun "ticks" 2 rnfs , formatRun "ticksWHNF" 2 rwhnfs , formatInt "tickIO" 2 rnfio , formatInt "tickWHNFIO" 2 rwhnfio , formatRun "ticksIO" 2 rnfsio , formatRun "ticksWHNFIO" 2 rwhnfsio ] -- perf basics (result, cs) <- runPerfT $ perf "sum" cycles (pure $ foldl' (+) 0 [0 .. a]) putStrLn (show (result, cs) :: Text) -- PerfT example -- prior to Perfification result <- do txt <- readFile "perf-analysis/examples/examples.hs" let n = Text.length txt let x = foldl' (+) 0 [1..n] putStrLn $ "sum of one to number of characters is: " <> (show x :: Text) pure (n, x) -- post-Perfification (result', ms) <- runPerfT $ do txt <- perf "file read" cycles $ readFile "perf-analysis/examples/examples.hs" n <- perf "length" cycles $ pure (Text.length txt) x <- perf "sum" cycles $ pure (foldl' (+) 0 [1..n]) perf "print to screen" cycles $ putStrLn $ "sum of one to number of characters is: " <> (show x :: Text) pure (n, x) when (result == result') $ print "PerfT has no effect on original computation" let fmt = sformat ((right 40 ' ' %. stext) %prec 3 % " " % stext) writeFile "other/perf.md" $ "\nperf cycle measurements\n---\n" <> code ((\(t,c) -> fmt t (int2Sci c) "cycles") <$> Map.toList ms)