perf-analysis (empty) → 0.0.1.0
raw patch · 6 files changed
+466/−0 lines, 6 filesdep +basedep +containersdep +deepseqsetup-changed
Dependencies added: base, containers, deepseq, formatting, optparse-generic, perf, perf-analysis, protolude, scientific, tdigest, text, vector
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- examples/examples.hs +250/−0
- perf-analysis.cabal +68/−0
- src/Perf/Analysis.hs +105/−0
- stack.yaml +11/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Tony Day (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Tony Day nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/examples.hs view
@@ -0,0 +1,250 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Main where++import Data.Scientific+import Formatting+import Options.Generic+import Perf+import Perf.Analysis+import Protolude hiding ((%))+import qualified Data.Map as Map+import qualified Data.Text as Text+import qualified Data.Vector as V+import qualified Data.Vector.Storable as S+import qualified Data.Vector.Unboxed as U++data Opts = Opts+ { runs :: Maybe Int -- <?> "number of runs"+ , sumTo :: Maybe Int -- <?> "sum to this number"+ , sumsTo :: Maybe [Int] -- <?> "sum to these numbers"+ } deriving (Generic, Show)++instance ParseField [Int]++instance ParseRecord Opts++-- | useful as a benchmark versus the `ticks` functions being imported+ticksInFile :: (NFData b) => Int -> (a -> b) -> a -> IO ([Cycle], b)+ticksInFile n0 f a = go f a n0 []+ where+ go f a n ts+ | n <= 0 = pure (ts, f a)+ | otherwise = do+ (t,_) <- tick f a+ go f a (n - 1) (t:ts)++-- | interposition of a lambda+fLambda :: Int -> Int+fLambda = \x -> foldl' (+) 0 [1 .. x]++-- | monomorphic version+fMono :: Int -> Int+fMono x = foldl' (+) 0 [1 .. x]++-- | polymorphic function version+fPoly :: (Integral b) => b -> b+fPoly x = foldl' (+) 0 [1 .. x]++main :: IO ()+main = do+ o :: Opts <- getRecord "perf examples"+ let !n = fromMaybe 1000 (runs o)+ let !a = fromMaybe 1000 (sumTo o)+ let !as = [1, 10, 100, 1000 :: Int]++ writeFile "other/run.md" $+ code+ [ formatInt "number of runs:" 2 n+ , formatInt "accumulate to:" 2 a+ , sformat ((right 24 ' ' %. stext)%stext) "function:" "foldl' (+) 0"+ ]++ -- | tick_ testing+ prewarmup <- tick_+ _ <- warmup 100+ onetick <- tick_+ ticks' <- replicateM 10 tick_+ manyticks <- replicateM 1000000 tick_+ let avticks = average manyticks+ let qticks = deciles 5 manyticks+ let tick999 = percentile 0.999 manyticks+ let tick99999 = percentile 0.99999 manyticks+ let tick99 = percentile 0.99 manyticks+ let tick40 = percentile 0.4 manyticks+ writeFile "other/tick_.md" $+ code+ [ "pre warmup: " <> show prewarmup <> " cycles"+ , "one tick_: " <> show onetick <> " cycles"+ , "next 10: " <> show ticks'+ , "average over 1m: " <> sformat (fixed 2) avticks <> " cycles"+ , "99.999% perc: " <> sformat commas (floor tick99999 :: Integer)+ , "99.9% perc: " <> sformat (fixed 2) tick999+ , "99th perc: " <> sformat (fixed 2) tick99+ , "40th perc: " <> sformat (fixed 2) tick40+ , "[min, 20th, .. 80th, max]:"+ , mconcat (sformat (" " % prec 4) . fromFloatDigits <$> qticks)+ ]++ -- | tick tests+ (t, resultPrime) <- tick fMono a+ print resultPrime+ (t2,_) <- tick fMono a+ (t3,_) <- tick fMono a+ (t',_) <- tick' fMono a+ (tio,_) <- tickIO (pure $ fMono a)+ -- mystery: if tickNoinline is used at all, all tick computations slow down+ -- by a factor of 2 (or by a function call)+ -- (tn,_) <- tickNoinline fMono a+ t10 <- sequence $ replicate 10 (tick fMono a)+ t10io <- sequence $ replicate 10 (tickIO $ pure (fMono a))+ -- t10n <- sequence $ replicate 10 (tickNoinline fMono a)+ t10' <- sequence $ replicate 10 (tick fMono a)+ writeFile "other/tick.md" $+ code+ [ "sum to " <> show a+ , "first measure: " <> show t <> " cycles"+ , "second measure: " <> show t2 <> " cycles"+ , "third measure: " <> show t3 <> " cycles"+ , "tick': " <> show t' <> " cycles"+ , "tickIO: " <> show tio <> " cycles"+ -- , "tickNoinline: " <> show tn <> " cycles"+ , "tick * 10: " <> show (fst <$> t10)+ , "tickIO * 10: " <> show (fst <$> t10io)+ -- , "tickNoinline * 10: " <> show (fst <$> t10n)+ , "tick * 10: " <> show (fst <$> t10')+ ]++ -- | ticks and ticksIO+ (rpure, _) <- ticks n fMono a+ (rpurePoly, _) <- ticks n fPoly a+ (rpureLambda, _) <- ticks n fLambda a+ (rio, _) <- ticksIO n (pure $ fMono a)+ (rioPoly, _) <- ticksIO n (pure $ fPoly a)+ (rioLambda, _) <- ticksIO n (pure $ fLambda a)++ writeFile "other/ticks.md" $+ code [ "acc = " <> show a <> " n = " <> show n+ , formatRunHeader+ , formatRun "monomorphic" 2 $ rpure+ , formatRun "includes lambda" 2 $ rpureLambda+ , formatRun "polymorphic" 2 $ rpurePoly+ , formatRun "ticksIO mono" 2 $ rio+ , formatRun "ticksIO lambda" 2 $ rioLambda+ , formatRun "ticksIO poly" 2 $ rioPoly+ ]++ gaps <- sequence $ (\a -> formatGap a <$> tickIO (ticks n fPoly a)) <$> as+ writeFile "other/ticksCost.md" $ code gaps++ -- | ns testing+ css <-+ fmap (fmap fst) <$>+ sequence ((replicateM n . tick fMono) <$> as)+ let r12 =+ "(replicateM n . tick fMono) <$> as: " <>+ mconcat (sformat (" " %prec 3) <$>+ (fromFloatDigits . percentile 0.4 <$> css))+ (ts, _) <- ns (ticks n fMono) as+ let r13 =+ "ns (ticks n fMono) as: " <>+ mconcat (sformat (" " %prec 3) <$>+ (fromFloatDigits . percentile 0.4 <$> ts))+ writeFile "other/tickns.md" $+ code ["sum to's " <> show as, r13, r12]++ -- | vectors+ let asl :: [Int]+ asl = [1 .. a]+ let suml :: [Int] -> Int+ suml = foldl' (+) 0+ (rlist, _) <- ticks n suml asl+ let sumv :: V.Vector Int -> Int+ sumv = V.foldl (+) 0+ let asv :: V.Vector Int =+ V.generate a identity+ (rboxed, _) <- ticks n sumv asv+ let sums :: S.Vector Int -> Int+ sums = S.foldl (+) 0+ let ass :: S.Vector Int =+ S.generate a identity+ (rstorable, _) <- ticks n sums ass+ let sumu :: U.Vector Int -> Int+ sumu = U.foldl (+) 0+ let asu :: U.Vector Int =+ U.generate a identity+ (runboxed, _) <- ticks n sumu asu+ writeFile "other/vector.md" $+ code+ [ "sum to " <> show a+ , formatRun "ticks list" 2 $ rlist+ , formatRun "ticks boxed" 2 $ rboxed+ , formatRun "ticks storable" 2 $ rstorable+ , formatRun "ticks unboxed" 2 $ runboxed+ ]++ -- WHNF+ (rnf, _) <- tick (fmap fMono) (Just a)+ (rwhnf, _) <- tick (fmap fMono) (Just a)+ (rnfs, _) <- ticks n (fmap fMono) (Just a)+ (rwhnfs, _) <- ticksWHNF n (fmap fMono) (Just a)+ (rnfio, _) <- tickIO (pure $ fmap fMono (Just a))+ (rwhnfio, _) <- tickWHNFIO (pure $ fmap fMono (Just a))+ (rnfsio, _) <- ticksIO n (pure $ fmap fMono (Just a))+ (rwhnfsio, _) <- ticksWHNFIO n (pure $ fmap fMono (Just a))++ writeFile "other/whnf.md" $ code+ [ "sum to " <> show a+ , formatInt "tick" 2 $ rnf+ , formatInt "tickWHNF" 2 $ rwhnf+ , formatRun "ticks" 2 rnfs+ , formatRun "ticksWHNF" 2 rwhnfs+ , formatInt "tickIO" 2 rnfio+ , formatInt "tickWHNFIO" 2 rwhnfio+ , formatRun "ticksIO" 2 rnfsio+ , formatRun "ticksWHNFIO" 2 rwhnfsio+ ]++ -- perf basics+ (result, cs) <- runPerfT $+ perf "sum" cycles (pure $ foldl' (+) 0 [0 .. a])+ putStrLn (show (result, cs) :: Text)++ -- PerfT example+ -- prior to Perfification+ result <- do+ txt <- readFile "perf-analysis/examples/examples.hs"+ let n = Text.length txt+ let x = foldl' (+) 0 [1..n]+ putStrLn $ "sum of one to number of characters is: " <>+ (show x :: Text)+ pure (n, x)++ -- post-Perfification+ (result', ms) <- runPerfT $ do+ txt <- perf "file read" cycles $ readFile "perf-analysis/examples/examples.hs"+ n <- perf "length" cycles $ pure (Text.length txt)+ x <- perf "sum" cycles $ pure (foldl' (+) 0 [1..n])+ perf "print to screen" cycles $+ putStrLn $ "sum of one to number of characters is: " <>+ (show x :: Text)+ pure (n, x)++ when (result == result') $ print "PerfT has no effect on original computation"++ let fmt = sformat ((right 40 ' ' %. stext) %prec 3 % " " % stext)+ writeFile "other/perf.md" $+ "\nperf cycle measurements\n---\n" <>+ code ((\(t,c) -> fmt t (int2Sci c) "cycles") <$> Map.toList ms)+
+ perf-analysis.cabal view
@@ -0,0 +1,68 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 97459822690cecba4e6db836b86eedf2d10cc0a7f8ff32e4c525614241b4af82++name: perf-analysis+version: 0.0.1.0+synopsis: analysis example using perf+description: Analytical tools to use with perf. +category: performance+homepage: https://github.com/tonyday567/perf#readme+bug-reports: https://github.com/tonyday567/perf/issues+author: Tony Day+maintainer: tonyday567@gmail.com+copyright: Tony Day+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10+extra-source-files:+ stack.yaml++source-repository head+ type: git+ location: https://github.com/tonyday567/perf+ subdir: perf-analysis++library+ exposed-modules:+ Perf.Analysis+ other-modules:+ Paths_perf_analysis+ hs-source-dirs:+ src+ build-depends:+ base >=4.7 && <4.12+ , deepseq+ , formatting+ , perf+ , protolude+ , scientific+ , tdigest+ , text+ , vector+ default-language: Haskell2010++executable perf-examples+ main-is: examples.hs+ other-modules:+ Paths_perf_analysis+ hs-source-dirs:+ examples+ ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -rtsopts -O2+ build-depends:+ base >=4.7 && <4.12+ , containers+ , deepseq+ , formatting+ , optparse-generic+ , perf+ , perf-analysis+ , protolude+ , scientific+ , tdigest+ , text+ , vector+ default-language: Haskell2010
+ src/Perf/Analysis.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE DataKinds #-}+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE NoImplicitPrelude #-}++-- | analytical functionality for perf+module Perf.Analysis where++import Data.Scientific+import Data.TDigest+import Formatting+import Perf+import Protolude hiding ((%))+import qualified Data.Text as Text++-- | compute deciles+--+-- >>> deciles 5 [1..1000]+-- [1.0,200.5,400.5,600.5,800.5,1000.0]+--+-- > c5 <- decile 5 <$> ticks n f a+--+deciles :: (Functor f, Foldable f, Integral a) => a -> f a -> [Double]+deciles n xs =+ (\x -> fromMaybe 0 $+ quantile x (tdigest (fromIntegral <$> xs) :: TDigest 25)) <$>+ ((/ fromIntegral n) . fromIntegral <$> [0 .. n])++-- | compute a percentile+--+-- >>> percentile 0.1 [1..1000]+-- 100.5+--+-- > c <- percentile 0.4 . fst <$> ticks n f a+--+percentile :: (Functor f, Foldable f, Integral a) => Double -> f a -> Double+percentile p xs = fromMaybe 0 $ quantile p (tdigest (fromIntegral <$> xs) :: TDigest 25)++-- | fixed precision for a Scientific+prec :: Int -> Format r (Scientific -> r)+prec n = scifmt Exponent (Just n)++-- | convert an integral to a Scientific+--+-- >>> sformat (left 8 ' ' %. prec 1) (int2Sci 1000)+-- " 1.0e3"+--+int2Sci :: (Integral a) => a -> Scientific+int2Sci n = scientific (fromIntegral n) 0++-- | format an Integral as a Scientific with a label and precision+formatInt :: (Integral a) => Text -> Int -> a -> Text+formatInt label p x =+ sformat+ ((right 24 ' ' %. stext) %+ (left 8 ' ' %. prec p)) label (int2Sci x)++-- | format the first 5 results, and then the 40th percentile+formatRun :: (Integral a) => Text -> Int -> [a] -> Text+formatRun label p xs =+ sformat+ ((right 24 ' ' %. stext) % stext %+ (left 9 ' ' %. prec p) %+ (left 9 ' ' %. prec p))+ label+ (Text.intercalate " " $ sformat (left 8 ' ' %. prec p) .+ int2Sci <$>+ take 3 xs)+ (fromFloatDigits $ percentile 0.5 xs)+ (fromFloatDigits $ average xs)++-- | header for formatRun+formatRunHeader :: Text+formatRunHeader =+ sformat+ ((right 24 ' ' %. stext) %+ (left 8 ' ' %. stext) %+ (left 9 ' ' %. stext) %+ (left 9 ' ' %. stext) %+ (left 9 ' ' %. stext) %+ (left 9 ' ' %. stext))+ "run"+ "first"+ "2nd"+ "3rd"+ "median"+ "av."++-- | format a tick result with an inside and outside measurement+formatGap :: (Integral a) => Int -> (a, ([a],b)) -> Text+formatGap a (co, (ci, _)) =+ sformat+ ("n = " %(left 7 ' ' %. prec 3)%" outside: "%(left 7 ' ' %. prec 3)%" inside: "%(left 7 ' ' %. prec 3)%" gap: "%(left 7 ' ' %. prec 3))+ (int2Sci a) (int2Sci co) (int2Sci $ sum ci) (int2Sci $ co - sum ci)++-- | place markdown backtics around text+code :: [Text] -> Text+code cs = "\n```\n" <> Text.intercalate "\n" cs <> "\n```\n"
+ stack.yaml view
@@ -0,0 +1,11 @@+resolver: nightly-2018-04-05++packages:+ - .+ - ../perf++extra-deps:+ - rdtsc-1.3.0.1+ - tdigest-0.2++allow-newer: true