packages feed

packed-data-0.1.0.0: benchmark/AST.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CApiFFI #-}
{-# LANGUAGE TemplateHaskell #-}

module AST (benchmark) where

import Criterion.Main
import Data.ByteString.Internal
import Data.Packed
import qualified Data.Packed.Reader as R
import Data.Void
import Foreign
import Foreign.C
import Foreign.ForeignPtr.Unsafe
import Utils
import Prelude hiding (sum)

foreign import capi unsafe "benchmark.h eval" c_eval :: Ptr Void -> IO CLong

foreign import capi unsafe "benchmark.h build_ast" c_build_ast :: CInt -> IO (Ptr Void)

foreign import capi unsafe "benchmark.h free_ast" c_free_ast :: Ptr Void -> IO ()

data AST = Value Int32 | Add AST AST | Sub AST AST | Mul AST AST

$(mkPacked ''AST [])

benchmark :: [Int] -> Benchmark
benchmark depths =
    bgroup
        "ast"
        $ fmap buildAndEvaluateASTWithDepth depths

buildAndEvaluateASTWithDepth :: Int -> Benchmark
buildAndEvaluateASTWithDepth n =
    bgroup
        (depthGroupName n)
        [ envWithCleanup (c_build_ast $ fromIntegral n) c_free_ast $ bench cTestName . nfAppIO c_eval
        , bench nativeTestName $ nf eval nativeAST
        , bench packedTestName $ nfAppIO (runReader evalPacked) packedAST
        , bench packedWithUnpackTestName $ whnf (eval . fst . unpack) packedAST
        , bench nonMonadicPackedTestName $ nfAppIO evalPackedNonMonadic packedAST
        ]
  where
    !packedAST = pack nativeAST
    !nativeAST = buildNativeAST n

eval :: AST -> Int32
eval (Value n) = n
eval (Add a b) = eval a + eval b
eval (Sub a b) = eval a - eval b
eval (Mul a b) = eval a * eval b

evalPacked :: PackedReader '[AST] r Int32
evalPacked =
    caseAST
        reader
        (opLambda (+))
        (opLambda (-))
        (opLambda (*))
  where
    {-# INLINE opLambda #-}
    opLambda ::
        (Int32 -> Int32 -> Int32) ->
        PackedReader '[AST, AST] r Int32
    opLambda f = R.do
        left <- evalPacked
        right <- evalPacked
        R.return (f left right)

evalPackedNonMonadic :: Packed (AST ': r) -> IO Int
evalPackedNonMonadic packed = fst <$> go (unsafeForeignPtrToPtr fptr)
  where
    (BS fptr _) = fromPacked packed
    go :: Ptr Word8 -> IO (Int, Ptr Word8)
    go ptr = do
        tag <- peek ptr :: IO Word8
        let !nextPtr = ptr `plusPtr` 1
        case tag of
            0 -> do
                !n <- peek nextPtr :: IO Int32
                return (fromIntegral n, plusPtr nextPtr (sizeOf n))
            1 -> opLambda (+) nextPtr
            2 -> opLambda (-) nextPtr
            3 -> opLambda (*) nextPtr
            _ -> undefined
    {-# INLINE opLambda #-}
    opLambda :: (Int -> Int -> Int) -> Ptr Int32 -> IO (Int, Ptr Word8)
    opLambda f ptr = do
        (!left, !r) <- go $ castPtr ptr
        (!right, !r1) <- go r
        let !res = left `f` right
        return (res, r1)

buildNativeAST :: Int -> AST
buildNativeAST 0 = Value 1
buildNativeAST n = Add (buildNativeAST (n - 1)) (buildNativeAST (n - 1))