weigh 0.0.1 → 0.0.2
raw patch · 4 files changed
+68/−18 lines, 4 files
Files
- CHANGELOG +4/−0
- src/Weigh.hs +51/−17
- src/test/Main.hs +12/−0
- weigh.cabal +1/−1
CHANGELOG view
@@ -1,3 +1,7 @@+0.0.2:+ * Remove magic numbers from weighing code, better accuracy+ * Add additional `io` combinator+ 0.0.1: * Support GHC 8.
src/Weigh.hs view
@@ -13,9 +13,10 @@ ,Weigh ,Weight(..) -- * Simple combinators- ,action ,func+ ,io ,value+ ,action -- * Validating combinators ,validateAction ,validateFunc@@ -59,7 +60,7 @@ -- | An action to run. data Action = forall a b. (NFData a) =>- Action {_actionRun :: !(b -> IO a)+ Action {_actionRun :: !(Either (b -> IO a) (b -> a)) ,_actionArg :: !b ,actionCheck :: Weight -> Maybe String} @@ -95,6 +96,7 @@ errors exitWith (ExitFailure (-1)) + -------------------------------------------------------------------------------- -- User DSL @@ -104,22 +106,24 @@ func :: (NFData a) => String -> (b -> a) -> b -> Weigh () func name !f !x = validateFunc name f x (const Nothing) +-- | Weigh a function applied to an argument.+--+-- Implemented in terms of 'validateFunc'.+io :: (NFData a) => String -> (b -> IO a) -> b -> Weigh ()+io name !f !x = validateAction name f x (const Nothing)+ -- | Weigh a value. -- -- Implemented in terms of 'action'. value :: NFData a => String -> a -> Weigh ()-value name !v = action name (return v)+value name !v = func name id v -- | Weigh an IO action. -- -- Implemented in terms of 'validateAction'. action :: NFData a => String -> IO a -> Weigh ()-action name !m =- validateAction name- (const m)- ()- (const Nothing)+action name !m = io name (const m) () -- | Make a validator that set sthe maximum allocations. maxAllocs :: Int64 -> (Weight -> Maybe String)@@ -135,14 +139,14 @@ :: (NFData a) => String -> (b -> IO a) -> b -> (Weight -> Maybe String) -> Weigh () validateAction name !m !arg !validate =- Weigh (tell [(name,Action m arg validate)])+ Weigh (tell [(name,Action (Left m) arg validate)]) -- | Weigh a function, validating the result validateFunc :: (NFData a) => String -> (b -> a) -> b -> (Weight -> Maybe String) -> Weigh () validateFunc name !f !x !validate =- Weigh (tell [(name,Action (return . f) x validate)])+ Weigh (tell [(name,Action (Right f) x validate)]) -------------------------------------------------------------------------------- -- Internal measuring actions@@ -158,7 +162,10 @@ Just act -> do case act of Action !run arg _ ->- do (bytes,gcs) <- weighAction run arg+ do (bytes,gcs) <-+ case run of+ Right f -> weighFunc f arg+ Left m -> weighAction m arg print (Weight {weightLabel = label ,weightAllocatedBytes = bytes ,weightGCs = gcs})@@ -183,13 +190,43 @@ let !r = read out in return r --- | Weigh an action. This function is heavily documented inside.+-- | Weigh a pure function. This function is heavily documented inside.+weighFunc+ :: (NFData a)+ => (b -> a) -- ^ A function whose memory use we want to measure.+ -> b -- ^ Argument to the function. Doesn't have to be forced.+ -> IO (Int64,Int64) -- ^ Bytes allocated and garbage collections.+weighFunc run !arg =+ do performGC+ -- The above forces getGCStats data to be generated NOW.+ !bootupStats <- getGCStats+ -- We need the above to subtract "program startup" overhead. This+ -- operation itself adds n bytes for the size of GCStats, but we+ -- subtract again that later.+ let !_ = force (run arg)+ performGC+ -- The above forces getGCStats data to be generated NOW.+ !actionStats <- getGCStats+ let reflectionGCs = 1 -- We performed an additional GC.+ actionBytes =+ (bytesAllocated actionStats - bytesAllocated bootupStats) -+ -- We subtract the size of "bootupStats", which will be+ -- included after we did the performGC.+ ghcStatsSizeInBytes+ actionGCs = numGcs actionStats - numGcs bootupStats - reflectionGCs+ -- If overheadBytes is too large, we conservatively just+ -- return zero. It's not perfect, but this library is for+ -- measuring large quantities anyway.+ actualBytes = max 0 actionBytes+ return (actualBytes,actionGCs)++-- | Weigh a pure function. This function is heavily documented inside. weighAction :: (NFData a) => (b -> IO a) -- ^ A function whose memory use we want to measure. -> b -- ^ Argument to the function. Doesn't have to be forced. -> IO (Int64,Int64) -- ^ Bytes allocated and garbage collections.-weighAction !run !arg =+weighAction run !arg = do performGC -- The above forces getGCStats data to be generated NOW. !bootupStats <- getGCStats@@ -207,13 +244,10 @@ -- included after we did the performGC. ghcStatsSizeInBytes actionGCs = numGcs actionStats - numGcs bootupStats - reflectionGCs- -- I believe that 24 is the cost to allocate and force the- -- thunk. This may change with GHC version in the future.- overheadBytes = 24 -- If overheadBytes is too large, we conservatively just -- return zero. It's not perfect, but this library is for -- measuring large quantities anyway.- actualBytes = max 0 (actionBytes - overheadBytes)+ actualBytes = max 0 actionBytes return (actualBytes,actionGCs) --------------------------------------------------------------------------------
src/test/Main.hs view
@@ -13,9 +13,21 @@ main :: IO () main = mainWith (do integers+ ioactions ints struct packing)++-- | Weigh IO actions.+ioactions :: Weigh ()+ioactions =+ do action "integers count IO CAF 0" (return (count 0))+ io "integers count IO func 0" (return . count) 0+ action "integers count IO CAF 1" (return (count 1))+ io "integers count IO func 1" (return . count) 1+ where count :: Integer -> ()+ count 0 = ()+ count a = count (a - 1) -- | Just counting integers. integers :: Weigh ()
weigh.cabal view
@@ -1,5 +1,5 @@ name: weigh-version: 0.0.1+version: 0.0.2 synopsis: Measure allocations of a Haskell functions/values description: Please see README.md homepage: https://github.com/fpco/weigh#readme