packages feed

heapsize (empty) → 0.1

raw patch · 6 files changed

+270/−0 lines, 6 filesdep +basedep +criteriondep +deepseqsetup-changed

Dependencies added: base, criterion, deepseq, ghc-heap, hashable, heapsize, primitive, unordered-containers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012 Dennis Felsing++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. 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.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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,3 @@+module Main where+import Distribution.Simple+main = defaultMain
+ benchmarks/Main.hs view
@@ -0,0 +1,55 @@+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveAnyClass #-}++import Criterion.Main+import Criterion.Types+import HeapSize+import GHC.Generics (Generic)+import Data.Primitive.ByteArray+import Data.IORef+import Control.DeepSeq++listBenchmark :: Benchmark+listBenchmark = bgroup "[Int]" $+  map mkBenchmark+    [ 1000+    , 10000+    , 100000+    , 1000000+    ]+  where+    mkBenchmark :: Int -> Benchmark+    mkBenchmark size = env (pure [0 .. size]) $ \lst -> bench (show size) $ nfAppIO recursiveSize lst++byteArrayBenchmark :: Benchmark+byteArrayBenchmark = bgroup "ByteArray#" $+  -- Just to make sure that it's O(1)+  map mkBenchmark [10, 10000]+  where+    mkBenchmark :: Int -> Benchmark+    mkBenchmark size = env+      (newByteArray size)+      (\lst -> bench (show size) $ nfAppIO recursiveSize lst)+++data C = C Int (IORef C)+  deriving (Generic, NFData)++circularBenchmark :: Benchmark+circularBenchmark = env mkT (\c -> bench "Circular" (nfAppIO recursiveSize c))+  where+    mkT :: IO C+    mkT = do+      rec first <- newIORef (C 1 second)+          second <- newIORef (C 2 third)+          third <- newIORef (C 3 first)+      readIORef first+++main :: IO ()+main =+  defaultMainWith+    (defaultConfig {reportFile = Just "ghc-datasize-benchmark.html"})+    [ listBenchmark, byteArrayBenchmark, circularBenchmark ]
+ cbits/heapsize_prim.cmm view
@@ -0,0 +1,15 @@+#include "Cmm.h"++unpackClosurePtrs ( P_ closure )+{+    W_ ptrArray;+    ("ptr" ptrArray) = foreign "C" heap_view_closurePtrs(MyCapability() "ptr", UNTAG(closure) "ptr");+    return (ptrArray);+}++closureSize ( P_ closure )+{+    W_ len;+    (len) = foreign "C" heap_view_closureSize(UNTAG(closure) "ptr");+    return (len);+}
+ heapsize.cabal view
@@ -0,0 +1,47 @@+cabal-version:      3.0+name:               heapsize+version:            0.1+license:            BSD-3-Clause+license-file:       LICENSE+category:           GHC, Debug, Development+build-type:         Simple+author:             Michail Pardalos <mpardalos@gmail.com>+maintainer:         Michail Pardalos <mpardalos@gmail.com>+copyright:          Michail Pardalos 2020+synopsis:           Determine the size of runtime data structures+description:        heapsize is a tool to determine the size data structures.+                    Determining the size of recursive data structures is+                    supported. All sizes are in Bytes.++Library+  Exposed-modules:+    HeapSize+  c-sources:+    cbits/heapsize_prim.cmm+  Default-Language: Haskell2010+  Build-depends:+      base >= 4.12 && < 4.15+    , deepseq >= 1.3 && < 1.5+    , ghc-heap >= 8.6+    , unordered-containers ==0.2.*+    , hashable ==1.3.*+    , primitive+  Hs-source-dirs: src/+  Ghc-options: -Wall++benchmark benchmarks+  build-depends:+      base+    , heapsize+    , criterion+    , primitive+    , deepseq+  default-language: Haskell2010+  ghc-options:      -Wall+  hs-source-dirs:   benchmarks+  main-is:          Main.hs+  type:             exitcode-stdio-1.0++source-repository head+    type:     git+    location: https://github.com/mpardalos/heapsize.git
+ src/HeapSize.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GHCForeignImportPrim #-}+{-# LANGUAGE UnliftedFFITypes #-}++{- |+   Module      : HeapSize+   Copyright   : (c) Michail Pardalos+   License     : 3-Clause BSD-style+   Maintainer  : mpardalos@gmail.com++   Based on GHC.Datasize by Dennis Felsing+ -}+module HeapSize (+  recursiveSize,+  recursiveSizeNoGC,+  recursiveSizeNF,+  closureSize+  )+  where++import Control.DeepSeq (NFData, force)++import GHC.Exts hiding (closureSize#)+import GHC.Arr+import GHC.Exts.Heap hiding (size)+import qualified Data.HashSet as H+import Data.IORef+import Data.Hashable++import Control.Monad++import System.Mem++foreign import prim "aToWordzh" aToWord# :: Any -> Word#+foreign import prim "unpackClosurePtrs" unpackClosurePtrs# :: Any -> Array# b+foreign import prim "closureSize" closureSize# :: Any -> Int#++-- | Get the *non-recursive* size of an closure in words+closureSize :: a -> IO Int+closureSize x = return (I# (closureSize# (unsafeCoerce# x)))++getClosures :: a -> IO (Array Int Box)+getClosures x = case unpackClosurePtrs# (unsafeCoerce# x) of+    pointers ->+      let nelems = I# (sizeofArray# pointers)+      in pure (fmap Box $ Array 0 (nelems - 1) nelems pointers)++-- | Calculate the recursive size of GHC objects in Bytes. Note that the actual+--   size in memory is calculated, so shared values are only counted once.+--+--   Call with+--   @+--    recursiveSize $! 2+--   @+--   to force evaluation to WHNF before calculating the size.+--+--   Call with+--   @+--    recursiveSize $!! \"foobar\"+--   @+--   ($!! from Control.DeepSeq) to force full evaluation before calculating the+--   size.+--+--   A garbage collection is performed before the size is calculated, because+--   the garbage collector would make heap walks difficult.+--+--   This function works very quickly on small data structures, but can be slow+--   on large and complex ones. If speed is an issue it's probably possible to+--   get the exact size of a small portion of the data structure and then+--   estimate the total size from that.+recursiveSize :: a -> IO Int+recursiveSize x = performGC >> recursiveSizeNoGC x++-- | Same as `recursiveSize` except without performing garbage collection first.+--   Useful if you want to measure the size of many objects in sequence. You can+--   call `performGC` once at first and then use this function to avoid multiple+--   unnecessary garbage collections.+recursiveSizeNoGC :: a -> IO Int+recursiveSizeNoGC x = do+  state <- newIORef (0, H.empty)+  go state (asBox x)++  fst <$> readIORef state+  where+    go :: IORef (Int, H.HashSet HashableBox) -> Box -> IO ()+    go state b@(Box y) = do+      (_, closuresSeen) <- readIORef state++      when (not $ H.member (HashableBox b) closuresSeen) $ do+        thisSize <- closureSize y+        modifyIORef state $ \(size, _) ->+          (size + thisSize, H.insert (HashableBox b) closuresSeen)++        mapM_ (go state) =<< getClosures y++-- | Calculate the recursive size of GHC objects in Bytes after calling+-- Control.DeepSeq.force on the data structure to force it into Normal Form.+-- Using this function requires that the data structure has an `NFData`+-- typeclass instance.++recursiveSizeNF :: NFData a => a -> IO Int+recursiveSizeNF = recursiveSize . force++newtype HashableBox = HashableBox Box+    deriving newtype Show++-- | Pointer Equality+instance Eq HashableBox where+    (HashableBox (Box a1)) == (HashableBox (Box a2)) =+        W# (aToWord# a1) == W# (aToWord# a2)++-- | Pointer hash+instance Hashable HashableBox where+    hashWithSalt n (HashableBox (Box a)) = hashWithSalt n (W# (aToWord# a))