serialise-0.1.0.0: bench/versus/Utils.hs
{-# LANGUAGE CPP #-}
module Utils
( prepBenchmarkFiles -- :: IO ()
) where
import Data.Time
import System.IO (hFlush, stdout)
import System.Mem
import Control.Monad (when)
import System.FilePath
import System.Directory
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BS
import qualified Codec.Compression.GZip as GZip
import Macro.DeepSeq ()
import qualified Macro.Load as Load
import qualified Macro.PkgBinary as PkgBinary
import qualified Macro.PkgCereal as PkgCereal
import qualified Macro.PkgStore as PkgStore
import qualified Macro.CBOR as CBOR
--------------------------------------------------------------------------------
-- | Get the path to the Hackage.haskell.org package index. Works on Windows
-- and Linux.
getHackageIndexLocation :: IO FilePath
getHackageIndexLocation = do
cabalDir <- getAppUserDataDirectory "cabal"
let dir = cabalDir </> "packages" </> "hackage.haskell.org"
return (dir </> "01-index.tar.gz")
-- | Copy the hackage index to a local directory. Returns the path to the
-- directory containing the file, and the file path itself.
copyHackageIndex :: IO (FilePath, FilePath)
copyHackageIndex = do
hackageIndex <- getHackageIndexLocation
let dataDir = "bench" </> "data"
dest = dataDir </> "01-index.tar.gz"
-- Create the data dir, and copy the index.
-- We do not try to create the 'bench' directory since it should exist.
createDirectoryIfMissing False dataDir
exists <- doesFileExist dest
when (not exists) $ do
notice "Copying hackage index" (copyFile hackageIndex dest)
return (dataDir, dest)
-- | Prepare all the files needed for the benchmarks to properly run,
-- including needing a copy of the Hackage index, and a few encodings
-- of some of its contents
prepBenchmarkFiles :: IO ()
prepBenchmarkFiles = do
-- Set up index
(destDir, indexFile) <- copyHackageIndex
-- Read it, and take about 20,000 entries. And a small set of 1,000 too.
let readIndex = Load.readPkgIndex . GZip.decompress
Right pkgs_ <- fmap readIndex (BS.readFile indexFile)
let _pkgs1k = take 1000 pkgs_
_pkgs20k = take 20000 pkgs_
-- Write a file to the temporary directory, if it does not exist.
write p bs = do
let file = destDir </> p
exists <- doesFileExist file
when (not exists) $ do
let msg = "Creating " ++ file
notice msg (BS.writeFile file bs)
-- Write a file to the temporary directory, if it does not exist.
-- Strict version.
writeS p bs = do
let file = destDir </> p
exists <- doesFileExist file
when (not exists) $ do
let msg = "Creating " ++ file
notice msg (B.writeFile file bs)
-- Encode that dense data in several forms, and write those forms out
-- to disk. TODO FIXME (aseipp): should we actually have the -small variant?
-- It doesn't have any similar benchmarks for the other libraries, and seems
-- like it isn't very useful, given the other ones.
write "binary.bin" (PkgBinary.serialise _pkgs20k)
write "cereal.bin" (PkgCereal.serialise _pkgs20k)
write "cbor.bin" (CBOR.serialise _pkgs20k)
writeS "store.bin" (PkgStore.serialise _pkgs20k)
--write "cbor-small.bin" (CBOR.serialise _pkgs1k)
-- And before we finish: do a garbage collection to clean up anything left
-- over.
notice "Preparation done; performing GC" doGC
--------------------------------------------------------------------------------
-- | Do a garbage collection.
doGC :: IO ()
#if MIN_VERSION_base(4,7,0)
doGC = performMinorGC >> performMajorGC
#else
doGC = performGC
#endif
-- Write a notice to the screen (with timing information).
notice :: String -> IO a -> IO a
notice m k = do
putStr ("INFO: " ++ m ++ "... ")
hFlush stdout
(v,t) <- timeIt k
putStrLn $ "OK (in " ++ show t ++ ")"
return v
-- | Time some action and return the time difference.
timeIt :: IO a -> IO (a, NominalDiffTime)
timeIt action = do
t <- getCurrentTime
x <- action
t' <- getCurrentTime
return (x, diffUTCTime t' t)