[commit: packages/binary] master: Optimize roll by using foldl' instead of foldr (1f643cb)

git at git.haskell.org git at git.haskell.org
Wed Dec 16 09:43:13 UTC 2015


Repository : ssh://git@git.haskell.org/binary

On branch  : master
Link       : http://git.haskell.org/packages/binary.git/commitdiff/1f643cbc973751605ec6a3ed0d93a6dab0bd0774

>---------------------------------------------------------------

commit 1f643cbc973751605ec6a3ed0d93a6dab0bd0774
Author: Bas van Dijk <v.dijk.bas at gmail.com>
Date:   Mon Sep 28 22:39:28 2015 +0200

    Optimize roll by using foldl' instead of foldr
    
    The "roll" benchmarks in the get executable show the difference:
    
      get roll
      benchmarking roll/foldr
      time                 547.4 ms   (537.8 ms .. 553.5 ms)
                           1.000 R²   (1.000 R² .. 1.000 R²)
      mean                 549.4 ms   (547.2 ms .. 550.4 ms)
      std dev              1.857 ms   (0.0 s .. 1.896 ms)
      variance introduced by outliers: 19% (moderately inflated)
    
      benchmarking roll/foldl'
      time                 434.7 ms   (426.4 ms .. 443.1 ms)
                           1.000 R²   (1.000 R² .. 1.000 R²)
      mean                 433.7 ms   (432.3 ms .. 434.7 ms)
      std dev              1.472 ms   (0.0 s .. 1.696 ms)
      variance introduced by outliers: 19% (moderately inflated)
    
    The "Integer/decode" benchmark shows the actual decoding speed.
    First the old implementation based on foldr:
    
      get Integer/decode
      benchmarking Integer/decode
      time                 552.9 ms   (540.2 ms .. 569.3 ms)
                           1.000 R²   (1.000 R² .. 1.000 R²)
      mean                 558.2 ms   (556.1 ms .. 559.3 ms)
      std dev              1.824 ms   (0.0 s .. 1.906 ms)
      variance introduced by outliers: 19% (moderately inflated)
    
    The new implementation based on foldl':
    
      get Integer/decode
      benchmarking Integer/decode
      time                 457.5 ms   (406.2 ms .. 505.6 ms)
                           0.998 R²   (0.994 R² .. 1.000 R²)
      mean                 455.9 ms   (448.3 ms .. 462.3 ms)
      std dev              10.11 ms   (0.0 s .. 11.02 ms)
      variance introduced by outliers: 19% (moderately inflated)


>---------------------------------------------------------------

1f643cbc973751605ec6a3ed0d93a6dab0bd0774
 benchmarks/Get.hs        | 34 ++++++++++++++++++++++++++++++++--
 src/Data/Binary/Class.hs |  6 +++---
 2 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/benchmarks/Get.hs b/benchmarks/Get.hs
index 26c2f98..191f585 100644
--- a/benchmarks/Get.hs
+++ b/benchmarks/Get.hs
@@ -12,10 +12,12 @@ import Criterion.Main
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Char8 as C8
 import qualified Data.ByteString.Lazy as L
+import Data.Bits
 import Data.Char (ord)
-import Data.Word (Word8)
+import Data.List (foldl')
 
 import Control.Applicative
+import Data.Binary
 import Data.Binary.Get
 
 import qualified Data.Serialize.Get as Cereal
@@ -36,7 +38,9 @@ main = do
     rnf bracketsInChunks,
     rnf bracketCount,
     rnf oneMegabyte,
-    rnf oneMegabyteLBS
+    rnf oneMegabyteLBS,
+    rnf manyBytes,
+    rnf encodedBigInteger
      ]
   defaultMain
     [ bgroup "brackets"
@@ -89,6 +93,13 @@ main = do
         , bench "chunk size 16 bytes" $
             whnf (runTest (getWord8N16A mega)) oneMegabyteLBS
         ]
+    , bgroup "roll"
+        [ bench "foldr"  $ nf (roll_foldr  :: [Word8] -> Integer) manyBytes
+        , bench "foldl'" $ nf (roll_foldl' :: [Word8] -> Integer) manyBytes
+        ]
+    , bgroup "Integer"
+        [ bench "decode" $ nf (decode :: L.ByteString -> Integer) encodedBigInteger
+        ]
     ]
 
 checkBracket :: Int -> Int
@@ -349,3 +360,22 @@ getWord8N16A = loop []
                     <*> getWord8
                     <*> getWord8
           loop (v:s) (n-16)
+
+manyBytes :: [Word8]
+manyBytes = concat $ replicate 256 [0..255]
+
+bigInteger :: Integer
+bigInteger = roll_foldl' manyBytes
+
+encodedBigInteger :: L.ByteString
+encodedBigInteger = encode bigInteger
+
+roll_foldr :: (Integral a, Num a, Bits a) => [Word8] -> a
+roll_foldr   = foldr unstep 0
+  where
+    unstep b a = a `shiftL` 8 .|. fromIntegral b
+
+roll_foldl' :: (Integral a, Num a, Bits a) => [Word8] -> a
+roll_foldl'   = foldl' unstep 0 . reverse
+  where
+    unstep a b = a `shiftL` 8 .|. fromIntegral b
diff --git a/src/Data/Binary/Class.hs b/src/Data/Binary/Class.hs
index 37117f7..b0f7529 100644
--- a/src/Data/Binary/Class.hs
+++ b/src/Data/Binary/Class.hs
@@ -53,7 +53,7 @@ import Data.ByteString.Lazy (ByteString)
 import qualified Data.ByteString.Lazy as L
 
 import Data.Char    (ord)
-import Data.List    (unfoldr)
+import Data.List    (unfoldr, foldl')
 
 -- And needed for the instances:
 import qualified Data.ByteString as B
@@ -249,9 +249,9 @@ unroll = unfoldr step
     step i = Just (fromIntegral i, i `shiftR` 8)
 
 roll :: (Integral a, Num a, Bits a) => [Word8] -> a
-roll   = foldr unstep 0
+roll   = foldl' unstep 0 . reverse
   where
-    unstep b a = a `shiftL` 8 .|. fromIntegral b
+    unstep a b = a `shiftL` 8 .|. fromIntegral b
 
 #ifdef HAS_NATURAL
 -- Fixed-size type for a subset of Natural



More information about the ghc-commits mailing list