[GHC] #7561: Unnecessary Heap Allocations - Slow Performance

GHC cvs-ghc at haskell.org
Sat Jan 19 10:38:15 CET 2013


#7561: Unnecessary Heap Allocations - Slow Performance
-------------------------------+--------------------------------------------
    Reporter:  wurmli          |       Owner:                         
        Type:  bug             |      Status:  new                    
    Priority:  normal          |   Milestone:  7.8.1                  
   Component:  Compiler        |     Version:  7.6.1                  
    Keywords:                  |          Os:  Linux                  
Architecture:  x86_64 (amd64)  |     Failure:  Runtime performance bug
  Difficulty:  Unknown         |    Testcase:                         
   Blockedby:                  |    Blocking:                         
     Related:                  |  
-------------------------------+--------------------------------------------

Comment(by wurmli):

 Let me add a similar case, exemplifying better why I think that not the
 vector library, but the way the optimiser deals with reductions seems to
 be the culprit. (That's why I had put "unnecessary heap allocations in the
 title; #1498 might be a related ticket.)

 {{{

 import System.Environment
 import Control.Applicative
 import Control.Monad

 import Control.Monad.ST

 import qualified Data.Vector.Storable.Mutable as VSM
 import qualified Data.Vector.Storable as G


 -------------------------------------------------------------------
 -- Increment counter by 1

 acc1 v = do{
   k<-VSM.read v 0;
   VSM.write v 0 (k+1)
   }

 -- Increment counter 4 times by 1 until limit is reached
 -- i.e. each loop adds 4

 whileModify1 :: G.MVector s Int -> ST s ()
 whileModify1 v = do
   go <- do{ k<-VSM.read v 0; n<-VSM.read v 1; return (compare k n)}
   case go of
     LT -> do {acc1 v; acc1 v; acc1 v; acc1 v; whileModify1 v}
     EQ -> return ()
     GT -> return ()

 -------------------------------------------------------------------
 -- Increment counter by 2

 acc2 v = do{
   k<-VSM.read v 0;
   VSM.write v 0 (k+1);
   k<-VSM.read v 0;
   VSM.write v 0 (k+1);
   }

 -- Increment counter twice by 2 until limit is reached
 -- i.e. each loop adds 4

 whileModify2 :: G.MVector s Int -> ST s ()
 whileModify2 v = do
   go <- do{ k<-VSM.read v 0; n<-VSM.read v 1; return (compare k n)}
   case go of
     LT -> do {acc2 v; acc2 v; whileModify2 v}
     EQ -> return ()
     GT -> return ()

 --------------------------------------------------------------------

 -- Case 1 is fast with low use of heap space
 -- Case 2 is slow with high use of heap
           (about 400 times slower, 300 times more heap)

 testAcc :: Int -> Int -> G.Vector Int
 testAcc a n = runST $ do
   v <- (G.thaw $ G.fromList [0,n]) :: ST s (G.MVector s Int)
   case a of
     1 -> whileModify1 v
     2 -> whileModify2 v
   G.freeze v

 main = do
   let k = 50000000
   n <- read.head <$> getArgs
   putStrLn $ show $ testAcc n k

 }}}

 The llvm backend improves timing about 4 fold

 {{{
 ghc -threaded -rtsopts -O2 -fllvm heapAndSpeed.hs
 }}}

 Even though the two ways of increasing the counter are almost the same,
 the resulting code behaves vastly different, not only in speed, but also
 in heap usage (and garbage collector activity).

 {{{
 ./heapAndSpeed 1 +RTS -s

 fromList [50000000,50000000]
           74,088 bytes allocated in the heap
            6,296 bytes copied during GC
           47,184 bytes maximum residency (1 sample(s))
           18,352 bytes maximum slop
                1 MB total memory in use (0 MB lost due to fragmentation)

 ...

 INIT    time    0.00s  (  0.00s elapsed)
 MUT     time    0.02s  (  0.02s elapsed)
 GC      time    0.00s  (  0.00s elapsed)
 EXIT    time    0.00s  (  0.00s elapsed)
 Total   time    0.02s  (  0.02s elapsed)

 }}}

 {{{

 ./heapAndSpeed 2 +RTS -s

 fromList [50000000,50000000]
   25,000,074,088 bytes allocated in the heap
        7,048,952 bytes copied during GC
           47,184 bytes maximum residency (2 sample(s))
           22,448 bytes maximum slop
                1 MB total memory in use (0 MB lost due to fragmentation)

 ...

 INIT    time    0.00s  (  0.00s elapsed)
 MUT     time    8.06s  (  8.07s elapsed)
 GC      time    0.17s  (  0.17s elapsed)
 EXIT    time    0.00s  (  0.00s elapsed)
 Total   time    8.23s  (  8.24s elapsed)

 }}}

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/7561#comment:5>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler



More information about the ghc-tickets mailing list