packages feed

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 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)++[![img](https://img.shields.io/hackage/v/perf.svg)](https://hackage.haskell.org/package/perf) [![img](https://github.com/tonyday567/perf/workflows/haskell-ci/badge.svg)](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)++&#x2026; 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">&#xa0;</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&rsquo;</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">&#xa0;</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&rsquo;</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">&#xa0;</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&rsquo;</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">&gt;250</td>+<td class="org-left">&gt;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))