Last active
January 18, 2018 18:59
-
-
Save lehins/0e15d8bd2e6cd660d835cd7b0cd90c6c to your computer and use it in GitHub Desktop.
encodeOctal Benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# LANGUAGE BangPatterns #-} | |
{-# LANGUAGE DeriveGeneric #-} | |
module Main where | |
import Control.Monad.Catch | |
import Criterion.Main | |
import Data.ByteString (ByteString) | |
import qualified Data.ByteString as S | |
import Data.ByteString.Builder | |
import Data.Monoid | |
import GHC.Generics | |
import Numeric | |
data OverflowException = OverflowException String deriving (Show, Generic) | |
instance Exception OverflowException | |
main :: IO () | |
main = defaultMain $ map (makeGroup baseInt) [7, 11] | |
++ map (makeGroup baseInteger) [100, 1000] | |
where | |
baseInt = 8 :: Int | |
baseInteger = 8 :: Integer | |
makeGroup base size = | |
bgroup | |
(show size) | |
[ bgroup | |
"small num" | |
[ bench "ByteString/unfoldrN" $ | |
nfIO (toLazyByteString <$> encodeOctal size base) | |
, bench "Builder/monoid" $ nfIO (toLazyByteString <$> encodeOctal' size base) | |
, bench "Numeric" $ nfIO (toLazyByteString <$> encodeOctal'' size base) | |
] | |
, bgroup | |
"big num" | |
[ bench "ByteString/unfoldrN" $ | |
nfIO (toLazyByteString <$> encodeOctal size (base ^ size - 1)) | |
, bench "Builder/monoid" $ | |
nfIO (toLazyByteString <$> encodeOctal' size (base ^ size - 1)) | |
, bench "Numeric" $ | |
nfIO (toLazyByteString <$> encodeOctal'' size (base ^ size - 1)) | |
] | |
] | |
encodeOctal :: (Show a, Integral a, MonadThrow m) => Int -> a -> m Builder | |
encodeOctal !len !val = do | |
enc <- case S.unfoldrN len toOctal val of | |
(valStr, Just 0) -> return valStr | |
over -> throwM $ OverflowException $ "<encodeOctal>: Tar value overflow: " ++ show over | |
return (byteString $ S.reverse enc) | |
where | |
toOctal 0 = Just (0, 0) | |
toOctal x = | |
let !(q, r) = x `quotRem` 8 | |
in Just (fromIntegral r + 48, q) | |
encodeOctal' :: (Show a, Integral a, MonadThrow m) => Int -> a -> m Builder | |
encodeOctal' !len !val = go 0 val mempty | |
where | |
go !n !cur !acc | |
| cur == 0 = | |
if n < len | |
then return $ byteString (S.replicate (len - n) 48) <> acc | |
else return acc | |
| n < len = | |
let !(q, r) = cur `quotRem` 8 | |
in go (n + 1) q (word8 (fromIntegral r + 48) <> acc) | |
| otherwise = | |
throwM $ | |
OverflowException $ | |
"<encodeOctal>: Tar value overflow (for maxLen " ++ show len ++ "): " ++ show val | |
encodeOctal'' :: (Show a, Integral a, MonadThrow m) => Int -> a -> m Builder | |
encodeOctal'' !len !val = return $ byteString (S.replicate (len - length enc) 48) <> string7 enc | |
where enc = showOct val "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Adding the short circuit to
unfoldrN
approach speeds it up for large zero padded numbers: