chronograph (empty) → 0.1.0.0
raw patch · 5 files changed
+232/−0 lines, 5 filesdep +basedep +deepseqdep +ghc-primsetup-changed
Dependencies added: base, deepseq, ghc-prim, time
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- chronograph.cabal +42/−0
- examples/Chrono.hs +35/−0
- src/Data/Chronograph.hs +123/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, John Lato++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 John Lato 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
+ chronograph.cabal view
@@ -0,0 +1,42 @@+-- Initial chronograph.cabal generated by cabal init. For further+-- documentation, see http://haskell.org/cabal/users-guide/++name: chronograph+version: 0.1.0.0+synopsis: measure timings of data evaluation+description: The 'Chronograph' data structure adds a measure field+ to an existing Haskell expression. This field will be the+ time necessary to evaluate the expression using an+ arbitrary evaluation strategy (WHNF by default).+ .+ Chronograph preserves laziness, so that the work of+ performing the evaluation is only done after the result is+ demanded.+ .+ If you want to benchmark your program, you should look to+ other packages like Criterion that perform statistical+ analysis of your results so you can determine how reliable+ they are. Chronograph just takes measurements, leaving+ the interpretation entirely to you.+license: BSD3+license-file: LICENSE+author: John Lato+maintainer: jwlato@gmail.com+copyright: John W. Lato, 2012+category: Testing+build-type: Simple+cabal-version: >=1.8++extra-source-files: examples/Chrono.hs++library+ exposed-modules: Data.Chronograph+ build-depends: base >=4.5 && < 5.0,+ ghc-prim >=0.2 && < 0.4,+ deepseq ==1.3.*,+ time ==1.4.*+ hs-source-dirs: src++source-repository head+ type: git+ location: https://github.com/JohnLato/chronograph
+ examples/Chrono.hs view
@@ -0,0 +1,35 @@+-- | A simple example of how to use Chronograph. We want to count the lines in+-- a file, and measure how long it takes.++module Main where++import Control.Applicative+import System.Environment+import Data.Chronograph++import Text.Printf++main :: IO ()+main = do+ args <- getArgs+ putStrLn "pure Chronograph"+ mapM_ procFile args+ putStrLn "IO Chronograph"+ mapM_ procIO args++procFile :: FilePath -> IO ()+procFile fp = do+ doc <- readFile fp+ let wc = length $ lines doc+ putStrLn $ formatOutput fp (chrono wc)+ +formatOutput :: FilePath -> Chronograph Int -> String+formatOutput fp chr = printf "%s :: %d, %s" fp (val chr) (show $ measure chr)++fileLinesIO :: FilePath -> IO Int+fileLinesIO fp = length . lines <$> readFile fp++procIO :: FilePath -> IO ()+procIO fp = do+ wc <- chronoIO $ fileLinesIO fp+ putStrLn $ formatOutput fp wc
+ src/Data/Chronograph.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE NamedFieldPuns #-}++ -- |+ -- Measure data and IO evaluation time in a lightweight manner.+ --+ -- A 'Chronograph a' has two parts, the value 'a' and the measurement of+ -- evaluation time. A Chronograph is lazy, so 'a' is only evaluted on demand.+ --+ -- This example counts the lines in a number of files, and records the+ -- evaluation time taken for each one.+ --+ -- > import System.Environment+ -- > import Control.Applicative+ -- > import Data.Chronograph+ -- >+ -- > import Text.Printf+ -- >+ -- > formatOutput :: FilePath -> Chronograph Int -> String+ -- > formatOutput fp chr = printf "%s :: %d, %s" fp (val chr) (show $ measure chr)+ -- >+ -- > procFile :: FilePath -> IO ()+ -- > procFile fp = do+ -- > doc <- readFile fp+ -- > let wc = length $ lines doc+ -- > putStrLn $ formatOutput fp (chrono wc)+ --+ -- 'chrono' creates a chronograph that evaluates its input as far as 'seq' would.+ -- In this case the input 'wc' is an Int, so 'chrono' fully evaluates it.+ -- deepseq-style evaluation is performed by 'chronoNF', and custom evaluation+ -- strategies can be implemented with 'chronoBy'.+ --+ -- although 'wc' is a pure value, IO is lazily performed in its evalution.+ -- This IO cost is included in 'chrono's measurement.+ --+ -- You can explicitly include timings of IO actions as well:+ --+ -- > fileLinesIO :: FilePath -> IO Int+ -- > fileLinesIO fp = length . lines <$> readFile fp+ -- >+ -- > procIO :: FilePath -> IO ()+ -- > procIO fp = do+ -- > wc <- chronoIO $ fileLinesIO fp+ -- > putStrLn $ formatOutput fp wc+ --+ -- > main :: IO ()+ -- > main = do+ -- > args <- getArgs+ -- > putStrLn "pure Chronograph"+ -- > mapM_ procFile args+ -- > putStrLn "IO Chronograph"+ -- > mapM_ procIO args+ --+module Data.Chronograph (+ Chronograph (..)+-- * chrono pure stuff+, chrono+, chronoNF+, chronoBy+-- * chrono IO stuff+, chronoJustIO+, chronoIO+, chronoNFIO+, chronoIOBy+) where++import Control.DeepSeq+import Data.Time (NominalDiffTime, getCurrentTime, diffUTCTime)+import GHC.Generics+import System.IO.Unsafe (unsafePerformIO)++data Chronograph a = Chronograph+ { val :: a+ , measure :: NominalDiffTime+ } deriving (Show, Generic)++----------------------------------------------------------+-- chrono pure functions++-- | Add a 'Chronograph' to measure evaluation to weak head normal form.+chrono :: a -> Chronograph a+chrono = chronoBy (`seq` ())++-- | Add a 'Chronograph' to measure evaluation to normal form.+chronoNF :: NFData a => a -> Chronograph a+chronoNF = chronoBy rnf++-- | Add a 'Chronograph' to measure evalution time with the provided strategy.+chronoBy :: (a -> ()) -> a -> Chronograph a+chronoBy eval x =+ let measure = unsafePerformIO $ do+ t0 <- getCurrentTime+ t1 <- eval x `seq` getCurrentTime+ return (t1 `diffUTCTime` t0)+ in Chronograph {val = measure `seq` x, measure }++----------------------------------------------------------+-- chrono IO computations++-- | Add a 'Chronograph' to measure IO time (no additional evaluation is+-- performed, although the IO action itself may perform some evaluation)+chronoJustIO :: IO a -> IO (Chronograph a)+chronoJustIO = chronoIOBy (const ())++-- | Add a 'Chronograph' to measure time of IO and evaluation to weak head+-- normal form.+chronoIO :: IO a -> IO (Chronograph a)+chronoIO = chronoIOBy (`seq` ())++-- | Add a 'Chronograph' to measure time of IO and evaluation to normal form.+chronoNFIO :: NFData a => IO a -> IO (Chronograph a)+chronoNFIO = chronoIOBy rnf++-- | Add a 'Chronograph' to measure time of IO and evaluation with the+-- provided strategy.+chronoIOBy :: (a -> ()) -> IO a -> IO (Chronograph a)+chronoIOBy eval mx = do+ t0 <- getCurrentTime+ val <- mx+ t1 <- eval val `seq` getCurrentTime+ let measure = t1 `diffUTCTime` t0++ return $ Chronograph {val, measure}