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 +22/−0
- Setup.hs +2/−0
- criterion-plus.cabal +199/−0
- internal-tests/InternalTests.hs +54/−0
- library/CriterionPlus.hs +66/−0
- library/CriterionPlus/CSI.hs +26/−0
- library/CriterionPlus/Monads.hs +311/−0
- library/CriterionPlus/Prelude/Basic.hs +96/−0
- library/CriterionPlus/Prelude/Data.hs +40/−0
- library/CriterionPlus/Prelude/Transformers.hs +21/−0
+ 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++