snappy / tests / Speedy.hs

{-# LANGUAGE BangPatterns, DeriveDataTypeable, OverloadedStrings,
    RecordWildCards #-}

import Control.Exception
import Data.Maybe
import Control.Monad
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as L
import System.IO
import System.Exit
import qualified Codec.Compression.GZip as G
import qualified Codec.Compression.Snappy as S
import System.Console.CmdArgs
import Data.Data
import Data.Typeable
import Data.Time.Clock

data Codec = Snappy | GZip
             deriving (Eq, Show, Typeable, Data)

data Action = Compress | Decompress
              deriving (Eq, Show, Typeable, Data)

data Command = Command {
      action :: Action
    , codec :: Codec
    , level :: Maybe Int
    , number :: Maybe Int
    , files :: [FilePath]
    } deriving (Show, Typeable, Data)

command = Command { action = enum [Compress, Decompress]
                  , codec = enum [Snappy, GZip]
                  , level = def
                  , number = def
                  , files = def &= args
                  }

rnf (L.Chunk _ cs) = rnf cs
rnf _              = ()

snappy Command{..} f = do
  bs0 <- B.readFile f
  let bs | action == Compress = bs0
         | otherwise          = S.compress bs0
      count = fromMaybe (200000000 `div` B.length bs) number
      c !i s | i >= count = ()
             | otherwise  = S.compress s `seq` c (i+1) s
      d !i s | i >= count = ()
             | otherwise  = S.decompress s `seq` d (i+1) s
  start <- getCurrentTime
  evaluate $ if action == Compress then c 0 bs else d 0 bs
  time <- (fromRational . toRational . flip diffUTCTime start) `fmap`
          getCurrentTime
  return (fromIntegral (B.length bs) * fromIntegral count / (time * 1048576.0),
          (B.length (S.compress bs0) * 100) `div` B.length bs0)

gzip Command{..} f = do
  bs0 <- L.readFile f
  let bs | action == Compress = bs0
         | otherwise          = compress bs0
      compress = G.compressWith G.defaultCompressParams {
                   G.compressLevel = G.CompressionLevel $ fromMaybe 3 level
                 }
      len = L.length bs
      count = fromMaybe (25000000 `div` fromIntegral len) number
      c !i s | i >= count = ()
             | otherwise  = rnf (compress s) `seq` c (i+1) s
      d !i s | i >= count = ()
             | otherwise  = rnf (G.decompress s) `seq` d (i+1) s
  start <- getCurrentTime
  evaluate $ if action == Compress then c 0 bs else d 0 bs
  time <- (fromRational . toRational . flip diffUTCTime start) `fmap`
          getCurrentTime
  return (fromIntegral len * fromIntegral count / (time * 1048576.0),
          fromIntegral $ (L.length (compress bs0) * 100) `div` L.length bs0)

main = do
  c@Command{..} <- cmdArgs command
  forM_ files $ \f -> do
    (mbSec, ratio) <- (if codec == Snappy then snappy else gzip) c f
    putStrLn $ show codec ++ " " ++ show action ++ " " ++
               show f ++ ": " ++ show (round mbSec) ++ " MB/sec, " ++
               show (100 - ratio) ++ "% smaller"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.