packages feed

criterion-plus (empty) → 0.1.0

raw patch · 10 files changed

+837/−0 lines, 10 filesdep +HTFdep +HUnitdep +QuickChecksetup-changed

Dependencies added: HTF, HUnit, QuickCheck, base, criterion, deepseq, loch-th, monad-control, mtl, optparse-applicative, placeholders, statistics, string-conversions, system-fileio, system-filepath, text, th-printf, transformers, transformers-base, vector

Files

+ LICENSE view
@@ -0,0 +1,22 @@+Copyright (c) 2014, Nikita Volkov++Permission is hereby granted, free of charge, to any person+obtaining a copy of this software and associated documentation+files (the "Software"), to deal in the Software without+restriction, including without limitation the rights to use,+copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the+Software is furnished to do so, subject to the following+conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ criterion-plus.cabal view
@@ -0,0 +1,199 @@+name:+  criterion-plus+version:+  0.1.0+synopsis:+  Enhancement of the "criterion" benchmarking library+description:+  A dome library over \"criterion\",+  which enhances it with the following features:+  .+  * Support for partial measuring, e.g., exclusion of the setup/teardown phases.+  .+  * Generation of multiple unrelated comparison reports.+  .+  * A flexible monadic API, +  which allows you to benchmark monad transformers and +  control which parts you actually want measured.+  .+category:+  Benchmarking+homepage:+  https://github.com/nikita-volkov/criterion-plus +bug-reports:+  https://github.com/nikita-volkov/criterion-plus/issues +author:+  Nikita Volkov <nikita.y.volkov@mail.ru>+maintainer:+  Nikita Volkov <nikita.y.volkov@mail.ru>+copyright:+  (c) 2014, Nikita Volkov+license:+  MIT+license-file:+  LICENSE+build-type:+  Simple+cabal-version:+  >=1.10+++source-repository head+  type:+    git+  location:+    git://github.com/nikita-volkov/criterion-plus.git+++library+  hs-source-dirs:+    library+  other-modules:+    CriterionPlus.Prelude.Basic+    CriterionPlus.Prelude.Data+    CriterionPlus.Prelude.Transformers+    CriterionPlus.Monads+    CriterionPlus.CSI+  exposed-modules:+    CriterionPlus+  build-depends:+    -- options:+    optparse-applicative == 0.8.*,+    -- criterion:+    criterion == 0.8.*,+    statistics == 0.10.*,+    -- file-system:+    system-filepath == 0.4.*,+    system-fileio == 0.3.*,+    -- data:+    vector > 0.7 && < 0.11,+    text,+    string-conversions == 0.3.*,+    th-printf == 0.3.*,+    -- control:+    monad-control == 0.3.*,+    transformers-base == 0.4.*,+    deepseq == 1.3.*,+    transformers,+    mtl,+    -- debugging:+    loch-th == 0.2.*,+    placeholders == 0.1.*,+    -- general:+    base >= 4.5 && < 5+  default-extensions:+    Arrows+    BangPatterns+    ConstraintKinds+    DataKinds+    DefaultSignatures+    DeriveDataTypeable+    DeriveFunctor+    DeriveGeneric+    EmptyDataDecls+    FlexibleContexts+    FlexibleInstances+    FunctionalDependencies+    GADTs+    GeneralizedNewtypeDeriving+    ImpredicativeTypes+    LambdaCase+    LiberalTypeSynonyms+    MultiParamTypeClasses+    MultiWayIf+    NoImplicitPrelude+    NoMonomorphismRestriction+    OverloadedStrings+    PatternGuards+    ParallelListComp+    QuasiQuotes+    RankNTypes+    RecordWildCards+    ScopedTypeVariables+    StandaloneDeriving+    TemplateHaskell+    TupleSections+    TypeFamilies+    TypeOperators+  default-language:+    Haskell2010+++test-suite internal-tests+  type:             +    exitcode-stdio-1.0+  hs-source-dirs:   +    internal-tests+    library+  main-is:          +    InternalTests.hs+  ghc-options:+    -O2+    -threaded+    "-with-rtsopts=-N"+  build-depends:+    -- testing:+    QuickCheck,+    HUnit,+    HTF == 0.11.*,+    -- options:+    optparse-applicative == 0.8.*,+    -- criterion:+    criterion == 0.8.*,+    statistics == 0.10.*,+    -- file-system:+    system-filepath == 0.4.*,+    system-fileio == 0.3.*,+    -- data:+    vector > 0.7 && < 0.11,+    text,+    string-conversions == 0.3.*,+    th-printf == 0.3.*,+    -- control:+    monad-control == 0.3.*,+    transformers-base == 0.4.*,+    deepseq == 1.3.*,+    transformers,+    mtl,+    -- debugging:+    loch-th == 0.2.*,+    placeholders == 0.1.*,+    -- general:+    base >= 4.5 && < 5+  default-extensions:+    Arrows+    BangPatterns+    ConstraintKinds+    DataKinds+    DefaultSignatures+    DeriveDataTypeable+    DeriveFunctor+    DeriveGeneric+    EmptyDataDecls+    FlexibleContexts+    FlexibleInstances+    FunctionalDependencies+    GADTs+    GeneralizedNewtypeDeriving+    ImpredicativeTypes+    LambdaCase+    LiberalTypeSynonyms+    MultiParamTypeClasses+    MultiWayIf+    NoImplicitPrelude+    NoMonomorphismRestriction+    OverloadedStrings+    PatternGuards+    ParallelListComp+    QuasiQuotes+    RankNTypes+    RecordWildCards+    ScopedTypeVariables+    StandaloneDeriving+    TemplateHaskell+    TupleSections+    TypeFamilies+    TypeOperators+  default-language:+    Haskell2010+
+ internal-tests/InternalTests.hs view
@@ -0,0 +1,54 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-}++import Test.Framework+import Test.QuickCheck.Monadic+import CriterionPlus.Prelude.Basic+import CriterionPlus.Prelude.Transformers+import qualified CriterionPlus.Monads as M+import qualified Criterion.Environment as C+import qualified Data.Vector.Unboxed as V++main = htfMain $ htf_thisModulesTests+++-- * Tests+-------------------------++test_aMoreComplexPureBenchmarkTakesLonger = do+  replicateM_ 10 $ do+    [a, b] <- runStandoff 100 $ do+      M.subject "shorter" $ do+        M.nf pureCalc 1+      M.subject "longer" $ do+        M.nf pureCalc 4+    assertEqual LT $ compareResults a b++test_aMoreComplexImpureBenchmarkTakesLonger = do+  replicateM_ 10 $ do+    [a, b] <- runStandoff 100 $ do+      M.subject "shorter" $ do+        M.nfIO $ return $ pureCalc 1+      M.subject "longer" $ do+        M.nfIO $ return $ pureCalc 4+    assertEqual LT $ compareResults a b++test_timerControlsDontAffectTheResults = unitTestPending ""+++-- * Utils+-------------------------++runStandoff :: Int -> M.Standoff a -> IO [M.SubjectReport]+runStandoff samples = M.runStandoff 1 "test-standoff" env settings where+  -- env = unsafePerformIO $ M.runBenchmark settings $ M.Benchmark $ lift $ ask+  env = C.Environment 0 0+  settings = M.Settings "dist/test" samples++compareResults :: M.SubjectReport -> M.SubjectReport -> Ordering+compareResults = on compare $ \(_, v, _, _) -> V.sum v++pureCalc :: Int -> [Int]+pureCalc a = map (+a) $ enumFromTo 0 $ 10^3*a++apxEqual :: Double -> Double -> Bool+apxEqual a b = a > b * 0.95 && a < b * 1.05
+ library/CriterionPlus.hs view
@@ -0,0 +1,66 @@+-- |+-- Using this library you can create executables with benchmarks.+-- It automatically implants a command-line options parser and info generator.+-- +-- Here is an example of how this library is supposed to be used:+-- +-- > import CriterionPlus+-- > import qualified SomeMySQLLib as MySQL+-- > import qualified SomePostgreSQLLib as PostgreSQL+-- > +-- > main = +-- >   benchmark $ do+-- >     standoff "Inserting rows" $ do+-- >       subject "MySQL" $ do+-- >         -- Exclude the "setup" phase from measurement:+-- >         pause+-- >         connection <- liftIO $ MySQL.openConnection+-- >         -- Measure what we want:+-- >         continue+-- >         liftIO $ MySQL.insertAThousandRows connection+-- >         -- Exclude the "teardown" phase from measurement:+-- >         pause+-- >         liftIO $ MySQL.closeConnection connection+-- >       subject "PostgreSQL" $ do+-- >         -- This is how we can exclude the "setup" phase from monad transformers:+-- >         pause+-- >         PostgreSQL.runSession $ do+-- >           lift $ continue+-- >           PostgreSQL.insertAThousandRows+-- >           -- Exclude "teardown":+-- >           lift $ pause+-- >     -- Each standoff generates an independent report file:+-- >     standoff "Querying" $ do+-- >       subject "MySQL" $ error "So on..."+-- >       subject "PostgreSQL" $ error "So on..."+-- +module CriterionPlus+(+  -- * IO+  benchmark,+  -- * Benchmark+  Benchmark,+  standoff,+  -- * Standoff+  Standoff,+  group,+  subject,+  -- * Subject+  Subject,+  continue,+  pause,+  -- ** Evaluation+  whnf,+  nf,+  nfIO,+  whnfIO,+  -- * Shared Types+  Name,+)+where++import CriterionPlus.Monads++++
+ library/CriterionPlus/CSI.hs view
@@ -0,0 +1,26 @@+module CriterionPlus.CSI where++import CriterionPlus.Prelude.Basic+import CriterionPlus.Prelude.Data++esc = "\ESC["+csi = "\o033["+cursorUp n = csi <> (cs $ show n) <> "A"+cursorDown n = csi <> (cs $ show n) <> "B"+-- | Moves cursor to beginning of the line n (default 1) lines down.+cursorNextLine n = csi <> (cs $ show n) <> "E"+-- | Moves cursor to beginning of the line n (default 1) lines up.+cursorPreviousLine n = csi <> (cs $ show n) <> "F"+-- | Moves the cursor to column n.+cursorHorizontalAbsolute n = csi <> (cs $ show n) <> "G"+-- | Clears part of the screen. If n is zero (or missing), clear from cursor to end of screen. If n is one, clear from cursor to beginning of the screen. If n is two, clear entire screen (and moves cursor to upper left on MS-DOS ANSI.SYS).+eraseData n = csi <> (cs $ show n) <> "J"+-- | Erases part of the line. If n is zero (or missing), clear from cursor to the end of the line. If n is one, clear from cursor to beginning of the line. If n is two, clear entire line. Cursor position does not change.+eraseInLine n = csi <> (cs $ show n) <> "K"+eraseLineToEnd = eraseInLine 0+eraseLineToBeginning = eraseInLine 1+eraseLine = eraseInLine 2+saveCursorPosition = csi <> "s"+restoreCursorPosition = csi <> "u"+hideCursor = csi <> "?25l"+showCursor = csi <> "?25h"
+ library/CriterionPlus/Monads.hs view
@@ -0,0 +1,311 @@+-- |+-- Builder monads.+module CriterionPlus.Monads where++import CriterionPlus.Prelude.Basic+import CriterionPlus.Prelude.Data+import CriterionPlus.Prelude.Transformers+import qualified Criterion.Config as C+import qualified Criterion.Report as C+import qualified Criterion.Types as C+import qualified Criterion.Environment as C+import qualified Criterion.Measurement as C+import qualified Criterion.IO.Printf as C+import qualified Criterion.Analysis as C+import qualified Criterion.Monad as C+import qualified Statistics.Resampling.Bootstrap as S+import qualified Statistics.Types as S+import qualified Data.Text as T+import qualified Data.Vector.Unboxed as V+import qualified Data.Vector.Unboxed.Mutable as VM+import qualified Filesystem.Path.CurrentOS as FS+import qualified Filesystem as FS+import qualified Options.Applicative as O+import qualified CriterionPlus.CSI as CSI+import qualified System.IO+++-- * Shared Types+-------------------------++type Name = StrictText++data Settings = +  Settings {+    reportsDir :: FilePath,+    samplesAmount :: Int+  }++type Environment = C.Environment+++-- * IO+-------------------------++-- |+-- Parse the command line options and run the benchmark.+-- +benchmark :: Benchmark () -> IO ()+benchmark b = do+  settings <- O.execParser $ O.info (O.helper <*> parser) $ O.fullDesc+  runBenchmark settings b+  where+    parser = Settings <$> reportsDir <*> samplesAmount where+      reportsDir = +        O.nullOption $ +          O.eitherReader readValue <>+          O.long "reportsDir" <>+          O.short 'd' <>+          O.value "." <>+          O.showDefault <>+          O.help "A path to directory to save all the reports in"+        where+          readValue s = let+            p = FS.decodeString s+            in if unsafePerformIO (FS.isDirectory p)+              then Right p+              else Left $ "The path does not exist or is not a directory: " <> s+      samplesAmount = +        O.option $ +          O.long "samplesAmount" <>+          O.short 's' <>+          O.value 100 <>+          O.eitherReader (validateValue . read) <>+          O.showDefault <>+          O.help "How many times to sample the benchmarks"+        where+          validateValue a = if a < 3 +            then Left "A value is lower than the minimum of 3"+            else Right a+++-- * Benchmark+-------------------------++-- |+-- A root of the \"criterion-plus\" monad stack.+-- +-- Use this monad to declare 'standoff's.+-- You can also lift a shared initialization into it using 'liftIO'.+newtype Benchmark a =+  Benchmark (StateT Int (ReaderT Environment (ReaderT Settings IO)) a)+  deriving (Functor, Applicative, Monad, MonadIO)++runBenchmark :: Settings -> Benchmark a -> IO a+runBenchmark settings (Benchmark m) = do+  env <- runCriterion settings $ C.measureEnvironment+  flip runReaderT settings $ flip runReaderT env $ flip evalStateT 1 $ m++-- |+-- Declare a named comparison of multiple subjects.+-- This will generate a separate report file.+standoff :: Name -> Standoff () -> Benchmark ()+standoff name s = Benchmark $ do+  i <- state $ id &&& succ+  env <- lift $ ask+  settings <- lift $ lift $ ask+  liftIO $ void $ runStandoff i name env settings s+++-- * Standoff+-------------------------++-- |+-- A monad for declaration of independent comparison,+-- which will produce a dedicated report file.+-- +-- Use this monad to 'group' and declare 'subject's.+-- You can also lift a shared initialization into it using 'liftIO'.+newtype Standoff a =+  Standoff (StateT [SubjectReport] (ReaderT Group (ReaderT Environment (ReaderT Settings IO))) a)+  deriving (Functor, Applicative, Monad, MonadIO)++-- | A reverse list of names of nested group declarations.+type Group = [Name]++runStandoff :: Int -> Name -> Environment -> Settings -> Standoff a -> IO [SubjectReport]+runStandoff i name env settings (Standoff m) = do+  reports <- +    fmap reverse $+    flip runReaderT settings $ flip runReaderT env $ flip runReaderT [] $ flip execStateT [] $ m+  renderHTML settings name reports file+  return reports+  where+    file = (reportsDir settings) <> (FS.decodeString $ uniqueName <> ".html")+    uniqueName = "standoff-" <> show i++-- |+-- Put the wrapped computations into a named group.+-- Can be nested.+group :: Name -> Standoff () -> Standoff ()+group name (Standoff m) = Standoff $ local (name :) m++-- |+-- Execute a named subject.+subject :: Name -> Subject a -> Standoff ()+subject name subj = Standoff $ do+  group <- lift $ ask+  env <- lift $ lift $ ask+  settings <- lift $ lift $ lift $ ask+  report <- liftIO $ runSubject name group env settings subj+  modify (report :)+++-- * Subject+-------------------------++-- |+-- A monad, which wraps the benchmarking subject and controls its measurement.+newtype Subject a = +  Subject (StateT SampleStartTime (StateT SampleTotalTime IO) a)+  deriving (Functor, Applicative, Monad, MonadIO, MonadBase IO)++instance MonadBaseControl IO Subject where+  newtype StM Subject a = SubjectStM (StM (StateT SampleStartTime (StateT SampleTotalTime IO)) a)+  liftBaseWith run =+    Subject $ liftBaseWith $ \runStateInBase -> +    run $ \(Subject s) -> liftM SubjectStM $ runStateInBase s+  restoreM (SubjectStM s) = Subject $ restoreM s+++type SubjectReport = (Name, S.Sample, C.SampleAnalysis, C.Outliers)++type SampleStartTime = Maybe Double+type SampleTotalTime = Double++runSubject :: Name -> Group -> Environment -> Settings -> Subject a -> IO SubjectReport+runSubject name group env settings subj = do+  putStrLnLT $ [lt|\nRunning a subject "%s"|] compositeName++  samplesVec <- collectSamplesVec++  analysis@C.SampleAnalysis{..} <- C.analyseSample 0.95 samplesVec (100 * 1000)++  reportEstimate "mean" anMean+  reportEstimate "std dev" anStdDev+  +  let outliers = C.classifyOutliers samplesVec+  reportOutliers outliers+  reportOutliersVariance anOutlierVar ++  return $ (compositeName, samplesVec, analysis, outliers)+  where+    reportEstimate header S.Estimate{..} = do+      putStrLnLT $+        [lt|%s: %s, lower bound: %s, upper bound: %s, confidence: %.3f|]+          (header :: LazyText)+          (C.secs estPoint)+          (C.secs estLowerBound)+          (C.secs estUpperBound)+          (estConfidenceLevel)+    reportOutliersVariance C.OutlierVariance{..} = do+      putStrLnLT $ [lt|variance introduced by outliers: %.3f%%|] (ovFraction * 100)+      putStrLnLT $ [lt|variance is %s by outliers|] $+        case ovEffect of+          C.Unaffected -> "unaffected" :: LazyText+          C.Slight -> "slightly inflated"+          C.Moderate -> "moderately inflated"+          C.Severe -> "severely inflated"+    reportOutliers = runCriterion settings . C.noteOutliers+    collectSamplesVec = do+      let amount = samplesAmount settings+      firstSample <- runSample+      let useFirstSample = firstSample > 0.2+      vec <- VM.new amount+      when useFirstSample $ VM.write vec 0 firstSample+      forM_ (enumFromTo (if useFirstSample then 1 else 0) (amount - 1)) $ \i -> do+        printTimeLeft firstSample (amount - i)+        sample <- runSample+        VM.write vec i sample+      V.unsafeFreeze vec+      where+        printTimeLeft sample amount = do+          putStrST $ +            [st|Collecting %d more samples in %.1f s.|] +              (amount) +              (sample * fromIntegral amount)+          System.IO.hFlush System.IO.stdout+          putStr (CSI.eraseLineToBeginning <> CSI.cursorHorizontalAbsolute 0)+        runSample = do+          performGC+          (_, time) <- +            (continue >> subj >> pause) |> +            \(Subject m) -> flip runStateT 0 $ flip runStateT Nothing $ m+          return $ time - C.envClockCost env+    compositeName = T.intercalate "/" $ reverse $ name : group++-- | +-- Continue the timer.+-- +-- By default it is already running, +-- so if you need to eclude something from the beginning of the subject+-- use 'pause'. E.g.:+-- +-- > subject "MySQL" $ do+-- >   pause+-- >   connection <- liftIO $ openConnection+-- >   continue+-- >   liftIO $ workWithConnection connection+-- >   pause+-- >   liftIO $ closeConnection connection+-- +continue :: Subject ()+continue = Subject $ do+  !time <- liftIO $ C.getTime+  put $ Just time++-- | Pause the timer.+pause :: Subject ()+pause = Subject $ do+  !time <- liftIO $ C.getTime+  get >>= \case+    Just startTime -> do+      lift $ modify (+ (time - startTime))+      put $ Nothing+    Nothing -> return ()++-- |+-- An adaptation of @Criterion.Types.'C.whnf'@.+whnf :: (MonadIO m) => (a -> b) -> a -> m ()+whnf f x = liftIO $ void $ evaluate (f x)+{-# NOINLINE whnf #-}++-- |+-- An adaptation of @Criterion.Types.'C.nf'@.+nf :: (MonadIO m, NFData b) => (a -> b) -> a -> m ()+nf f x = liftIO $ evaluate (rnf (f x))+{-# NOINLINE nf #-}++-- |+-- An adaptation of @Criterion.Types.'C.nfIO'@.+nfIO :: (MonadIO m, NFData a) => IO a -> m ()+nfIO = liftIO . C.nfIO+{-# NOINLINE nfIO #-}++-- |+-- An adaptation of @Criterion.Types.'C.whnfIO'@.+whnfIO :: (MonadIO m) => IO a -> m ()+whnfIO = liftIO . C.whnfIO+{-# NOINLINE whnfIO #-}+++-- * Criterion Helpers+-------------------------++runCriterion :: Settings -> C.Criterion a -> IO a+runCriterion settings = +  C.withConfig $ C.defaultConfig {+    C.cfgSamples = Last $ Just $ (samplesAmount settings)+  }++renderHTML :: Settings -> Name -> [SubjectReport] -> FilePath -> IO ()+renderHTML settings name reports file = do+  C.withConfig config $ C.report reports'+  where+    reports' = [C.Report i (cs n) s a o | i <- [0..] | (n, s, a, o) <- reports]+    config = +      C.defaultConfig {+        C.cfgBanner = Last $ Just $ cs name,+        C.cfgReport = Last $ Just $ FS.encodeString file,+        C.cfgSamples = Last $ Just $ samplesAmount settings+      } 
+ library/CriterionPlus/Prelude/Basic.hs view
@@ -0,0 +1,96 @@+module CriterionPlus.Prelude.Basic+( +  module Exports,++  traceM,+  bug,+  bottom,++  (?:),+  (|>),+  (<|),+  (|$>),+)+where++-- base+-------------------------+import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, FilePath, id, (.))+import Control.Monad as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Applicative as Exports+import Control.Arrow as Exports hiding (left, right)+import Control.Category as Exports+import Data.Monoid as Exports+import Data.Foldable as Exports+import Data.Traversable as Exports hiding (for)+import Data.Maybe as Exports+import Data.Either as Exports+import Data.List as Exports hiding (concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')+import Data.Tuple as Exports+import Data.Function as Exports hiding ((.), id)+import Data.Ord as Exports (Down(..))+import Data.String as Exports+import Data.Int as Exports+import Data.Word as Exports+import Data.Ratio as Exports+import Data.Fixed as Exports+import Data.Ix as Exports+import Data.Data as Exports+import Text.Read as Exports (readMaybe, readEither)+import Control.Exception as Exports hiding (tryJust, try, assert)+import Control.Concurrent as Exports hiding (yield)+import System.Mem as Exports+import System.Mem.StableName as Exports+import System.Timeout as Exports+import System.Exit as Exports+import System.IO.Unsafe as Exports+import System.IO as Exports (Handle, hClose)+import System.IO.Error as Exports+import Unsafe.Coerce as Exports+import GHC.Generics as Exports (Generic)+import GHC.IO.Exception as Exports+import Data.IORef as Exports+import Data.STRef as Exports+import Control.Monad.ST as Exports+import Debug.Trace as Exports hiding (traceM)++-- placeholders+-------------------------+import Development.Placeholders as Exports++-- deepseq+-------------------------+import Control.DeepSeq as Exports++-- custom+-------------------------+import qualified Debug.Trace.LocationTH++traceM :: (Monad m) => String -> m ()+traceM s = trace s $ return ()++bug = [e| $(Debug.Trace.LocationTH.failure) . (msg <>) |]+  where+    msg = "A \"criterion-plus\" package bug: " :: String++bottom = [e| $bug "Bottom evaluated" |]++(?:) :: Maybe a -> a -> a+maybeA ?: b = fromMaybe b maybeA+{-# INLINE (?:) #-}++(|>) :: a -> (a -> b) -> b+a |> aToB = aToB a+{-# INLINE (|>) #-}++(<|) :: (a -> b) -> a -> b+aToB <| a = aToB a+{-# INLINE (<|) #-}++-- | +-- The following are all the same:+-- fmap f a == f <$> a == a |> fmap f == a |$> f+-- +-- This operator accomodates the left-to-right operators: >>=, >>>, |>.+(|$>) = flip fmap+{-# INLINE (|$>) #-}
+ library/CriterionPlus/Prelude/Data.hs view
@@ -0,0 +1,40 @@+module CriterionPlus.Prelude.Data+(+  module Exports,+  putStrLnLT,+  putStrLnST,+  putStrLT,+  putStrST,+)+where++-- string-conversions+-------------------------+import Data.String.Conversions as Exports hiding (LT, ST)++-- system-filepath+-------------------------+import Filesystem.Path as Exports (FilePath)++-- th-printf+-------------------------+import Text.Printf.TH as Exports+++-- Custom+-------------------------+import CriterionPlus.Prelude.Basic+import qualified Data.Text.IO+import qualified Data.Text.Lazy.IO++putStrLnLT :: LazyText -> IO ()+putStrLnLT = Data.Text.Lazy.IO.putStrLn++putStrLT :: LazyText -> IO ()+putStrLT = Data.Text.Lazy.IO.putStr++putStrLnST :: StrictText -> IO ()+putStrLnST = Data.Text.IO.putStrLn++putStrST :: StrictText -> IO ()+putStrST = Data.Text.IO.putStr
+ library/CriterionPlus/Prelude/Transformers.hs view
@@ -0,0 +1,21 @@+module CriterionPlus.Prelude.Transformers (module Exports) where++-- mtl+-------------------------+import Control.Monad.Identity as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.State.Strict as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.Reader as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.Writer.Strict as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM, Any)+import Control.Monad.RWS.Strict as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM, Any)+import Control.Monad.Error as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)+import Control.Monad.Trans as Exports++-- transformers-base+-------------------------+import Control.Monad.Base as Exports++-- monad-control+-------------------------+import Control.Monad.Trans.Control as Exports++