packages feed

weigh 0.0.7 → 0.0.8

raw patch · 5 files changed

+157/−78 lines, 5 filesdep −template-haskellPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: template-haskell

API changes (from Hackage documentation)

+ Weigh: instance Control.DeepSeq.NFData Weigh.Action
+ Weigh: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Weigh.Grouped a)
+ Weigh: instance Data.Foldable.Foldable Weigh.Grouped
+ Weigh: instance Data.Traversable.Traversable Weigh.Grouped
+ Weigh: instance GHC.Base.Functor Weigh.Grouped
+ Weigh: instance GHC.Classes.Eq a => GHC.Classes.Eq (Weigh.Grouped a)
+ Weigh: instance GHC.Generics.Generic (Weigh.Grouped a)
+ Weigh: instance GHC.Show.Show a => GHC.Show.Show (Weigh.Grouped a)
+ Weigh: wgroup :: String -> Weigh () -> Weigh ()
- Weigh: weighDispatch :: [String] -> [(String, Action)] -> IO (Maybe [Weight])
+ Weigh: weighDispatch :: [String] -> [Grouped Action] -> IO (Maybe [(Grouped Weight)])
- Weigh: weighResults :: Weigh a -> IO ([(Weight, Maybe String)], Config)
+ Weigh: weighResults :: Weigh a -> IO ([Grouped (Weight, Maybe String)], Config)

Files

CHANGELOG view
@@ -1,3 +1,6 @@+0.0.7:+	* Support grouping+ 0.0.6: 	* Support GHC 8.2 	* Use more reliable calculations
src/Weigh.hs view
@@ -1,3 +1,8 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ExistentialQuantification #-}@@ -21,6 +26,8 @@ --         count 0 = () --         count a = count (a - 1) -- @+--+-- Use 'wgroup' to group sets of tests.  module Weigh   (-- * Main entry points@@ -34,6 +41,7 @@   ,io   ,value   ,action+  ,wgroup   -- * Validating combinators   ,validateAction   ,validateFunc@@ -55,10 +63,13 @@ import Control.Arrow import Control.DeepSeq import Control.Monad.State-import Data.List+import qualified Data.Foldable as Foldable+import qualified Data.Traversable as Traversable+import Data.Int+import qualified Data.List as List import Data.List.Split import Data.Maybe-import Data.Int+import GHC.Generics import Prelude import System.Environment import System.Exit@@ -77,12 +88,14 @@   deriving (Show, Eq, Enum)  -- | Weigh configuration.-data Config = Config {configColumns :: [Column]}-  deriving (Show)+data Config = Config+  { configColumns :: [Column]+  , configPrefix :: String+  } deriving (Show)  -- | Weigh specification monad. newtype Weigh a =-  Weigh {runWeigh :: State (Config, [(String,Action)]) a}+  Weigh {runWeigh :: State (Config, [Grouped Action]) a}   deriving (Monad,Functor,Applicative)  -- | How much a computation weighed in at.@@ -95,51 +108,64 @@          }   deriving (Read,Show) +-- | Some grouped thing.+data Grouped a+  = Grouped String [Grouped a]+  | Singleton String a+  deriving (Eq, Show, Functor, Traversable.Traversable, Foldable.Foldable, Generic)+instance NFData a => NFData (Grouped a)+ -- | An action to run. data Action =   forall a b. (NFData a) =>   Action {_actionRun :: !(Either (b -> IO a) (b -> a))          ,_actionArg :: !b+         ,actionName :: !String          ,actionCheck :: Weight -> Maybe String}+instance NFData Action where rnf _ = ()  -------------------------------------------------------------------------------- -- Main-runners  -- | Just run the measuring and print a report. Uses 'weighResults'. mainWith :: Weigh a -> IO ()-mainWith m =-  do (results, config) <- weighResults m-     unless (null results)-            (do putStrLn ""-                putStrLn (report config results))-     case mapMaybe (\(w,r) ->-                      do msg <- r-                         return (w,msg))-                   results of-       [] -> return ()-       errors ->-         do putStrLn "\nCheck problems:"-            mapM_ (\(w,r) -> putStrLn ("  " ++ weightLabel w ++ "\n    " ++ r)) errors-            exitWith (ExitFailure (-1))+mainWith m = do+  (results, config) <- weighResults m+  unless+    (null results)+    (do putStrLn ""+        putStrLn (report config results))+  case mapMaybe+         (\(w, r) -> do+            msg <- r+            return (w, msg))+         (concatMap Foldable.toList (Foldable.toList results)) of+    [] -> return ()+    errors -> do+      putStrLn "\nCheck problems:"+      mapM_+        (\(w, r) -> putStrLn ("  " ++ weightLabel w ++ "\n    " ++ r))+        errors+      exitWith (ExitFailure (-1))  -- | Run the measuring and return all the results, each one may have -- an error. weighResults-  :: Weigh a -> IO ([(Weight,Maybe String)], Config)+  :: Weigh a -> IO ([Grouped (Weight,Maybe String)], Config) weighResults m = do   args <- getArgs-  let (config, cases) =-        execState (runWeigh m) (defaultConfig, [])+  let (config, cases) = execState (runWeigh m) (defaultConfig, [])   result <- weighDispatch args cases   case result of     Nothing -> return ([], config)     Just weights ->       return-        ( map-            (\w ->-               case lookup (weightLabel w) cases of-                 Nothing -> (w, Nothing)-                 Just a -> (w, actionCheck a w))+        ( fmap+            (fmap+               (\w ->+                  case glookup (weightLabel w) cases of+                    Nothing -> (w, Nothing)+                    Just a -> (w, actionCheck a w)))             weights         , config) @@ -152,7 +178,7 @@  -- | Default config. defaultConfig :: Config-defaultConfig = Config {configColumns = defaultColumns}+defaultConfig = Config {configColumns = defaultColumns, configPrefix = ""}  -- | Set the config. Default is: 'defaultConfig'. setColumns :: [Column] -> Weigh ()@@ -214,7 +240,7 @@                -> (Weight -> Maybe String) -- ^ A validating function, returns maybe an error.                -> Weigh () validateAction name !m !arg !validate =-  tellAction [(name,Action (Left m) arg validate)]+  tellAction name (Action (Left m) arg name validate)  -- | Weigh a function, validating the result validateFunc :: (NFData a)@@ -224,29 +250,42 @@              -> (Weight -> Maybe String) -- ^ A validating function, returns maybe an error.              -> Weigh () validateFunc name !f !x !validate =-  tellAction [(name,Action (Right f) x validate)]+  tellAction name (Action (Right f) x name validate)  -- | Write out an action.-tellAction :: [(String, Action)] -> Weigh ()-tellAction x = Weigh (modify (second ( ++ x)))+tellAction :: String -> Action -> Weigh ()+tellAction name act =+  Weigh (do prefix <- gets (configPrefix . fst)+            modify (second (\x -> x ++ [Singleton (prefix ++ "/" ++ name) act]))) +-- | Make a grouping of tests.+wgroup :: String -> Weigh () -> Weigh ()+wgroup str wei = do+  (orig, start) <- Weigh get+  let startL = length $ start+  Weigh (modify (first (\c -> c {configPrefix = configPrefix orig ++ "/" ++ str})))+  wei+  Weigh $ do+    modify $ second $ \x -> take startL x ++ [Grouped str $ drop startL x]+    modify (first (\c -> c {configPrefix = configPrefix orig}))+ -------------------------------------------------------------------------------- -- Internal measuring actions  -- | Weigh a set of actions. The value of the actions are forced -- completely to ensure they are fully allocated. weighDispatch :: [String] -- ^ Program arguments.-              -> [(String,Action)] -- ^ Weigh name:action mapping.-              -> IO (Maybe [Weight])+              -> [Grouped Action] -- ^ Weigh name:action mapping.+              -> IO (Maybe [(Grouped Weight)]) weighDispatch args cases =   case args of     ("--case":label:fp:_) ->       let !_ = force fp-      in case lookup label (deepseq (map fst cases) cases) of+      in case glookup label (force cases) of            Nothing -> error "No such case!"            Just act -> do              case act of-               Action !run arg _ -> do+               Action !run arg _ _ -> do                  (bytes, gcs, liveBytes, maxByte) <-                    case run of                      Right f -> weighFunc f arg@@ -262,15 +301,18 @@                        , weightMaxBytes = maxByte                        }))              return Nothing-    _-      | names == nub names -> fmap Just (mapM (fork . fst) cases)-      | otherwise -> error "Non-unique names specified for things to measure."-      where names = map fst cases+    _ -> fmap Just (Traversable.traverse (Traversable.traverse fork) cases) +-- | Lookup an action.+glookup :: String -> [Grouped Action] -> Maybe Action+glookup label =+  Foldable.find ((== label) . actionName) .+  concat . map Foldable.toList . Foldable.toList+ -- | Fork a case and run it.-fork :: String -- ^ Label for the case.+fork :: Action -- ^ Label for the case.      -> IO Weight-fork label =+fork act =   withSystemTempFile     "weigh"     (\fp h -> do@@ -279,22 +321,23 @@        (exit, _, err) <-          readProcessWithExitCode            me-           ["--case", label, fp, "+RTS", "-T", "-RTS"]+           ["--case", actionName act, fp, "+RTS", "-T", "-RTS"]            ""        case exit of          ExitFailure {} ->-           error ("Error in case (" ++ show label ++ "):\n  " ++ err)-         ExitSuccess ->-           do out <- readFile fp-              case reads out of-                [(!r, _)] -> return r-                _ ->-                  error-                    (concat-                       [ "Malformed output from subprocess. Weigh"-                       , " (currently) communicates with its sub-"-                       , "processes via a temporary file."-                       ]))+           error+             ("Error in case (" ++ show (actionName act) ++ "):\n  " ++ err)+         ExitSuccess -> do+           out <- readFile fp+           case reads out of+             [(!r, _)] -> return r+             _ ->+               error+                 (concat+                    [ "Malformed output from subprocess. Weigh"+                    , " (currently) communicates with its sub-"+                    , "processes via a temporary file."+                    ]))  -- | Weigh a pure function. This function is heavily documented inside. weighFunc@@ -389,10 +432,39 @@ -------------------------------------------------------------------------------- -- Formatting functions +report :: Config -> [Grouped (Weight,Maybe String)] -> String+report config gs =+  List.intercalate+    "\n\n"+    (filter+       (not . null)+       [ if null singletons+           then []+           else reportTabular config singletons+       , List.intercalate "\n\n" (map (uncurry (reportGroup config)) groups)+       ])+  where+    singletons =+      mapMaybe+        (\case+           Singleton _ v -> Just v+           _ -> Nothing)+        gs+    groups =+      mapMaybe+        (\case+           Grouped title vs -> Just (title, vs)+           _ -> Nothing)+        gs++reportGroup :: Config -> [Char] -> [Grouped (Weight, Maybe String)] -> [Char]+reportGroup config title gs = title ++ "\n\n" ++ indent (report config gs)+ -- | Make a report of the weights.-report :: Config -> [(Weight,Maybe String)] -> String-report config = tablize . (select headings :) . map (select . toRow)+reportTabular :: Config -> [(Weight,Maybe String)] -> String+reportTabular config = tabled   where+    tabled = tablize . (select headings :) . map (select . toRow)     select row = mapMaybe (\name -> lookup name row) (configColumns config)     headings =       [ (Case, (True, "Case"))@@ -418,8 +490,8 @@ -- | Make a table out of a list of rows. tablize :: [[(Bool,String)]] -> String tablize xs =-  intercalate "\n"-              (map (intercalate "  " . map fill . zip [0 ..]) xs)+  List.intercalate "\n"+              (map (List.intercalate "  " . map fill . zip [0 ..]) xs)   where fill (x',(left',text')) = printf ("%" ++ direction ++ show width ++ "s") text'           where direction = if left'                                then "-"@@ -428,4 +500,8 @@  -- | Formatting an integral number to 1,000,000, etc. commas :: (Num a,Integral a,Show a) => a -> String-commas = reverse . intercalate "," . chunksOf 3 . reverse . show+commas = reverse . List.intercalate "," . chunksOf 3 . reverse . show++-- | Indent all lines in a string.+indent :: [Char] -> [Char]+indent = List.intercalate "\n" . map (replicate 2 ' '++) . lines
src/Weigh/GHCStats.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE CPP #-}  -- | Calculate the size of GHC.Stats statically.
src/test/Main.hs view
@@ -12,11 +12,12 @@ -- | Weigh integers. main :: IO () main =-  mainWith (do integers-               ioactions-               ints-               struct-               packing)+  mainWith+    (do wgroup "Integers" integers+        wgroup "IO actions" ioactions+        wgroup "Ints" ints+        wgroup "Structs" struct+        wgroup "Packing" packing)  -- | Weigh IO actions. ioactions :: Weigh ()@@ -31,16 +32,17 @@  -- | Just counting integers. integers :: Weigh ()-integers =-  do func "integers count 0" count 0-     func "integers count 1" count 1-     func "integers count 2" count 2-     func "integers count 3" count 3-     func "integers count 10" count 10-     func "integers count 100" count 100-  where count :: Integer -> ()-        count 0 = ()-        count a = count (a - 1)+integers = do+  func "integers count 0" count 0+  func "integers count 1" count 1+  func "integers count 2" count 2+  func "integers count 3" count 3+  func "integers count 10" count 10+  func "integers count 100" count 100+  where+    count :: Integer -> ()+    count 0 = ()+    count a = count (a - 1)  -- | We count ints and ensure that the allocations are optimized away -- to only two 64-bit Ints (16 bytes).
weigh.cabal view
@@ -1,5 +1,5 @@ name:                weigh-version:             0.0.7+version:             0.0.8 synopsis:            Measure allocations of a Haskell functions/values description:         Please see README.md homepage:            https://github.com/fpco/weigh#readme@@ -29,7 +29,6 @@                      , deepseq                      , mtl                      , split-                     , template-haskell                      , temporary   default-language:    Haskell2010