perf 0.12.0.1 → 0.13.0.0
raw patch · 12 files changed
+1421/−896 lines, 12 filesdep +clockdep −gaugedep −rdtscdep −timePVP ok
version bump matches the API change (PVP)
Dependencies added: clock
Dependencies removed: gauge, rdtsc, time
API changes (from Hackage documentation)
- Perf.Stats: StatSecs :: StatDType
- Perf.Stats: averageSecs :: [Double] -> Double
- Perf.Time: Cycles :: Word64 -> Cycles
- Perf.Time: [word] :: Cycles -> Word64
- Perf.Time: clocktime :: StepMeasure IO Double
- Perf.Time: cputime :: StepMeasure IO Integer
- Perf.Time: instance GHC.Base.Monoid Perf.Time.Cycles
- Perf.Time: instance GHC.Base.Semigroup Perf.Time.Cycles
- Perf.Time: instance GHC.Classes.Eq Perf.Time.Cycles
- Perf.Time: instance GHC.Classes.Ord Perf.Time.Cycles
- Perf.Time: instance GHC.Enum.Enum Perf.Time.Cycles
- Perf.Time: instance GHC.Num.Num Perf.Time.Cycles
- Perf.Time: instance GHC.Read.Read Perf.Time.Cycles
- Perf.Time: instance GHC.Real.Integral Perf.Time.Cycles
- Perf.Time: instance GHC.Real.Real Perf.Time.Cycles
- Perf.Time: instance GHC.Show.Show Perf.Time.Cycles
- Perf.Time: newtype Cycles
+ Perf.Report: [reportClock] :: ReportOptions -> Clock
+ Perf.Time: defaultClock :: Clock
+ Perf.Time: nanos :: IO Nanos
+ Perf.Time: nanosWith :: Clock -> IO Nanos
+ Perf.Time: tickWith :: Clock -> (a -> b) -> a -> IO (Nanos, b)
+ Perf.Time: timesWith :: Clock -> Int -> Measure IO [Nanos]
+ Perf.Time: toSecs :: Nanos -> Double
+ Perf.Time: type Nanos = Integer
- Perf.Measure: measureDs :: MeasureType -> Int -> Measure IO [[Double]]
+ Perf.Measure: measureDs :: MeasureType -> Clock -> Int -> Measure IO [[Double]]
- Perf.Report: ReportOptions :: Int -> StatDType -> MeasureType -> Golden -> Header -> CompareLevels -> ReportOptions
+ Perf.Report: ReportOptions :: Int -> Clock -> StatDType -> MeasureType -> Golden -> Header -> CompareLevels -> ReportOptions
- Perf.Time: stepTime :: StepMeasure IO Cycles
+ Perf.Time: stepTime :: StepMeasure IO Nanos
- Perf.Time: tick :: (a -> b) -> a -> IO (Cycles, b)
+ Perf.Time: tick :: (a -> b) -> a -> IO (Nanos, b)
- Perf.Time: tickForce :: (NFData a, NFData b) => (a -> b) -> a -> IO (Cycles, b)
+ Perf.Time: tickForce :: (NFData a, NFData b) => (a -> b) -> a -> IO (Nanos, b)
- Perf.Time: tickForceArgs :: NFData a => (a -> b) -> a -> IO (Cycles, b)
+ Perf.Time: tickForceArgs :: NFData a => (a -> b) -> a -> IO (Nanos, b)
- Perf.Time: tickIO :: IO a -> IO (Cycles, a)
+ Perf.Time: tickIO :: IO a -> IO (Nanos, a)
- Perf.Time: tickLazy :: (a -> b) -> a -> IO (Cycles, b)
+ Perf.Time: tickLazy :: (a -> b) -> a -> IO (Nanos, b)
- Perf.Time: tickWHNF :: (a -> b) -> a -> IO (Cycles, b)
+ Perf.Time: tickWHNF :: (a -> b) -> a -> IO (Nanos, b)
- Perf.Time: tick_ :: IO Cycles
+ Perf.Time: tick_ :: IO Nanos
- Perf.Time: ticks :: Int -> (a -> b) -> a -> IO ([Cycles], b)
+ Perf.Time: ticks :: Int -> (a -> b) -> a -> IO ([Nanos], b)
- Perf.Time: ticksIO :: Int -> IO a -> IO ([Cycles], a)
+ Perf.Time: ticksIO :: Int -> IO a -> IO ([Nanos], a)
- Perf.Time: time :: Measure IO Cycles
+ Perf.Time: time :: Measure IO Nanos
- Perf.Time: times :: Int -> Measure IO [Cycles]
+ Perf.Time: times :: Int -> Measure IO [Nanos]
Files
- ChangeLog.md +4/−0
- app/explore.hs +13/−30
- perf.cabal +12/−62
- readme.md +1246/−0
- readme.org +0/−670
- src/Perf.hs +4/−4
- src/Perf/BigO.hs +1/−11
- src/Perf/Measure.hs +4/−3
- src/Perf/Report.hs +24/−3
- src/Perf/Stats.hs +1/−9
- src/Perf/Time.hs +109/−98
- src/Perf/Types.hs +3/−6
ChangeLog.md view
@@ -1,3 +1,7 @@+0.13+===+* replaced rdtsc with clocks library.+ 0.12.0.0 === * added reportMain and support for executable development.
app/explore.hs view
@@ -12,12 +12,11 @@ import Data.Text (Text) import Data.Text qualified as Text import Data.Text.IO qualified as Text-import Gauge import Options.Applicative import Perf import Prelude -data RunType = RunExample | RunExamples | RunNub | RunExampleIO | RunSums | RunLengths | RunGauge | RunNoOps | RunTicks deriving (Eq, Show)+data RunType = RunExample | RunExamples | RunClocks | RunNub | RunExampleIO | RunSums | RunLengths | RunNoOps | RunTicks deriving (Eq, Show) data ExploreOptions = ExploreOptions { exploreReportOptions :: ReportOptions,@@ -32,12 +31,12 @@ flag' RunSums (long "sums" <> help "run on sum algorithms") <|> flag' RunLengths (long "lengths" <> help "run on length algorithms") <|> flag' RunNub (long "nub" <> help "nub test")+ <|> flag' RunClocks (long "clocks" <> help "clock test") <|> flag' RunExamples (long "examples" <> help "run on example algorithms") <|> flag' RunExample (long "example" <> help "run on the example algorithm") <|> flag' RunExampleIO (long "exampleIO" <> help "exampleIO test") <|> flag' RunNoOps (long "noops" <> help "noops test") <|> flag' RunTicks (long "ticks" <> help "tick test")- <|> flag' RunGauge (long "gauge" <> help "gauge runs on exmaple for comparison") <|> pure RunExample exploreOptions :: Parser ExploreOptions@@ -91,26 +90,6 @@ fap "const" (const ()) () fam "pure" (pure ()) --- | * gauge experiment-testGauge ::- (NFData b) =>- Text ->- (a -> b) ->- a ->- IO ()-testGauge label f a = do- Text.putStrLn label- benchmarkWith defaultConfig (whnf f a)- benchmarkWith defaultConfig (nf f a)--testGaugeExample :: ExamplePattern Int -> IO ()-testGaugeExample (PatternSumFuse label f a) = testGauge label f a-testGaugeExample (PatternSum label f a) = testGauge label f a-testGaugeExample (PatternLengthF label f a) = testGauge label f a-testGaugeExample (PatternConstFuse label f a) = testGauge label f a-testGaugeExample (PatternMapInc label f a) = testGauge label f a-testGaugeExample (PatternNoOp label f a) = testGauge label f a- main :: IO () main = do o <- execParser exploreOpts@@ -118,6 +97,7 @@ let n = reportN repOptions let s = reportStatDType repOptions let mt = reportMeasureType repOptions+ let c = reportClock repOptions let !l = exploreLength o let a = exploreExample o let r = exploreRun o@@ -133,27 +113,30 @@ repOptions (intercalate "-" [show r, show l]) (statExamples l)+ RunClocks -> do+ reportMainWith+ repOptions+ (intercalate "-" [show r, show a, show l, show c])+ (testExample (examplePattern a l)) RunExampleIO -> do- m1 <- execPerfT (measureDs mt 1) exampleIO- (_, (m', m2)) <- outer "outer-total" (measureDs mt 1) (measureDs mt 1) exampleIO+ m1 <- execPerfT (measureDs mt c 1) exampleIO+ (_, (m', m2)) <- outer "outer-total" (measureDs mt c 1) (measureDs mt c 1) exampleIO let ms = mconcat [Map.mapKeys (\x -> ["normal", x]) m1, Map.mapKeys (\x -> ["outer", x]) (m2 <> m')] putStrLn "" let o' = replaceDefaultFilePath (intercalate "-" [show r, show mt, show s]) repOptions report o' (fmap (statD s) <$> ms) RunSums -> do- m <- statSums n l (measureDs mt)+ m <- statSums n l (measureDs mt c) let o' = replaceDefaultFilePath (intercalate "-" [show r, show mt, show n, show l, show s]) repOptions report o' (statify s m) RunLengths -> do- m <- statLengths n l (measureDs mt)+ m <- statLengths n l (measureDs mt c) let o' = replaceDefaultFilePath (intercalate "-" [show r, show mt, show n, show l, show s]) repOptions report o' (statify s m) RunNoOps -> do- m <- perfNoOps (measureDs mt n)+ m <- perfNoOps (measureDs mt c n) let o' = replaceDefaultFilePath (intercalate "-" [show r, show mt, show n]) repOptions report o' (allStats 4 (Map.mapKeys (: []) m)) RunTicks -> do m <- statTicksSums n l s reportOrg2D (fmap (expt (Just 3)) m)- RunGauge -> do- mapM_ testGaugeExample ((`examplePattern` l) <$> allExamples)
perf.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: perf-version: 0.12.0.1+version: 0.13.0.0 license: BSD-3-Clause license-file: LICENSE copyright: Tony Day (c) 2018@@ -9,16 +9,20 @@ maintainer: tonyday567@gmail.com homepage: https://github.com/tonyday567/perf#readme bug-reports: https://github.com/tonyday567/perf/issues-synopsis: Low-level run time measurement.+synopsis: Performance methods and monad. description: A set of tools to measure performance of Haskell programs.- See the Perf module for an example and full API documentation.+ See the Perf module and readme for an example and full API documentation. build-type: Simple-tested-with: GHC == 8.10 || ==9.2 || ==9.4 || ==9.6 || ==9.8+tested-with:+ , GHC == 9.10.1+ , GHC == 9.8.2+ , GHC == 9.6.5+ extra-doc-files: ChangeLog.md- readme.org+ readme.md source-repository head type: git@@ -43,60 +47,7 @@ -Wredundant-constraints common ghc2021-stanza- if impl ( ghc >= 9.2 )- default-language: GHC2021-- if impl ( ghc < 9.2 )- default-language: Haskell2010- default-extensions:- BangPatterns- BinaryLiterals- ConstrainedClassMethods- ConstraintKinds- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveLift- DeriveTraversable- DoAndIfThenElse- EmptyCase- EmptyDataDecls- EmptyDataDeriving- ExistentialQuantification- ExplicitForAll- FlexibleContexts- FlexibleInstances- ForeignFunctionInterface- GADTSyntax- GeneralisedNewtypeDeriving- HexFloatLiterals- ImplicitPrelude- InstanceSigs- KindSignatures- MonomorphismRestriction- MultiParamTypeClasses- NamedFieldPuns- NamedWildCards- NumericUnderscores- PatternGuards- PolyKinds- PostfixOperators- RankNTypes- RelaxedPolyRec- ScopedTypeVariables- StandaloneDeriving- StarIsType- TraditionalRecordSyntax- TupleSections- TypeApplications- TypeOperators- TypeSynonymInstances-- if impl ( ghc < 9.2 ) && impl ( ghc >= 8.10 )- default-extensions:- ImportQualifiedPost- StandaloneKindSignatures+ default-language: GHC2021 library import: ghc-options-stanza@@ -104,16 +55,15 @@ hs-source-dirs: src build-depends: , base >=4.7 && <5+ , clock >=0.8 && <0.9 , containers >=0.6 && <0.8 , deepseq >=1.4.4 && <1.6 , formatn >=0.2.1 && <0.4 , mtl >=2.2.2 && <2.4 , numhask-space >=0.10 && <0.12 , optparse-applicative >=0.17 && <0.19- , rdtsc >=1.3 && <1.4 , recursion-schemes >=5.2.2 && <5.3 , text >=1.2 && <2.2- , time >=1.9 && <1.13 , vector >=0.12.3 && <0.14 exposed-modules: Perf@@ -136,10 +86,10 @@ hs-source-dirs: app build-depends: , base >=4.7 && <5+ , clock >=0.8 && <0.9 , containers >=0.6 && <0.8 , deepseq >=1.4.4 && <1.6 , formatn >=0.2.1 && <0.4- , gauge >=0.2.5 && <0.3 , mtl >=2.2.2 && <2.4 , optparse-applicative >=0.17 && <0.19 , perf
+ readme.md view
@@ -0,0 +1,1246 @@++# Table of Contents++1. [Introduction](#orga196864)+2. [Setup](#orgfd30244)+3. [System.Clock](#orgb51effe)+ 1. [resolution](#org7dcd69d)+4. [Time](#org6c19f0f)+ 1. [What is a tick?](#org49fb855)+ 2. [tick\_](#org1de7ebb)+ 3. [multiple ticks](#org27958a7)+ 4. [tickIO](#orga206cb6)+ 5. [sum example](#orgd6c8625)+5. [PerfT](#org0974e4d)+6. [perf-explore](#org216f105)+ 1. [noops](#org2d42223)+ 2. [measurement context](#org95a9062)+ 1. [short list](#orgdb37d7c)+ 2. [long list](#org56b0098)+ 3. [sums](#orgff01033)+ 4. [lengths](#org5abd0c1)+ 5. [Space](#org01bde6f)+7. [Perf.BigO](#org753786d)+8. [References](#org47311bd)+ 1. [Core](#orgadd7f60)+ 2. [Profiling](#org31e588b)+ 1. [setup](#orgf72792c)+ 2. [Space usage output (-s)](#org1d9ca37)+ 3. [Cost center profile (-p)](#orgeb93acc)+ 4. [heap analysis (-hc -l)](#org76c2a10)+ 3. [Cache speed](#org3a53ed0)++[](https://hackage.haskell.org/package/perf) [](https://github.com/tonyday567/perf/actions?query=workflow%3Ahaskell-ci)+++<a id="orga196864"></a>++# Introduction++`perf` provides some ideas, code and a library for low-level performance measurement for Haskell hacking. The library:++- provides a monad transformer, `PerfT`, as a light-weight wrapper for use on existing code. `PerfT` modifications can be included in code bases, as opposed to performance being separated code and process, with any effects able to be erased at compile time with `evalPerfT`.++- focuses on fast and accurate measurement.++- is polymorphic to what, exactly, is being measured, so that concepts such as counters, debug checks, time and space performance can share treatment.++- can measure big O for algorithms that can be defined in terms of input size growth.+++<a id="orgfd30244"></a>++# Setup++Note that running perf.org is very slow compared with an external process which accesses the compiled version of the library.++ :r+ :set -Wno-type-defaults+ :set -Wno-unused-do-bind+ :set -Wno-name-shadowing+ :set -XOverloadedStrings+ :set -XOverloadedLabels+ import Perf+ import Data.FormatN+ import qualified Data.Text as Text+ import qualified Data.Text.IO as Text+ import qualified Data.Map.Strict as Map+ import Control.Monad+ import Data.Bifunctor+ import System.Clock+ putStrLn "ok"++ [ 1 of 10] Compiling Perf.Stats ( src/Perf/Stats.hs, interpreted ) [Source file changed]+ [ 3 of 10] Compiling Perf.Time ( src/Perf/Time.hs, interpreted ) [Source file changed]+ [ 6 of 10] Compiling Perf.Measure ( src/Perf/Measure.hs, interpreted ) [Source file changed]+ [ 7 of 10] Compiling Perf.Report ( src/Perf/Report.hs, interpreted ) [Source file changed]+ [ 8 of 10] Compiling Perf.BigO ( src/Perf/BigO.hs, interpreted ) [Perf.Stats changed]+ [10 of 10] Compiling Perf ( src/Perf.hs, interpreted ) [Perf.BigO changed]+ Ok, 10 modules reloaded.+ ok+++<a id="orgb51effe"></a>++# System.Clock++The default clock is MonoticRaw for linux & macOS, and ThreadCPUTime for Windows.+++<a id="org7dcd69d"></a>++## resolution++ getRes Monotonic+ getRes Realtime+ getRes ProcessCPUTime+ getRes ThreadCPUTime+ getRes MonotonicRaw++ TimeSpec {sec = 0, nsec = 1000}+ TimeSpec {sec = 0, nsec = 1000}+ TimeSpec {sec = 0, nsec = 1000}+ TimeSpec {sec = 0, nsec = 42}+ TimeSpec {sec = 0, nsec = 42}+++<a id="org6c19f0f"></a>++# Time+++<a id="org49fb855"></a>++## What is a tick?++A fundamental operation of Perf.Time is tick, which sandwiches a (strict) function application between two readings of a clock, and returns time in nanoseconds, and the computation result. In this way, the \`Perf\` monad can be inserted into the midst of a computation in an attempt to measure performance in-situ as opposed to sitting off in a separate and decontextualized process.++ :t tick++ tick :: (a -> b) -> a -> IO (Nanos, b)++`tick` returns in the IO monad, because reading a cycle counter is an IO effect. A trivial but fundamental point is that performance measurement effects the computation being measured.+++<a id="org1de7ebb"></a>++## tick\_++tick\_ measures the nanoseconds between two immediate clock reads.++ :t tick_++ tick_ :: IO Nanos++ replicateM 10 tick_++ [1833,500,416,416,416,375,375,416,416,416]+++<a id="org27958a7"></a>++## multiple ticks++ fmap (fmap (fst)) . replicateM 10 $ tick (const ()) ()++ [7000,2333,2000,2208,1958,1959,1959,2000,2000,1959]++Here, `const () ()` was evaluated and took 7 micro-seconds for the first effect, reducing down to 2 msecs after 10 effects.+++<a id="orga206cb6"></a>++## tickIO++`tickIO` measures the evaluation of an IO value.++ :t tickIO++ tickIO :: IO a -> IO (Cycles, a)++ fmap (fmap fst) . replicateM 10 $ tickIO (pure ())++ [5541,1625,1458,1833,1375,1416,1375,1375,1375,1375]+++<a id="orgd6c8625"></a>++## sum example++ fmap (expt (Just 2) . fromIntegral) . fst <$> ticks 10 sum ([1..10000] :: [Double])++ ["5.0e5","2.4e5","2.4e5","2.4e5","2.4e5","2.4e5","2.4e5","2.4e5","2.5e5","2.4e5"]++ ts <- ticks 10000 sum ([1..1000] :: [Double])+ print $ average (fmap fromIntegral $ fst ts)++ 10747.1975+++<a id="org0974e4d"></a>++# PerfT++`PerfT` allows for multiple measurement points and is polymorphic in what is being measured. It returns a Map of results held in State.++Compare a lower-level usage of ticks, measuring the average of summing to one thousand over one thousand trials:++ first (average . fmap fromIntegral) <$> ticks 1000 sum [1..1000]++ (25947.635,500500)++… with PerfT usage++ second (fmap (average . fmap fromIntegral)) <$> runPerfT (times 1000) (sum |$| [1..1000])++ (500500,fromList [("",26217.098)])++Comparing performance of sum versus a list fusion approach:++ fmap (average . fmap fromIntegral) <$> (execPerfT (times 1000) $ do; (fap "sum" sum [1..1000]); (fap "fusion" (\x -> sum [1..x]) 1000))++ fromList [("fusion",32871.248),("sum",26924.128)]++An IO example++ exampleIO' :: IO ()+ exampleIO' = do+ txt <- Text.readFile "src/Perf.hs"+ let n = Text.length txt+ Text.putStrLn $ "length of file is: " <> Text.pack (show n)++ exampleIO = execPerfT time (do+ txt <- fam "file_read" (Text.readFile "src/Perf.hs")+ n <- fap "length" Text.length txt+ fam "print_result" (Text.putStrLn $ "length of file is: " <> Text.pack (show n)))++ perf-explore --exampleIO++ length of file is: 1794+ length of file is: 1794+ + label1 label2 label3 old result new result change+ + normal file-read time 2.31e5 1.28e5 improvement+ normal length time 2.71e3 2.00e3 improvement+ normal print-result time 3.75e4 1.32e4 improvement+ outer file-read time 6.05e4 3.64e4 improvement+ outer length time 9.59e2 6.25e2 improvement+ outer outer-total time 7.39e4 4.02e4 improvement+ outer print-result time 9.79e3 1.71e3 improvement+++<a id="org216f105"></a>++# perf-explore++`perf-explore` contains some exploratory routines used to develop `perf`++ perf-explore --help++ examples of perf usage+ + Usage: perf-explore [-n|--runs ARG]+ [--Monotonic | --Realtime | --ProcessCPUTime |+ --ThreadCPUTime | --MonotonicRaw]+ [--best | --median | --average]+ [--time | --space | --spacetime | --allocation | --count]+ [-g|--golden ARG] [--nocheck] [-r|--record]+ [--header | --noheader] [--error ARG] [--warning ARG]+ [--improved ARG]+ [--sums | --lengths | --nub | --clocks | --examples |+ --example | --exampleIO | --noops | --ticks]+ [-l|--length ARG]+ [--sumFuse | --sum | --lengthF | --constFuse | --mapInc |+ --noOp]+ + perf exploration+ + Available options:+ -n,--runs ARG number of runs to perform+ --best report upper decile+ --median report median+ --average report average+ --time measure time performance+ --space measure space performance+ --spacetime measure both space and time performance+ --allocation measure bytes allocated+ --count measure count+ -g,--golden ARG golden file name+ --nocheck do not check versus the golden file+ -r,--record record the result to the golden file+ --header include headers+ --noheader dont include headers+ --error ARG error level+ --warning ARG warning level+ --improved ARG improved level+ --sums run on sum algorithms+ --lengths run on length algorithms+ --nub nub test+ --clocks clock test+ --examples run on example algorithms+ --example run on the example algorithm+ --exampleIO exampleIO test+ --noops noops test+ --ticks tick test+ -l,--length ARG length of list+ --sumFuse fused sum pipeline+ --sum sum+ --lengthF foldr id length+ --constFuse fused const pipeline+ --mapInc fmap (+1)+ --noOp const ()+ -h,--help Show this help text++ fmap averageI <$> execPerfT (times 10000) (sum |$| [1..1000])++ fromList [("",136055.5594)]++The equivalent to the above code is:++ perf-explore -n 10000 -l 1000 --sum --nocheck++ label1 label2 results+ + sum time 6.32e3+++<a id="org2d42223"></a>++## noops++This no-op experiment is useful to understand the pure time performance of the machinery around measurement. It can be (re)run with:++ perf-explore --noops++ label1 label2 label3 old result new result change+ + const 1st time 1.72e4 8.79e3 improvement+ const 2nd time 2.09e2 1.25e2 improvement+ const 3rd time 1.66e2 1.25e2 improvement+ const 4th time 2.08e2 8.30e1 improvement+ const average time 2.08e2 1.10e2 improvement+ const best time 1.31e2 6.31e1 improvement+ const median time 1.60e2 7.76e1 improvement+ pure 1st time 1.00e3 1.25e2 improvement+ pure 2nd time 1.67e2 8.30e1 improvement+ pure 3rd time 1.66e2 8.30e1 improvement+ pure 4th time 1.25e2 4.20e1 improvement+ pure average time 1.85e2 8.29e1 improvement+ pure best time 1.31e2 6.37e1 improvement+ pure median time 1.63e2 7.79e1 improvement+++<a id="org95a9062"></a>++## measurement context++Exploration of how the code surrounding measurement effects performance.++ perf-explore -n 1000 -l 1000 --ticks --nocheck++<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">+++<colgroup>+<col class="org-left" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />+</colgroup>+<tbody>+<tr>+<td class="org-left"> </td>+<td class="org-right">stepTime</td>+<td class="org-right">tick</td>+<td class="org-right">tickForce</td>+<td class="org-right">tickForceArgs</td>+<td class="org-right">tickLazy</td>+<td class="org-right">tickWHNF</td>+<td class="org-right">times</td>+</tr>++<tr>+<td class="org-left">sumAux</td>+<td class="org-right">3.29e3</td>+<td class="org-right">4.83e3</td>+<td class="org-right">3.29e3</td>+<td class="org-right">3.29e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">3.92e3</td>+<td class="org-right">3.29e3</td>+</tr>++<tr>+<td class="org-left">sumCata</td>+<td class="org-right">5.86e3</td>+<td class="org-right">5.61e3</td>+<td class="org-right">6.00e3</td>+<td class="org-right">6.12e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">5.78e3</td>+<td class="org-right">5.86e3</td>+</tr>++<tr>+<td class="org-left">sumCo</td>+<td class="org-right">3.73e3</td>+<td class="org-right">4.63e3</td>+<td class="org-right">3.66e3</td>+<td class="org-right">3.66e3</td>+<td class="org-right">1.90e2</td>+<td class="org-right">4.36e3</td>+<td class="org-right">3.72e3</td>+</tr>++<tr>+<td class="org-left">sumCoCase</td>+<td class="org-right">5.08e3</td>+<td class="org-right">5.10e3</td>+<td class="org-right">4.96e3</td>+<td class="org-right">4.95e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">5.12e3</td>+<td class="org-right">5.11e3</td>+</tr>++<tr>+<td class="org-left">sumCoGo</td>+<td class="org-right">3.47e3</td>+<td class="org-right">4.74e3</td>+<td class="org-right">4.66e3</td>+<td class="org-right">4.64e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">4.72e3</td>+<td class="org-right">3.29e3</td>+</tr>++<tr>+<td class="org-left">sumF</td>+<td class="org-right">5.92e3</td>+<td class="org-right">4.85e3</td>+<td class="org-right">4.84e3</td>+<td class="org-right">6.41e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">4.85e3</td>+<td class="org-right">5.91e3</td>+</tr>++<tr>+<td class="org-left">sumFlip</td>+<td class="org-right">4.54e3</td>+<td class="org-right">4.45e3</td>+<td class="org-right">4.44e3</td>+<td class="org-right">4.44e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">4.44e3</td>+<td class="org-right">4.26e3</td>+</tr>++<tr>+<td class="org-left">sumFlipLazy</td>+<td class="org-right">4.52e3</td>+<td class="org-right">4.51e3</td>+<td class="org-right">4.47e3</td>+<td class="org-right">4.47e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">4.49e3</td>+<td class="org-right">4.50e3</td>+</tr>++<tr>+<td class="org-left">sumFoldr</td>+<td class="org-right">5.55e3</td>+<td class="org-right">4.78e3</td>+<td class="org-right">4.71e3</td>+<td class="org-right">4.72e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">4.77e3</td>+<td class="org-right">5.56e3</td>+</tr>++<tr>+<td class="org-left">sumFuse</td>+<td class="org-right">8.28e2</td>+<td class="org-right">8.33e2</td>+<td class="org-right">8.29e2</td>+<td class="org-right">8.29e2</td>+<td class="org-right">1.86e2</td>+<td class="org-right">8.28e2</td>+<td class="org-right">8.29e2</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldl’</td>+<td class="org-right">2.03e3</td>+<td class="org-right">8.29e2</td>+<td class="org-right">8.32e2</td>+<td class="org-right">8.29e2</td>+<td class="org-right">1.84e2</td>+<td class="org-right">8.29e2</td>+<td class="org-right">8.29e2</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldr</td>+<td class="org-right">1.17e3</td>+<td class="org-right">1.17e3</td>+<td class="org-right">1.18e3</td>+<td class="org-right">1.17e3</td>+<td class="org-right">1.84e2</td>+<td class="org-right">1.19e3</td>+<td class="org-right">1.17e3</td>+</tr>++<tr>+<td class="org-left">sumFusePoly</td>+<td class="org-right">8.40e2</td>+<td class="org-right">8.37e2</td>+<td class="org-right">8.35e2</td>+<td class="org-right">8.36e2</td>+<td class="org-right">1.84e2</td>+<td class="org-right">8.40e2</td>+<td class="org-right">8.37e2</td>+</tr>++<tr>+<td class="org-left">sumLambda</td>+<td class="org-right">3.67e3</td>+<td class="org-right">5.03e3</td>+<td class="org-right">3.67e3</td>+<td class="org-right">3.67e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">3.78e3</td>+<td class="org-right">3.67e3</td>+</tr>++<tr>+<td class="org-left">sumMono</td>+<td class="org-right">3.66e3</td>+<td class="org-right">5.13e3</td>+<td class="org-right">5.12e3</td>+<td class="org-right">7.20e3</td>+<td class="org-right">1.84e2</td>+<td class="org-right">5.13e3</td>+<td class="org-right">3.66e3</td>+</tr>++<tr>+<td class="org-left">sumPoly</td>+<td class="org-right">4.83e3</td>+<td class="org-right">4.85e3</td>+<td class="org-right">4.83e3</td>+<td class="org-right">4.84e3</td>+<td class="org-right">1.86e2</td>+<td class="org-right">4.84e3</td>+<td class="org-right">4.84e3</td>+</tr>++<tr>+<td class="org-left">sumSum</td>+<td class="org-right">4.55e3</td>+<td class="org-right">4.83e3</td>+<td class="org-right">4.53e3</td>+<td class="org-right">4.53e3</td>+<td class="org-right">1.85e2</td>+<td class="org-right">6.02e3</td>+<td class="org-right">4.55e3</td>+</tr>++<tr>+<td class="org-left">sumTail</td>+<td class="org-right">4.54e3</td>+<td class="org-right">7.07e3</td>+<td class="org-right">5.81e3</td>+<td class="org-right">4.96e3</td>+<td class="org-right">3.27e2</td>+<td class="org-right">6.49e3</td>+<td class="org-right">4.43e3</td>+</tr>++<tr>+<td class="org-left">sumTailLazy</td>+<td class="org-right">6.24e3</td>+<td class="org-right">4.41e3</td>+<td class="org-right">6.47e3</td>+<td class="org-right">6.23e3</td>+<td class="org-right">2.03e2</td>+<td class="org-right">5.49e3</td>+<td class="org-right">6.24e3</td>+</tr>+</tbody>+</table>+++<a id="orgdb37d7c"></a>++### short list++ perf-explore -n 10000 -l 10 --median --ticks++<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">+++<colgroup>+<col class="org-left" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />+</colgroup>+<tbody>+<tr>+<td class="org-left"> </td>+<td class="org-right">stepTime</td>+<td class="org-right">tick</td>+<td class="org-right">tickForce</td>+<td class="org-right">tickForceArgs</td>+<td class="org-right">tickLazy</td>+<td class="org-right">tickWHNF</td>+<td class="org-right">times</td>+</tr>++<tr>+<td class="org-left">sumAux</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.21e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumCata</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.20e2</td>+<td class="org-right">2.21e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumCo</td>+<td class="org-right">2.22e2</td>+<td class="org-right">2.34e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.21e2</td>+</tr>++<tr>+<td class="org-left">sumCoCase</td>+<td class="org-right">2.15e2</td>+<td class="org-right">2.32e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.36e2</td>+<td class="org-right">1.91e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumCoGo</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.23e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.31e2</td>+<td class="org-right">1.87e2</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumF</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.30e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.20e2</td>+<td class="org-right">1.86e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.20e2</td>+</tr>++<tr>+<td class="org-left">sumFlip</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.34e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.16e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.17e2</td>+</tr>++<tr>+<td class="org-left">sumFlipLazy</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.23e2</td>+<td class="org-right">2.16e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumFoldr</td>+<td class="org-right">2.14e2</td>+<td class="org-right">2.31e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.17e2</td>+<td class="org-right">2.18e2</td>+</tr>++<tr>+<td class="org-left">sumFuse</td>+<td class="org-right">2.02e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldl’</td>+<td class="org-right">2.02e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.03e2</td>+<td class="org-right">2.03e2</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldr</td>+<td class="org-right">2.04e2</td>+<td class="org-right">2.04e2</td>+<td class="org-right">2.07e2</td>+<td class="org-right">2.04e2</td>+<td class="org-right">1.94e2</td>+<td class="org-right">2.05e2</td>+<td class="org-right">2.04e2</td>+</tr>++<tr>+<td class="org-left">sumFusePoly</td>+<td class="org-right">2.05e2</td>+<td class="org-right">2.05e2</td>+<td class="org-right">2.05e2</td>+<td class="org-right">2.05e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.05e2</td>+<td class="org-right">2.05e2</td>+</tr>++<tr>+<td class="org-left">sumLambda</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.39e2</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">1.84e2</td>+<td class="org-right">2.20e2</td>+<td class="org-right">2.19e2</td>+</tr>++<tr>+<td class="org-left">sumMono</td>+<td class="org-right">2.08e2</td>+<td class="org-right">2.31e2</td>+<td class="org-right">2.08e2</td>+<td class="org-right">2.11e2</td>+<td class="org-right">1.92e2</td>+<td class="org-right">2.09e2</td>+<td class="org-right">2.09e2</td>+</tr>++<tr>+<td class="org-left">sumPoly</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.32e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.20e2</td>+<td class="org-right">2.20e2</td>+</tr>++<tr>+<td class="org-left">sumSum</td>+<td class="org-right">2.18e2</td>+<td class="org-right">2.33e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">2.19e2</td>+<td class="org-right">1.85e2</td>+<td class="org-right">2.20e2</td>+<td class="org-right">2.19e2</td>+</tr>++<tr>+<td class="org-left">sumTail</td>+<td class="org-right">2.52e2</td>+<td class="org-right">4.19e2</td>+<td class="org-right">2.95e2</td>+<td class="org-right">2.60e2</td>+<td class="org-right">2.69e2</td>+<td class="org-right">3.64e2</td>+<td class="org-right">2.42e2</td>+</tr>++<tr>+<td class="org-left">sumTailLazy</td>+<td class="org-right">2.09e2</td>+<td class="org-right">2.42e2</td>+<td class="org-right">2.13e2</td>+<td class="org-right">2.10e2</td>+<td class="org-right">1.90e2</td>+<td class="org-right">2.28e2</td>+<td class="org-right">2.11e2</td>+</tr>+</tbody>+</table>+++<a id="org56b0098"></a>++### long list++ perf-explore -n 100 -l 100000 --best --ticks++<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">+++<colgroup>+<col class="org-left" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />++<col class="org-right" />+</colgroup>+<tbody>+<tr>+<td class="org-left"> </td>+<td class="org-right">stepTime</td>+<td class="org-right">tick</td>+<td class="org-right">tickForce</td>+<td class="org-right">tickForceArgs</td>+<td class="org-right">tickLazy</td>+<td class="org-right">tickWHNF</td>+<td class="org-right">times</td>+</tr>++<tr>+<td class="org-left">sumAux</td>+<td class="org-right">7.38e5</td>+<td class="org-right">7.38e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.38e5</td>+<td class="org-right">7.38e5</td>+</tr>++<tr>+<td class="org-left">sumCata</td>+<td class="org-right">7.40e5</td>+<td class="org-right">7.40e5</td>+<td class="org-right">7.38e5</td>+<td class="org-right">7.39e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.40e5</td>+<td class="org-right">7.40e5</td>+</tr>++<tr>+<td class="org-left">sumCo</td>+<td class="org-right">7.40e5</td>+<td class="org-right">7.41e5</td>+<td class="org-right">7.38e5</td>+<td class="org-right">7.38e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.41e5</td>+<td class="org-right">7.39e5</td>+</tr>++<tr>+<td class="org-left">sumCoCase</td>+<td class="org-right">7.39e5</td>+<td class="org-right">7.39e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.40e5</td>+<td class="org-right">7.38e5</td>+</tr>++<tr>+<td class="org-left">sumCoGo</td>+<td class="org-right">7.39e5</td>+<td class="org-right">7.39e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">7.36e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.39e5</td>+<td class="org-right">7.39e5</td>+</tr>++<tr>+<td class="org-left">sumF</td>+<td class="org-right">3.52e5</td>+<td class="org-right">3.52e5</td>+<td class="org-right">3.52e5</td>+<td class="org-right">3.52e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.52e5</td>+<td class="org-right">3.52e5</td>+</tr>++<tr>+<td class="org-left">sumFlip</td>+<td class="org-right">3.75e5</td>+<td class="org-right">3.75e5</td>+<td class="org-right">3.75e5</td>+<td class="org-right">3.75e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.75e5</td>+<td class="org-right">3.75e5</td>+</tr>++<tr>+<td class="org-left">sumFlipLazy</td>+<td class="org-right">3.65e5</td>+<td class="org-right">3.65e5</td>+<td class="org-right">3.65e5</td>+<td class="org-right">3.65e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.65e5</td>+<td class="org-right">3.65e5</td>+</tr>++<tr>+<td class="org-left">sumFoldr</td>+<td class="org-right">7.51e5</td>+<td class="org-right">7.52e5</td>+<td class="org-right">7.47e5</td>+<td class="org-right">7.48e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">7.51e5</td>+<td class="org-right">7.51e5</td>+</tr>++<tr>+<td class="org-left">sumFuse</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">1.66e2</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldl’</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">1.66e2</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+</tr>++<tr>+<td class="org-left">sumFuseFoldr</td>+<td class="org-right">4.97e5</td>+<td class="org-right">4.95e5</td>+<td class="org-right">4.96e5</td>+<td class="org-right">4.97e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">4.96e5</td>+<td class="org-right">4.97e5</td>+</tr>++<tr>+<td class="org-left">sumFusePoly</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+<td class="org-right">1.66e2</td>+<td class="org-right">6.26e4</td>+<td class="org-right">6.26e4</td>+</tr>++<tr>+<td class="org-left">sumLambda</td>+<td class="org-right">3.73e5</td>+<td class="org-right">3.71e5</td>+<td class="org-right">3.71e5</td>+<td class="org-right">3.71e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.71e5</td>+<td class="org-right">3.73e5</td>+</tr>++<tr>+<td class="org-left">sumMono</td>+<td class="org-right">3.95e5</td>+<td class="org-right">3.95e5</td>+<td class="org-right">3.95e5</td>+<td class="org-right">3.95e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.95e5</td>+<td class="org-right">3.95e5</td>+</tr>++<tr>+<td class="org-left">sumPoly</td>+<td class="org-right">3.85e5</td>+<td class="org-right">3.85e5</td>+<td class="org-right">3.84e5</td>+<td class="org-right">3.84e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.85e5</td>+<td class="org-right">3.85e5</td>+</tr>++<tr>+<td class="org-left">sumSum</td>+<td class="org-right">4.06e5</td>+<td class="org-right">4.06e5</td>+<td class="org-right">4.06e5</td>+<td class="org-right">4.06e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">4.06e5</td>+<td class="org-right">4.06e5</td>+</tr>++<tr>+<td class="org-left">sumTail</td>+<td class="org-right">3.06e5</td>+<td class="org-right">3.53e5</td>+<td class="org-right">3.06e5</td>+<td class="org-right">3.06e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.08e5</td>+<td class="org-right">3.06e5</td>+</tr>++<tr>+<td class="org-left">sumTailLazy</td>+<td class="org-right">3.01e5</td>+<td class="org-right">3.01e5</td>+<td class="org-right">3.01e5</td>+<td class="org-right">3.01e5</td>+<td class="org-right">1.66e2</td>+<td class="org-right">3.01e5</td>+<td class="org-right">3.01e5</td>+</tr>+</tbody>+</table>+++<a id="orgff01033"></a>++## sums++ perf-explore -n 1000 -l 1000 --sums++ label1 label2 old result new result change+ + sumAux time 5.53e3 5.21e3 improvement+ sumCata time 5.18e3 4.73e3 improvement+ sumCo time 6.50e3 6.40e3+ sumCoCase time 5.16e3 6.03e3 slightly-degraded+ sumCoGo time 6.11e3 5.88e3+ sumF time 5.44e3 4.16e3 improvement+ sumFlip time 7.23e3 7.07e3+ sumFlipLazy time 6.68e3 6.44e3+ sumFoldr time 5.23e3 5.00e3+ sumFuse time 6.85e2 6.81e2+ sumFuseFoldl' time 6.94e2 6.78e2+ sumFuseFoldr time 1.04e3 1.02e3+ sumFusePoly time 6.96e2 6.84e2+ sumLambda time 4.79e3 4.77e3+ sumMono time 4.82e3 4.84e3+ sumPoly time 4.77e3 4.81e3+ sumSum time 4.95e3 5.05e3+ sumTail time 7.32e3 7.10e3+ sumTailLazy time 6.75e3 6.52e3+++<a id="org5abd0c1"></a>++## lengths++ perf-explore -n 1000 -l 1000 --lengths++ label1 label2 old result new result change + + lengthAux time 4.44e3 3.68e3 improvement + lengthCo time 4.91e3 4.45e3 improvement + lengthCoCase time 4.90e3 4.44e3 improvement+ lengthF time 3.38e3 3.21e3+ lengthFMono time 4.16e3 4.00e3+ lengthFlip time 5.49e3 4.90e3 improvement+ lengthFlipLazy time 5.32e3 4.77e3 improvement+ lengthFoldr time 4.23e3 3.90e3 improvement+ lengthFoldrConsttime 3.98e3 3.74e3 improvement+ lengthTail time 6.47e3 5.30e3 improvement+ lengthTailLazy time 6.11e3 5.34e3 improvement+++<a id="org01bde6f"></a>++## Space++ perf-explore -n 10 -l 100000 --space +RTS -T -RTS++ label1 label2 old result new result change+ + sum MaxMem 4.61e6 4.61e6+ sum allocated 4.20e5 4.20e5+ sum gcLiveBytes 2.15e5 2.17e5+ sum gcollects 1.00e-1 1.00e-1+ sum maxLiveBytes 0.00e0 0.00e0++Data is collected from GHCStats++- allocated_bytes+- gcs+- gcdetails_live_bytes+- max_live_bytes+- max_mem_in_use_bytes+++<a id="org753786d"></a>++# Perf.BigO++Perf.BigO represents functionality to determine the complexity order for a computation.++We could do a regression and minimise the error term, but we know that the largest run contains the most information; we would need to weight the simulations according to some heuristic.++Instead, we:++- estimate the order factor for each possible Order, from N3 to N0, setting the highest n run constant factor to zero,+- pick the order based on lowest absolute error result summed across all the runs,++ import qualified Prelude as P+ import Data.List (nub)+ estOrder (\x -> sum $ nub [1..x]) 10 [1,10,100,1000]++ BigOrder {bigOrder = N2, bigFactor = 4.05725, bigConstant = 0.0}+++<a id="org47311bd"></a>++## Cache speed++The average cycles per + operation can get down to about 0.7 cycles, and there are about 4 cache registers per cycle, so a sum pipeline uses 2.8 register instructions per +.++<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">+++<colgroup>+<col class="org-left" />++<col class="org-right" />++<col class="org-left" />+</colgroup>+<thead>+<tr>+<th scope="col" class="org-left">Cache</th>+<th scope="col" class="org-right">nsecs</th>+<th scope="col" class="org-left">Cycles</th>+</tr>+</thead>+<tbody>+<tr>+<td class="org-left">register</td>+<td class="org-right">0.1</td>+<td class="org-left">4 per cycle</td>+</tr>++<tr>+<td class="org-left">L1 Cache access</td>+<td class="org-right">1</td>+<td class="org-left">3-4 cycles</td>+</tr>++<tr>+<td class="org-left">L2 Cache access</td>+<td class="org-right">4</td>+<td class="org-left">11-12 cycles</td>+</tr>++<tr>+<td class="org-left">L3 unified access</td>+<td class="org-right">14</td>+<td class="org-left">30 - 40</td>+</tr>++<tr>+<td class="org-left">DRAM hit</td>+<td class="org-right">80</td>+<td class="org-left">195 cycles</td>+</tr>++<tr>+<td class="org-left">L1 miss</td>+<td class="org-right">16</td>+<td class="org-left">40 cycles</td>+</tr>++<tr>+<td class="org-left">L2 miss</td>+<td class="org-right">>250</td>+<td class="org-left">>600 cycles</td>+</tr>+</tbody>+</table>+
− readme.org
@@ -1,670 +0,0 @@-#+TITLE: perf-#+PROPERTY: header-args :exports both-#+PROPERTY: header-args :eval no-export--[[https://hackage.haskell.org/package/perf][file:https://img.shields.io/hackage/v/perf.svg]] [[https://github.com/tonyday567/perf/actions?query=workflow%3Ahaskell-ci][file:https://github.com/tonyday567/perf/workflows/haskell-ci/badge.svg]]--* Introduction--~perf~ provides some ideas, code and a library for low-level performance measurement for Haskell hacking. The library:--- provides a monad transformer, ~PerfT~, as a light-weight wrapper for use on existing code. ~PerfT~ modifications can be included in code bases, as opposed to performance being separated code and process, with any effects able to be erased at compile time with ~evalPerfT~.--- focuses on using the [[https://en.wikipedia.org/wiki/Time_Stamp_Counter][rdtsc]] instruction set that comes with the x86 chip to measure accurate time performance as number of clock cycles.--- abstracts the concept of what is being measured, so that concepts such as counters, debug checks, time and space performance can be treated equivalently.--* Setup--Note that running this readme.org is very slow compared with an external process which accesses the compiled version of the library.--#+begin_src haskell-ng :results output-:r-:set -Wno-type-defaults-:set -Wno-unused-do-bind-:set -Wno-name-shadowing-:set -XOverloadedStrings-:set -XOverloadedLabels-import Perf-import Data.FormatN-import qualified Data.Text as Text-import qualified Data.Text.IO as Text-import qualified Data.Map.Strict as Map-import Control.Monad-import Data.Bifunctor-putStrLn "ok"-#+end_src--#+RESULTS:-#+begin_example-Build profile: -w ghc-9.8.1 -O1-In order, the following will be built (use -v for more details):- - perf-0.12.0.1 (lib) (first run)-Preprocessing library for perf-0.12.0.1..-GHCi, version 9.8.1: https://www.haskell.org/ghc/ :? for help-Loaded GHCi configuration from /Users/tonyday/haskell/perf/.ghci-[ 1 of 10] Compiling Perf.Stats ( src/Perf/Stats.hs, interpreted )-[ 2 of 10] Compiling Perf.Types ( src/Perf/Types.hs, interpreted )-[ 3 of 10] Compiling Perf.Time ( src/Perf/Time.hs, interpreted )-[ 4 of 10] Compiling Perf.Space ( src/Perf/Space.hs, interpreted )-[ 5 of 10] Compiling Perf.Count ( src/Perf/Count.hs, interpreted )-[ 6 of 10] Compiling Perf.Measure ( src/Perf/Measure.hs, interpreted )-[ 7 of 10] Compiling Perf.Report ( src/Perf/Report.hs, interpreted )-[ 8 of 10] Compiling Perf.BigO ( src/Perf/BigO.hs, interpreted )-[ 9 of 10] Compiling Perf.Algos ( src/Perf/Algos.hs, interpreted )-[10 of 10] Compiling Perf ( src/Perf.hs, interpreted )-Ok, 10 modules loaded.-Ok, 10 modules loaded.-ok-#+end_example--* Time-** What is a tick?--The fundamental operation of Perf.Time is tick, which sandwiches a (strict) function application between two readings of the rdstc chip.--#+begin_src haskell-ng :results output :exports both-:t tick-#+end_src--#+RESULTS:-: tick :: (a -> b) -> a -> IO (Cycles, b)--tick returns in the IO monad, because reading a cycle counter is an IO effect. The trivial but fundamental point is that performance measurement effects the computation being measured.--Unlike benchmarking-style libraries, such as criterion, the result of the computation is passed through along with the cycle count. In this way, the `Perf` monad can be inserted into the midst of a computation in an attempt to measure performance in-situ as opposed to sitting off in a separate and perhaps unrepresentative process.--** tick_--tick_ measures the number of cycles between two clock reads.--#+begin_src haskell-ng :results output :exports both-:t tick_-#+end_src--#+RESULTS:-: tick_ :: IO Cycles---#+begin_src haskell-ng :results output :exports both-fmap word <$> replicateM 10 tick_-#+end_src--#+RESULTS:-: [3063,1201,990,978,945,948,951,948,951,948]--** multiple ticks--#+begin_src haskell-ng :results output :exports both-fmap (fmap (word . fst)) . replicateM 10 $ tick (const ()) ()-#+end_src--#+RESULTS:-: [12104,5040,4868,4508,4188,4172,4800,4748,5760,4216]--Here, ~const () ()~ was evaluated and took 6.1k cycles for the first effect, reducing down to 2.6k after 10 effects. What it did in that time we will never know, really, at least from usage of ~perf~. 2.2k cycles is, on my 2.5GHz machine equal to 2200 / 2.5e9 = 8.8e-7 of a second or 880 nanoseconds.--** tickIO--~tickIO~ measures the evaluation of an IO value.--#+begin_src haskell-ng :results output :exports both-:t tickIO-#+end_src--#+RESULTS:-: tickIO :: IO a -> IO (Cycles, a)--#+begin_src haskell-ng :results output :exports both-fmap (fmap (word . fst)) . replicateM 10 $ tickIO (pure ())-#+end_src--#+RESULTS:-: [10084,3588,4104,2844,2916,3140,2800,2848,2848,2804]--** sum example--#+begin_src haskell-ng :exports both-fmap (expt (Just 2) . fromIntegral) . fst <$> ticks 10 sum ([1..10000] :: [Double])-#+end_src--#+RESULTS:-: ["1.7e6","6.1e5","6.2e5","5.4e5","4.3e5","4.1e5","4.1e5","4.3e5","4.1e5","4.1e5"]---#+begin_src haskell-ng :results output :exports both-ts <- ticks 10000 sum ([1..1000] :: [Double])-print $ average (fmap fromIntegral $ fst ts)-#+end_src--#+RESULTS:-: 30411.6957--* PerfT--~PerfT~ allows for multiple measurement points and is polymorphic in what is being measured. It returns a Map of results held in State.--Compare a lower-level usage of ticks, measuring the average of summing to one thousand over one thousand trials:--#+begin_src haskell-ng :results output :exports both-first (average . fmap fromIntegral) <$> ticks 1000 sum [1..1000]-#+end_src--#+RESULTS:-: (50914.804,500500)--... with PerfT usage--#+begin_src haskell-ng :results output :exports both-second (fmap (average . fmap fromIntegral)) <$> runPerfT (times 1000) (sum |$| [1..1000])-#+end_src--#+RESULTS:-: (500500,fromList [("",44362.311)])--Comparing performance of sum versus a list fusion approach:--#+begin_src haskell-ng :results output :exports both-fmap (average . fmap fromIntegral) <$> (execPerfT (times 1000) $ do; (fap "sum" sum [1..1000]); (fap "fusion" (\x -> sum [1..x]) 1000))-#+end_src--#+RESULTS:-: fromList [("fusion",97804.085),("sum",45053.771)]--An IO example--#+begin_src haskell-ng-exampleIO' :: IO ()-exampleIO' = do- txt <- Text.readFile "src/Perf.hs"- let n = Text.length txt- Text.putStrLn $ "length of file is: " <> Text.pack (show n)-#+end_src--#+begin_src haskell-ng :results output :exports both-exampleIO = execPerfT time (do- txt <- fam "file_read" (Text.readFile "src/Perf.hs")- n <- fap "length" Text.length txt- fam "print_result" (Text.putStrLn $ "length of file is: " <> Text.pack (show n)))-#+end_src--#+begin_src sh :results output :exports both-perf-explore --exampleIO-#+end_src--#+RESULTS:-#+begin_example-length of file is: 1794-length of file is: 1794--label1 label2 label3 old result new result change--normal file-read time 5.96e5 6.58e5 slightly-degraded-normal length time 1.10e4 7.12e3 improvement-normal print-result time 1.44e5 5.70e4 improvement-outer file-read time 1.36e5 1.39e5-outer length time 1.45e3 1.33e3 improvement-outer outer-total time 1.67e5 1.50e5 improvement-outer print-result time 2.70e4 6.26e3 improvement-#+end_example--* perf-explore--~perf-explore~ contains some exploratory routines used to develop =perf=--#+begin_src sh :results output :exports both-perf-explore --help-#+end_src--#+RESULTS:-#+begin_example-examples of perf usage--Usage: perf-explore [-n|--runs ARG]- [--best | --median | --average | --averagesecs]- [--time | --space | --spacetime | --allocation | --count]- [-g|--golden ARG] [--nocheck] [-r|--record]- [--header | --noheader] [--error ARG] [--warning ARG]- [--improved ARG]- [--sums | --lengths | --nub | --examples | --example |- --exampleIO | --noops | --ticks | --gauge]- [-l|--length ARG]- [--sumFuse | --sum | --lengthF | --constFuse | --mapInc |- --noOp]-- perf exploration--Available options:- -n,--runs ARG number of runs to perform- --best report upper decile- --median report median- --average report average- --averagesecs report average in seconds- --time measure time performance- --space measure space performance- --spacetime measure both space and time performance- --allocation measure bytes allocated- --count measure count- -g,--golden ARG golden file name- --nocheck do not check versus the golden file- -r,--record record the result to the golden file- --header include headers- --noheader dont include headers- --error ARG error level- --warning ARG warning level- --improved ARG improved level- --sums run on sum algorithms- --lengths run on length algorithms- --nub nub test- --examples run on example algorithms- --example run on the example algorithm- --exampleIO exampleIO test- --noops noops test- --ticks tick test- --gauge gauge runs on exmaple for comparison- -l,--length ARG length of list- --sumFuse fused sum pipeline- --sum sum- --lengthF foldr id length- --constFuse fused const pipeline- --mapInc fmap (+1)- --noOp const ()- -h,--help Show this help text-#+end_example--#+begin_src haskell-ng :results output :exports both-fmap averageI <$> execPerfT (times 10000) (sum |$| [1..1000])-#+end_src--#+RESULTS:-: fromList [("",35729.9518)]--The equivalent to the above code is:--#+begin_src sh :results output :exports both-perf-explore -n 10000 -l 1000 --sum --nocheck-#+end_src--#+RESULTS:-: label1 label2 results-:-: sum time 1.47e4--** noops--This no-op experiment is useful to understand the pure time performance of the machinery around measurement. It can be (re)run with:--#+begin_src sh :results output :exports both-perf-explore --noops-#+end_src--#+RESULTS:-#+begin_example-label1 label2 label3 results--const 1st time 6.05e3-const 2nd time 2.80e1-const 3rd time 3.60e1-const 4th time 3.20e1-const average time 4.11e1-const best time 2.74e1-const median time 3.54e1-pure 1st time 6.46e3-pure 2nd time 7.60e1-pure 3rd time 4.00e1-pure 4th time 4.00e1-pure average time 4.28e1-pure best time 2.82e1-pure median time 3.64e1-No golden file found. To create one, run with '-r'-#+end_example--** measurement context--Exploration of how the code surrounding measurement effects performance.--#+begin_src sh :results drawer :exports both-perf-explore -n 1000 -l 1000 --ticks --nocheck-#+end_src--#+RESULTS:-:results:-| | stepTime | tick | tickForce | tickForceArgs | tickLazy | tickWHNF | times |-| sumAux | 1.46e4 | 1.51e4 | 1.65e4 | 1.45e4 | 1.96e1 | 1.45e4 | 1.46e4 |-| sumCata | 1.45e4 | 1.82e4 | 1.45e4 | 1.45e4 | 1.99e1 | 1.46e4 | 1.48e4 |-| sumCo | 1.45e4 | 1.46e4 | 1.46e4 | 1.63e4 | 2.02e1 | 1.45e4 | 1.44e4 |-| sumCoCase | 1.44e4 | 1.51e4 | 1.61e4 | 1.46e4 | 2.78e1 | 2.10e4 | 1.49e4 |-| sumCoGo | 2.61e4 | 1.46e4 | 3.82e4 | 2.55e4 | 2.04e1 | 1.45e4 | 2.59e4 |-| sumF | 1.34e4 | 1.34e4 | 2.01e4 | 1.64e4 | 2.01e1 | 1.33e4 | 1.33e4 |-| sumFlip | 1.37e4 | 1.34e4 | 1.34e4 | 1.33e4 | 2.06e1 | 1.41e4 | 1.33e4 |-| sumFlipLazy | 1.33e4 | 1.34e4 | 1.33e4 | 1.34e4 | 2.01e1 | 1.37e4 | 1.34e4 |-| sumFoldr | 1.44e4 | 1.45e4 | 2.18e4 | 1.68e4 | 3.22e1 | 1.54e4 | 1.45e4 |-| sumFuse | 3.19e3 | 1.98e3 | 1.93e3 | 2.19e3 | 1.98e1 | 1.83e3 | 3.15e3 |-| sumFuseFoldl' | 1.88e3 | 2.46e3 | 2.02e3 | 2.27e3 | 2.39e1 | 2.38e3 | 2.15e3 |-| sumFuseFoldr | 5.79e3 | 6.01e3 | 3.70e3 | 3.70e3 | 2.00e1 | 3.70e3 | 6.11e3 |-| sumFusePoly | 2.67e3 | 2.68e3 | 2.65e3 | 2.29e3 | 2.73e1 | 2.44e3 | 2.60e3 |-| sumLambda | 1.59e4 | 1.35e4 | 1.47e4 | 1.99e4 | 1.99e1 | 1.36e4 | 1.34e4 |-| sumMono | 1.38e4 | 1.69e4 | 1.35e4 | 1.36e4 | 2.04e1 | 1.37e4 | 1.34e4 |-| sumPoly | 1.99e4 | 1.42e4 | 1.35e4 | 1.73e4 | 1.97e1 | 1.35e4 | 1.51e4 |-| sumSum | 1.34e4 | 1.36e4 | 1.34e4 | 1.35e4 | 1.99e1 | 1.34e4 | 2.01e4 |-| sumTail | 2.03e4 | 1.37e4 | 1.34e4 | 1.34e4 | 1.99e1 | 1.34e4 | 2.01e4 |-| sumTailLazy | 1.34e4 | 1.66e4 | 1.33e4 | 1.41e4 | 1.99e1 | 1.35e4 | 1.33e4 |-:end:--*** short list-#+begin_src sh :results drawer :exports both-perf-explore -n 10000 -l 10 --best --ticks-#+end_src--#+RESULTS:-:results:-| | stepTime | tick | tickForce | tickForceArgs | tickLazy | tickWHNF | times |-| sumAux | 1.81e2 | 2.33e2 | 1.81e2 | 1.82e2 | 1.32e1 | 1.83e2 | 1.82e2 |-| sumCata | 1.81e2 | 2.33e2 | 1.81e2 | 1.82e2 | 1.33e1 | 2.33e2 | 1.81e2 |-| sumCo | 2.10e2 | 2.33e2 | 2.13e2 | 2.11e2 | 2.49e1 | 2.33e2 | 2.10e2 |-| sumCoCase | 1.81e2 | 2.47e2 | 1.82e2 | 1.82e2 | 1.83e1 | 2.47e2 | 1.81e2 |-| sumCoGo | 1.81e2 | 2.34e2 | 2.34e2 | 2.34e2 | 1.33e1 | 2.33e2 | 1.82e2 |-| sumF | 1.03e2 | 2.22e2 | 1.05e2 | 1.04e2 | 1.88e1 | 1.83e2 | 1.04e2 |-| sumFlip | 1.13e2 | 1.28e2 | 1.20e2 | 1.20e2 | 1.88e1 | 1.17e2 | 1.63e2 |-| sumFlipLazy | 1.13e2 | 2.07e2 | 1.76e2 | 1.20e2 | 2.49e1 | 2.04e2 | 1.01e2 |-| sumFoldr | 1.81e2 | 2.66e2 | 2.65e2 | 1.81e2 | 1.88e1 | 2.65e2 | 1.81e2 |-| sumFuse | 2.58e1 | 2.71e1 | 2.85e1 | 3.36e1 | 1.36e1 | 3.36e1 | 3.42e1 |-| sumFuseFoldl' | 5.04e1 | 2.71e1 | 5.11e1 | 5.08e1 | 1.41e1 | 2.65e1 | 3.96e1 |-| sumFuseFoldr | 8.16e1 | 8.61e1 | 6.26e1 | 7.34e1 | 1.25e1 | 8.47e1 | 8.54e1 |-| sumFusePoly | 4.19e1 | 5.82e1 | 4.26e1 | 5.81e1 | 1.30e1 | 5.91e1 | 4.30e1 |-| sumLambda | 1.39e2 | 1.30e2 | 1.60e2 | 1.63e2 | 1.33e1 | 1.29e2 | 1.43e2 |-| sumMono | 8.04e1 | 1.43e2 | 8.64e1 | 8.41e1 | 1.83e1 | 1.15e2 | 8.16e1 |-| sumPoly | 1.16e2 | 1.29e2 | 1.44e2 | 1.21e2 | 2.49e1 | 1.30e2 | 1.18e2 |-| sumSum | 1.02e2 | 1.03e2 | 1.04e2 | 1.05e2 | 1.33e1 | 1.02e2 | 1.04e2 |-| sumTail | 1.40e2 | 1.37e2 | 1.31e2 | 1.31e2 | 1.36e1 | 1.45e2 | 1.42e2 |-| sumTailLazy | 1.06e2 | 1.78e2 | 1.12e2 | 1.12e2 | 1.84e1 | 2.25e2 | 1.09e2 |-:end:---*** long list-#+begin_src sh :results drawer :exports both-perf-explore -n 100 -l 100000 --best --ticks-#+end_src--#+RESULTS:-:results:-| | stepTime | tick | tickForce | tickForceArgs | tickLazy | tickWHNF | times |-| sumAux | 2.82e6 | 2.70e6 | 2.66e6 | 2.75e6 | 1.83e1 | 2.70e6 | 2.79e6 |-| sumCata | 2.71e6 | 2.69e6 | 2.63e6 | 2.64e6 | 1.28e1 | 2.68e6 | 2.75e6 |-| sumCo | 2.71e6 | 2.73e6 | 2.63e6 | 2.64e6 | 2.52e1 | 2.71e6 | 2.73e6 |-| sumCoCase | 2.69e6 | 2.70e6 | 2.70e6 | 2.65e6 | 1.85e1 | 2.76e6 | 2.67e6 |-| sumCoGo | 2.68e6 | 2.77e6 | 2.65e6 | 2.63e6 | 1.82e1 | 2.75e6 | 2.69e6 |-| sumF | 1.24e6 | 1.24e6 | 1.24e6 | 1.24e6 | 1.26e1 | 1.24e6 | 1.27e6 |-| sumFlip | 1.16e6 | 1.14e6 | 1.14e6 | 1.16e6 | 1.35e1 | 1.13e6 | 1.16e6 |-| sumFlipLazy | 1.03e6 | 1.03e6 | 1.03e6 | 1.02e6 | 1.37e1 | 1.03e6 | 1.03e6 |-| sumFoldr | 2.76e6 | 2.83e6 | 2.66e6 | 2.67e6 | 1.82e1 | 2.81e6 | 2.74e6 |-| sumFuse | 1.45e5 | 1.45e5 | 1.45e5 | 1.44e5 | 1.39e1 | 1.45e5 | 1.45e5 |-| sumFuseFoldl' | 1.45e5 | 1.45e5 | 1.45e5 | 1.46e5 | 1.35e1 | 1.45e5 | 1.45e5 |-| sumFuseFoldr | 1.76e6 | 1.77e6 | 1.75e6 | 1.75e6 | 1.90e1 | 1.76e6 | 1.75e6 |-| sumFusePoly | 1.45e5 | 2.00e5 | 1.45e5 | 1.45e5 | 1.32e1 | 1.68e5 | 1.45e5 |-| sumLambda | 9.35e5 | 9.52e5 | 9.35e5 | 9.38e5 | 2.48e1 | 9.46e5 | 9.44e5 |-| sumMono | 1.13e6 | 1.12e6 | 1.11e6 | 1.11e6 | 1.23e1 | 1.11e6 | 1.11e6 |-| sumPoly | 1.03e6 | 1.04e6 | 1.02e6 | 1.03e6 | 1.32e1 | 1.03e6 | 1.03e6 |-| sumSum | 1.20e6 | 1.22e6 | 1.20e6 | 1.19e6 | 1.32e1 | 1.22e6 | 1.20e6 |-| sumTail | 1.22e6 | 1.22e6 | 1.22e6 | 1.22e6 | 1.28e1 | 1.22e6 | 1.22e6 |-| sumTailLazy | 1.16e6 | 1.19e6 | 1.18e6 | 1.16e6 | 1.29e1 | 1.18e6 | 1.16e6 |-:end:--** sums--#+begin_src sh :output drawer :exports both-perf-explore -n 1000 -l 1000 --sums-#+end_src--#+RESULTS:-#+begin_example-label1 label2 old result new result change--sumAux time 1.51e4 1.46e4-sumCata time 1.55e4 1.46e4 improvement-sumCo time 1.55e4 2.01e4 degraded-sumCoCase time 1.57e4 1.47e4 improvement-sumCoGo time 1.54e4 1.69e4 slightly-degraded-sumF time 1.33e4 2.02e4 degraded-sumFlip time 1.41e4 1.36e4-sumFlipLazy time 1.47e4 1.93e4 degraded-sumFoldr time 1.56e4 1.75e4 slightly-degraded-sumFuse time 1.66e3 2.61e3 degraded-sumFuseFoldl' time 1.71e3 2.70e3 degraded-sumFuseFoldr time 5.83e3 6.72e3 slightly-degraded-sumFusePoly time 2.12e3 2.64e3 degraded-sumLambda time 1.47e4 1.39e4 improvement-sumMono time 1.73e4 1.40e4 improvement-sumPoly time 1.87e4 1.36e4 improvement-sumSum time 1.33e4 1.34e4-sumTail time 1.47e4 1.40e4 improvement-sumTailLazy time 1.44e4 1.34e4 improvement-#+end_example--** lengths--#+begin_src sh :exports both-perf-explore -n 1000 -l 1000 --lengths-#+end_src--#+RESULTS:-#+begin_example-label1 label2 old result new result change--lengthAux time 1.45e4 1.60e4 slightly-degraded-lengthCo time 1.64e4 1.39e4 improvement-lengthCoCase time 1.78e4 1.31e4 improvement-lengthF time 1.06e4 1.10e4-lengthFMono time 1.05e4 1.05e4-lengthFlip time 1.11e4 1.07e4-lengthFlipLazy time 1.13e4 1.07e4 improvement-lengthFoldr time 1.27e4 1.33e4 slightly-degraded-lengthFoldrConsttime 1.27e4 1.36e4 slightly-degraded-lengthTail time 1.08e4 1.10e4-lengthTailLazy time 1.15e4 1.03e4 improvement-#+end_example--** Gauge comparison--#+begin_src sh :results output :exports both-perf-explore -n 1000 -l 1000 --average --gauge-#+end_src--#+RESULTS:-#+begin_example-sumFuse-benchmarking function ... function time 849.4 ns--benchmarking function ... function time 848.6 ns--sum-benchmarking function ... function time 3.786 μs--benchmarking function ... function time 3.791 μs--lengthF-benchmarking function ... function time 1.863 μs--benchmarking function ... function time 1.872 μs--constFuse-benchmarking function ... function time 571.0 ns--benchmarking function ... function time 1.080 μs--mapInc-benchmarking function ... function time 9.203 ns--benchmarking function ... function time 15.99 μs--noop-benchmarking function ... function time 5.770 ns--benchmarking function ... function time 4.740 ns--#+end_example--** Space--Data is collected from GHCStats--- allocated_bytes-- gcs-- gcdetails_live_bytes-- max_live_bytes-- max_mem_in_use_bytes--#+begin_src sh :results ouput :exports both-perf-explore -n 10 -l 100000 --space +RTS -T -RTS-#+end_src--#+RESULTS:-: label1 label2 results-:-: sum MaxMem 4.61e6-: sum allocated 4.19e5-: sum gcLiveBytes 2.19e5-: sum gcollects 1.00e-1-: sum maxLiveBytes 0.00e0-: No golden file found. To create one, run with '-r'--* Perf.BigO--Perf.BigO represents functionality to determine the complexity order for a computation.--We could do a regression and minimise the error term, but we know that the largest run contains the most information; we would need to weight the simulations according to some heuristic.--Instead, we:--- estimate the order factor for each possible Order, from N3 to N0, setting the highest n run constant factor to zero,-- pick the order based on lowest absolute error result summed across all the runs,--#+begin_src haskell-ng :results output :exports both-import qualified Prelude as P-import Data.List (nub)-estOrder (\x -> sum $ nub [1..x]) 10 [1,10,100,1000]-#+end_src--#+RESULTS:-: BigOrder {bigOrder = N2, bigFactor = 13.375059, bigConstant = 0.0}--* References--[[https://github.com/haskell-perf/checklist][The Haskell performance checklist]]--[[https://github.com/ndmitchell/spaceleak][ndmitchell/spaceleak: Notes on space leaks]]--** Core--[[https://ghc.gitlab.haskell.org/ghc/doc/users_guide/debugging.html#options-debugging][5.13. Debugging the compiler]]--#+begin_src sh-ghc app/speed.hs -ddump-simpl -ddump-to-file -fforce-recomp -dlint -O-#+end_src--[[https://wiki.haskell.org/Performance/GHC#Looking_at_the_Core][haskell wiki: Looking at the Core]]--[[https://godbolt.org/][godbolt]]--[[https://gitlab.haskell.org/ghc/ghc/-/issues/15185][ghc issue 15185: Enum instance for IntX / WordX are inefficient]]--[[https://fixpt.de/blog/2017-12-04-strictness-analysis-part-1.html][fixpt - All About Strictness Analysis (part 1)]]--** Profiling-*** setup-[[https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#prof-heap][8. Profiling]]--A typical configuration step for profiling:--#+begin_src sh :results output-cabal configure --enable-library-profiling --enable-executable-profiling -fprof-auto -fprof -write-ghc-environment-files=always-#+end_src--A cabal.project.local with profiling enabled:--#+begin_quote-write-ghc-environment-files: always-ignore-project: False-flags: +prof +prof-auto-library-profiling: True-executable-profiling: True-#+end_quote--Examples from markup-parse R&D:--Executable compilation:--#+begin_src sh :results output-ghc -prof -fprof-auto -rtsopts app/speed0.hs -threaded -fforce-recomp-#+end_src--Executable run:--#+begin_src sh :results output-app/speed0 +RTS -s -p -hc -l -RTS-#+end_src--*** Space usage output (-s)--#+begin_example-885,263,472 bytes allocated in the heap- 8,507,448 bytes copied during GC- 163,200 bytes maximum residency (4 sample(s))- 27,752 bytes maximum slop- 6 MiB total memory in use (0 MiB lost due to fragmentation)-- Tot time (elapsed) Avg pause Max pause- Gen 0 207 colls, 0 par 0.009s 0.010s 0.0001s 0.0002s- Gen 1 4 colls, 0 par 0.001s 0.001s 0.0004s 0.0005s-- TASKS: 4 (1 bound, 3 peak workers (3 total), using -N1)-- SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)-- INIT time 0.006s ( 0.006s elapsed)- MUT time 0.367s ( 0.360s elapsed)- GC time 0.010s ( 0.011s elapsed)- RP time 0.000s ( 0.000s elapsed)- PROF time 0.000s ( 0.000s elapsed)- EXIT time 0.001s ( 0.001s elapsed)- Total time 0.384s ( 0.380s elapsed)-#+end_example--*** Cost center profile (-p)--Dumped to speed0.prof--#+begin_example-COST CENTRE MODULE SRC %time %alloc--token MarkupParse src/MarkupParse.hs:(259,1)-(260,20) 50.2 50.4-wrappedQ' MarkupParse.FlatParse src/MarkupParse/FlatParse.hs:(215,1)-(217,78) 20.8 23.1-ws_ MarkupParse.FlatParse src/MarkupParse/FlatParse.hs:(135,1)-(146,4) 14.3 5.5-eq MarkupParse.FlatParse src/MarkupParse/FlatParse.hs:243:1-30 10.6 11.1-gather MarkupParse src/MarkupParse.hs:(420,1)-(428,100) 2.4 3.7-runParser FlatParse.Basic src/FlatParse/Basic.hs:(217,1)-(225,24) 1.0 6.0-#+end_example--*** heap analysis (-hc -l)--#+begin_src sh :results output-eventlog2html speed0.eventlog-#+end_src--Produces speed0.eventlog.html which contains heap charts.--** Cache speed--The average cycles per + operation can get down to about 0.7 cycles, and there are about 4 cache registers per cycle, so a sum pipeline uses 2.8 register instructions per +.--| Cache | Cycles |-|-------------------|----------------|-| register | 4 per cycle |-| L1 Cache access | 3-4 cycles |-| L2 Cache access | 11-12 cycles |-| L3 unified access | 30 - 40 |-| DRAM hit | 195 cycles |-| L1 miss | 40 cycles |-| L2 miss | >600 cycles |--
src/Perf.hs view
@@ -2,21 +2,21 @@ -- -- @perf@ provides tools for measuring the runtime performance of Haskell functions. It includes: ----- - time measurement via reading the RDTSC register (TSC stands for "time stamp counter"), which is present on all x86 CPUs since the Pentium architecture. For more details, see https://en.wikipedia.org/wiki/Time_Stamp_Counter+-- - time measurement via the [clock](https://hackage.haskell.org/package/clock) library. ----- - abstraction of what is a 'Measure' so that the library includes both space and time measurement with the same API.+-- - a polymorphic approach to what a 'Measure' is so that a wide variety of measurements such as counting, space and time measurement can share the same API. -- -- - 'PerfT' which is a monad transformer designed to add the collection of performance information to existing code. Running the code produces a tuple of the original computation results, and a Map of performance measurements that were specified. -- -- - functionality to determine performance order, in 'Perf.BigO' ----- - reporting functionality in 'Perf.Report'. @perf@ can be run via 'cabal bench'; see the project's cabal file for an example.+-- - reporting functionality encapsulated in 'Perf.Report'. @perf@ can be run via 'cabal bench' and will, for example, error on performance degradation; see the project's cabal file for an example. module Perf ( -- * re-exports module Perf.Types, -- | Representation of what a Performance 'Measure' is. module Perf.Measure,- -- | Low-level time performance 'Measure' counting 'Cycles'+ -- | Low-level time performance 'Measure' counting 'Nanos' module Perf.Time, -- | Low-level space performance 'Measure's based on GHC's allocation statistics. module Perf.Space,
src/Perf/BigO.hs view
@@ -1,16 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} --- | Order of complexity calculations.------ References------ - <https://en.wikibooks.org/wiki/Optimizing_Code_for_Speed/Order_of_Complexity_Optimizations#:~:text=of%2DComplexity%20Reduction-,What%20is%20order%20of%20complexity%3F,*log(N))%20etc What is Order of Complexity> .------ - <https://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level-performance/ Hskell as fast as C ~ donsbot blog>------ - <https://www.fpcomplete.com/haskell/tutorial/profiling/ profiling ~ fpcomplete>------ - <https://www.reddit.com/r/haskell/comments/nl0rkl/looking_for_good_rules_of_thumbs_on_what_haskell/ rules of thumb ~ reddit>+-- | [Order of complexity](https://en.wikibooks.org/wiki/Optimizing_Code_for_Speed/Order_of_Complexity_Optimizations#:~:text=of%2DComplexity%20Reduction-,What%20is%20order%20of%20complexity%3F,*log(N)) calculations. module Perf.BigO ( O (..), olist,
src/Perf/Measure.hs view
@@ -17,6 +17,7 @@ import Perf.Stats import Perf.Time import Perf.Types+import System.Clock import Prelude hiding (cycle) -- | Command-line measurement options.@@ -33,10 +34,10 @@ <|> pure MeasureTime -- | unification of the different measurements to being a list of doubles.-measureDs :: MeasureType -> Int -> Measure IO [[Double]]-measureDs mt n =+measureDs :: MeasureType -> Clock -> Int -> Measure IO [[Double]]+measureDs mt c n = case mt of- MeasureTime -> fmap ((: []) . fromIntegral) <$> times n+ MeasureTime -> fmap ((: []) . fromIntegral) <$> timesWith c n MeasureSpace -> toMeasureN n (ssToList <$> space False) MeasureSpaceTime -> toMeasureN n ((\x y -> ssToList x <> [fromIntegral y]) <$> space False <*> stepTime) MeasureAllocation -> fmap ((: []) . fromIntegral) <$> toMeasureN n (allocation False)
src/Perf/Report.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} -- | Reporting on performance, potentially checking versus a canned results.@@ -43,7 +44,9 @@ import Options.Applicative import Perf.Measure import Perf.Stats+import Perf.Time (defaultClock) import Perf.Types+import System.Clock import System.Exit import Text.Printf hiding (parseFormat) import Text.Read@@ -65,6 +68,7 @@ data ReportOptions = ReportOptions { -- | Number of times to run a benchmark. reportN :: Int,+ reportClock :: Clock, reportStatDType :: StatDType, reportMeasureType :: MeasureType, reportGolden :: Golden,@@ -76,11 +80,12 @@ -- | Default options -- -- >>> defaultReportOptions--- ReportOptions {reportN = 1000, reportStatDType = StatAverage, reportMeasureType = MeasureTime, reportGolden = Golden {golden = "other/bench.perf", check = True, record = False}, reportHeader = Header, reportCompare = CompareLevels {errorLevel = 0.2, warningLevel = 5.0e-2, improvedLevel = 5.0e-2}}+-- ReportOptions {reportN = 1000, reportClock = MonotonicRaw, reportStatDType = StatAverage, reportMeasureType = MeasureTime, reportGolden = Golden {golden = "other/bench.perf", check = True, record = False}, reportHeader = Header, reportCompare = CompareLevels {errorLevel = 0.2, warningLevel = 5.0e-2, improvedLevel = 5.0e-2}} defaultReportOptions :: ReportOptions defaultReportOptions = ReportOptions 1000+ defaultClock StatAverage MeasureTime defaultGolden@@ -92,12 +97,27 @@ parseReportOptions = ReportOptions <$> option auto (value 1000 <> long "runs" <> short 'n' <> help "number of runs to perform")+ <*> parseClock <*> parseStatD <*> parseMeasure <*> parseGolden <*> parseHeader <*> parseCompareLevels defaultCompareLevels +-- | Parse command-line 'Clock' options.+parseClock :: Parser Clock+parseClock =+ flag' Monotonic (long "Monotonic")+ <|> flag' Realtime (long "Realtime")+ <|> flag' ProcessCPUTime (long "ProcessCPUTime")+ <|> flag' ThreadCPUTime (long "ThreadCPUTime")+#ifdef mingw32_HOST_OS+ <|> pure ThreadCPUTime+#else+ <|> flag' MonotonicRaw (long "MonotonicRaw")+ <|> pure MonotonicRaw+#endif+ -- | Default command-line parser. infoReportOptions :: ParserInfo ReportOptions infoReportOptions =@@ -126,9 +146,10 @@ reportMainWith o name t = do let !n = reportN o let s = reportStatDType o+ let c = reportClock o let mt = reportMeasureType o let o' = replaceDefaultFilePath (intercalate "-" [name, show n, show mt, show s]) o- m <- execPerfT (measureDs mt n) t+ m <- execPerfT (measureDs mt c n) t report o' (statify s m) -- | Levels of geometric difference in compared performance that triggers reporting.@@ -162,7 +183,7 @@ data CompareResult = CompareResult {oldResult :: Maybe Double, newResult :: Maybe Double, noteResult :: Text} deriving (Show, Eq) hasDegraded :: Map.Map a CompareResult -> Bool-hasDegraded m = any ((== "degraded") . noteResult) $ fmap snd (Map.toList m)+hasDegraded m = any (((== "degraded") . noteResult) . snd) (Map.toList m) -- | Compare two results and produce some notes given level triggers. compareNote :: (Ord a) => CompareLevels -> Map.Map a Double -> Map.Map a Double -> Map.Map a CompareResult
src/Perf/Stats.hs view
@@ -6,7 +6,6 @@ median, tenth, averageI,- averageSecs, StatDType (..), statD, statDs,@@ -42,26 +41,20 @@ averageI :: (Integral a) => [a] -> Double averageI xs = sum (fromIntegral <$> xs) / (fromIntegral . length $ xs) --- | Compute the average time in seconds.-averageSecs :: [Double] -> Double-averageSecs xs = sum xs / (fromIntegral . length $ xs) / 2.5e9- -- | Command-line options for type of statistic.-data StatDType = StatAverage | StatMedian | StatBest | StatSecs deriving (Eq, Show)+data StatDType = StatAverage | StatMedian | StatBest deriving (Eq, Show) -- | Compute a statistic. statD :: StatDType -> [Double] -> Double statD StatBest = tenth statD StatMedian = median statD StatAverage = average-statD StatSecs = averageSecs -- | Compute a list of statistics. statDs :: StatDType -> [[Double]] -> [Double] statDs StatBest = fmap tenth . List.transpose statDs StatMedian = fmap median . List.transpose statDs StatAverage = fmap average . List.transpose-statDs StatSecs = fmap averageSecs . List.transpose -- | Parse command-line 'StatDType' options. parseStatD :: Parser StatDType@@ -69,7 +62,6 @@ flag' StatBest (long "best" <> help "report upper decile") <|> flag' StatMedian (long "median" <> help "report median") <|> flag' StatAverage (long "average" <> help "report average")- <|> flag' StatSecs (long "averagesecs" <> help "report average in seconds") <|> pure StatAverage -- | Add a statistic to a State Map
src/Perf/Time.hs view
@@ -1,13 +1,16 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE ViewPatterns #-} --- | 'tick' uses the rdtsc chipset to measure time performance of a computation.------ The measurement unit is one oscillation of the chip crystal as measured by the <https://en.wikipedia.org/wiki/Time_Stamp_Counter rdtsc> instruction which inspects the TSC register.------ For reference, a computer with a frequency of 2 GHz means that one cycle is equivalent to 0.5 nanoseconds.+-- | Use of 'System.Clock' from the [clock](https://hackage.haskell.org/package/clock) library to measure time performance of a computation. module Perf.Time- ( tick_,+ ( Nanos,+ defaultClock,+ toSecs,+ nanosWith,+ nanos,+ tick_, warmup,+ tickWith, tick, tickWHNF, tickLazy,@@ -16,179 +19,187 @@ tickIO, ticks, ticksIO,- Cycles (..),- cputime,- clocktime, time, times,+ timesWith, stepTime, ) where import Control.DeepSeq import Control.Monad (replicateM_)-import Data.Fixed-import Data.Time-import GHC.Word (Word64) import Perf.Types-import System.CPUTime-import System.CPUTime.Rdtsc+import System.Clock import Prelude --- | Clock count.-newtype Cycles = Cycles {word :: Word64}- deriving (Show, Read, Eq, Ord, Num, Real, Enum, Integral)+-- | A performance measure of number of nanoseconds.+type Nanos = Integer -instance Semigroup Cycles where- (<>) = (+)+-- | Convert 'Nanos' to seconds.+toSecs :: Nanos -> Double+toSecs ns = fromIntegral ns / 1e9 -instance Monoid Cycles where- mempty = 0+-- | 'MonotonicRaw' is the default for macOS & linux, at around 42 nano time resolution, and a 'tick_' measurement of around 170 nanos. For Windows, 'ThreadCPUTime' has a similar time resolution at 42 nanos and a 'tick_' of around 500 nanos.+defaultClock :: Clock --- | tick_ measures the number of cycles it takes to read the rdtsc chip twice: the difference is then how long it took to read the clock the second time.-tick_ :: IO Cycles+#ifdef mingw32_HOST_OS+defaultClock = ThreadCPUTime+#else+defaultClock = MonotonicRaw+#endif++-- | A single 'defaultClock' reading (note that the absolute value is not meaningful).+nanos :: IO Nanos+nanos = nanosWith defaultClock++-- | A single reading of a specific 'Clock'.+nanosWith :: Clock -> IO Nanos+nanosWith c = toNanoSecs <$> getTime c++-- | tick_ measures the number of nanos it takes to read the clock.+tick_ :: IO Nanos tick_ = do- t <- rdtsc- t' <- rdtsc- pure (Cycles (t' - t))+ t <- nanos+ t' <- nanos+ pure (t' - t) --- | Warm up the register, to avoid a high first measurement. Without a warmup, one or more larger values can occur at the start of a measurement spree, and often are in the zone of an L2 miss.+-- | Warm up the clock, to avoid a high first measurement. Without a warmup, one or more larger values can occur at the start of a measurement spree, and often are in the zone of an L2 miss. warmup :: Int -> IO () warmup n = replicateM_ n tick_ +-- | tick from a specific 'Clock'+tickWith :: Clock -> (a -> b) -> a -> IO (Nanos, b)+tickWith c !f !a = do+ !t <- nanosWith c+ !a' <- pure $! f a+ !t' <- nanosWith c+ pure (t' - t, a')+{-# INLINEABLE tickWith #-}+ -- | /tick f a/ -- -- - strictly evaluates f and a to WHNF--- - starts the cycle counter+-- - reads the clock -- - strictly evaluates f a to WHNF--- - stops the cycle counter--- - returns (number of cycles, f a)-tick :: (a -> b) -> a -> IO (Cycles, b)+-- - reads the clock+-- - returns (nanos, f a)+tick :: (a -> b) -> a -> IO (Nanos, b) tick !f !a = do- !t <- rdtsc+ !t <- nanos !a' <- pure $! f a- !t' <- rdtsc- pure (Cycles (t' - t), a')+ !t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tick #-} -- | /tickWHNF f a/ ----- - starts the cycle counter+-- - reads the clock -- - strictly evaluates f a to WHNF (this may also kick off thunk evaluation in f or a which will also be captured in the cycle count)--- - stops the cycle counter--- - returns (number of cycles, f a)-tickWHNF :: (a -> b) -> a -> IO (Cycles, b)+-- - reads the clock+-- - returns (nanos, f a)+tickWHNF :: (a -> b) -> a -> IO (Nanos, b) tickWHNF f a = do- !t <- rdtsc+ !t <- nanos !a' <- pure $! f a- !t' <- rdtsc- pure (Cycles (t' - t), a')+ !t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tickWHNF #-} -- | /tickLazy f a/ ----- - starts the cycle counter+-- - reads the clock -- - lazily evaluates f a--- - stops the cycle counter--- - returns (number of cycles, f a)-tickLazy :: (a -> b) -> a -> IO (Cycles, b)+-- - reads the clock+-- - returns (nanos, f a)+tickLazy :: (a -> b) -> a -> IO (Nanos, b) tickLazy f a = do- t <- rdtsc+ t <- nanos let a' = f a- t' <- rdtsc- pure (Cycles (t' - t), a')+ t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tickLazy #-} -- | /tickForce f a/ -- -- - deeply evaluates f and a,--- - starts the cycle counter+-- - reads the clock -- - deeply evaluates f a--- - stops the cycle counter--- - returns (number of cycles, f a)-tickForce :: (NFData a, NFData b) => (a -> b) -> a -> IO (Cycles, b)+-- - reads the clock+-- - returns (nanos, f a)+tickForce :: (NFData a, NFData b) => (a -> b) -> a -> IO (Nanos, b) tickForce (force -> !f) (force -> !a) = do- !t <- rdtsc+ !t <- nanos !a' <- pure (force (f a))- !t' <- rdtsc- pure (Cycles (t' - t), a')+ !t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tickForce #-} -- | /tickForceArgs f a/ -- -- - deeply evaluates f and a,--- - starts the cycle counter+-- - reads the clock -- - strictly evaluates f a to WHNF--- - stops the cycle counter--- - returns (number of cycles, f a)-tickForceArgs :: (NFData a) => (a -> b) -> a -> IO (Cycles, b)+-- - reads the clock+-- - returns (nanos, f a)+tickForceArgs :: (NFData a) => (a -> b) -> a -> IO (Nanos, b) tickForceArgs (force -> !f) (force -> !a) = do- !t <- rdtsc+ !t <- nanos !a' <- pure $! f a- !t' <- rdtsc- pure (Cycles (t' - t), a')+ !t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tickForceArgs #-} -- | measures an /IO a/-tickIO :: IO a -> IO (Cycles, a)+tickIO :: IO a -> IO (Nanos, a) tickIO a = do- !t <- rdtsc+ !t <- nanos !a' <- a- !t' <- rdtsc- pure (Cycles (t' - t), a')+ !t' <- nanos+ pure (t' - t, a') {-# INLINEABLE tickIO #-} +-- | measures an /IO a/+tickIOWith :: Clock -> IO a -> IO (Nanos, a)+tickIOWith c a = do+ !t <- nanosWith c+ !a' <- a+ !t' <- nanosWith c+ pure (t' - t, a')+{-# INLINEABLE tickIOWith #-}+ -- | n measurements of a tick ----- returns a list of Cycles and the last evaluated f a-ticks :: Int -> (a -> b) -> a -> IO ([Cycles], b)+-- returns a list of Nanos and the last evaluated f a+ticks :: Int -> (a -> b) -> a -> IO ([Nanos], b) ticks = multi tick {-# INLINEABLE ticks #-} -- | n measurements of a tickIO ----- returns an IO tuple; list of Cycles and the last evaluated f a-ticksIO :: Int -> IO a -> IO ([Cycles], a)+-- returns an IO tuple; list of Nanos and the last evaluated f a+ticksIO :: Int -> IO a -> IO ([Nanos], a) ticksIO = multiM tickIO {-# INLINEABLE ticksIO #-} -- | tick as a 'StepMeasure'-stepTime :: StepMeasure IO Cycles+stepTime :: StepMeasure IO Nanos stepTime = StepMeasure start stop where- start = Cycles <$> rdtsc- stop r = fmap (\x -> x - r) (Cycles <$> rdtsc)+ start = nanos+ stop r = fmap (\x -> x - r) nanos {-# INLINEABLE stepTime #-} --- | a measure using 'getCPUTime' from System.CPUTime (unit is picoseconds)-cputime :: StepMeasure IO Integer-cputime = StepMeasure start stop- where- start = getCPUTime- stop a = do- t <- getCPUTime- return $ t - a---- | a measure using 'getCurrentTime' (unit is seconds)-clocktime :: StepMeasure IO Double-clocktime = StepMeasure start stop- where- start = getCurrentTime- stop a = do- t <- getCurrentTime- return $ fromNominalDiffTime $ diffUTCTime t a--fromNominalDiffTime :: NominalDiffTime -> Double-fromNominalDiffTime t = fromInteger i * 1e-12- where- (MkFixed i) = nominalDiffTimeToSeconds t- -- | tick as a 'Measure'-time :: Measure IO Cycles+time :: Measure IO Nanos time = Measure tick tickIO {-# INLINEABLE time #-} -- | tick as a multi-Measure-times :: Int -> Measure IO [Cycles]+times :: Int -> Measure IO [Nanos] times n = Measure (ticks n) (ticksIO n) {-# INLINEABLE times #-}++-- | tickWith as a multi-Measure+timesWith :: Clock -> Int -> Measure IO [Nanos]+timesWith c n = Measure (multi (tickWith c) n) (multiM (tickIOWith c) n)+{-# INLINEABLE timesWith #-}
src/Perf/Types.hs view
@@ -222,8 +222,7 @@ (\((a, m), m') -> (a, (m', m))) <$> runPerfT outerm- ( fam label (runPerfT meas p)- )+ (fam label (runPerfT meas p)) -- | run a PerfT and calculate excess performance over the entire computation slop :: (MonadIO m, Num t, Semigroup t) => Text -> Measure m t -> PerfT m t a -> m (a, Map.Map Text t)@@ -231,8 +230,7 @@ (\((a, m), m') -> (a, m <> Map.insert "slop" (m' Map.! l - Map.foldl' (+) 0 m) m')) <$> runPerfT meas- ( fam l (runPerfT meas p)- )+ (fam l (runPerfT meas p)) -- | run a multi PerfT and calculate excess performance over the entire computation slops :: (MonadIO m, Num t, Semigroup t) => Int -> Measure m t -> PerfT m [t] a -> m (a, (Map.Map Text t, Map.Map Text [t]))@@ -240,5 +238,4 @@ (\((a, ms), m') -> (a, (Map.insert "slop" (m' Map.! "outer" - Map.foldl' (+) 0 (fmap sum ms)) m', ms))) <$> runPerfT meas- ( fam "outer" (runPerfT (repeated n meas) p)- )+ (fam "outer" (runPerfT (repeated n meas) p))