packages feed

mariadb-haskell-0.1.0.0: validation/hs/Validation/Supplementary.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Validation.Supplementary (run) where

import Control.Exception (evaluate)
import Control.Monad
import Data.List (isPrefixOf)
import Data.IORef
import qualified Data.ByteString as B
import GHC.Clock
import GHC.Stats
import System.Mem
import Database.MySQL.Base
import qualified System.IO.Streams as S
import Validation.Common

-- Separate experiment: no change to any frozen benchmark function or enumeration.
run :: String -> Int -> IO ()
run ident n = case ident of
    "connect.tcp" -> measured (fromIntegral n) $ replicateM_ n (withConn (const (pure ()))) >> pure (fromIntegral n)
    "parameters.select" -> withConn $ \c -> measured (fromIntegral n) $ loop n (param c)
    "parameters.mixed" -> withConn $ \c -> measured (fromIntegral n) $ loop n (scalarInt c "SELECT 3" >> param c)
    "transaction.empty" -> withConn $ \c -> measured (fromIntegral n) $ loop n (withTransaction c (pure 7))
    "batch.1000" -> withConn $ \c -> do
        exec c "CREATE TEMPORARY TABLE mb_stage1_batch(n INT)"
        let action = do
                exec c "TRUNCATE mb_stage1_batch"
                void (executeMany c "INSERT INTO mb_stage1_batch VALUES(?)" [[MySQLInt32 x] | x <- [1..1000]])
                scalarInt c "SELECT SUM(n) FROM mb_stage1_batch"
        measured (fromIntegral (n*1000)) (loop n action)
    "stream.retention" -> withConn $ \c -> measured (fromIntegral n) $ do
        (_,stream) <- query_ c (sql ("SELECT seq,REPEAT('x',128) FROM seq_1_to_" ++ show n))
        S.fold (\a row -> a + sum [number v | v <- take 1 row]) 0 stream
    "retention.tiny-slice" -> withConn $ \c -> do
        retained <- newIORef B.empty
        (_,stream) <- query_ c (sql ("SELECT CAST('x' AS BINARY),CAST(REPEAT('y',"++show n++") AS BINARY)"))
        start <- getMonotonicTimeNSec
        let saveFirst = do
                value <- S.read stream
                case value of
                    Just (MySQLBytes small:_) -> writeIORef retained small
                    _ -> fail "tiny slice shape"
        saveFirst
        S.skipToEof stream
        performGC
        stats <- getRTSStats
        small <- readIORef retained
        equal "tiny slice retained exactly" "x" small
        end <- getMonotonicTimeNSec
        putStrLn ("RETENTION\t"++ident++"\t"++show n++"\t"++show (end-start)++"\t"++show (gcdetails_live_bytes (gc stats))++"\t1")
    _ | "retention." `isPrefixOf` ident -> withConn $ \c -> do
        (_,stream) <- query_ c (sql ("SELECT seq,REPEAT('x',128) FROM seq_1_to_" ++ show n))
        let consume count total = do
                next <- S.read stream
                case next of
                    Nothing -> pure total
                    Just values -> do
                        when (count `mod` 10000==0) $ do
                            performGC
                            stats <- getRTSStats
                            putStrLn ("LIVE_HEAP\t"++show count++"\t"++show (gcdetails_live_bytes (gc stats)))
                        value <- case values of x:_ -> pure (number x); [] -> fail "empty retention row"
                        value `seq` total `seq` consume (count+1) (total+value)
        start <- getMonotonicTimeNSec
        checksum <- consume (0::Int) 0
        end <- getMonotonicTimeNSec
        equal "stream retention checksum" (toInteger n*(toInteger n+1) `div` 2) checksum
        performGC
        stats <- getRTSStats
        putStrLn ("RETENTION\t"++ident++"\t"++show n++"\t"++show (end-start)++"\t"++show (gcdetails_live_bytes (gc stats))++"\t"++show checksum)
      | otherwise -> fail ("unknown supplementary profile: " ++ ident)
  where
    param c = do
        (_,stream) <- query c "SELECT ?" [MySQLInt32 7]
        xs <- S.toList stream
        case xs of [[x]] -> pure (number x); _ -> fail "parameter checksum shape"
    loop count action = sum <$> replicateM count action
    measured :: Integer -> IO Integer -> IO ()
    measured units action = do
        -- Two warm-ups with the same operation, then one measured region.
        void action; void action
        performGC
        before <- getRTSStats
        start <- getMonotonicTimeNSec
        checksum <- action >>= evaluate
        end <- getMonotonicTimeNSec
        performGC
        after <- getRTSStats
        putStrLn ("STAGE1\t" ++ ident ++ "\t" ++ show (units :: Integer) ++ "\t" ++ show (end-start)
            ++ "\t" ++ show (allocated_bytes after-allocated_bytes before)
            ++ "\t" ++ show (max_live_bytes after) ++ "\t" ++ show checksum)