Skip to content

Commit 637fec5

Browse files
authored
Merge pull request #446 from IntersectMBO/jdral/plot-wp8-latency
Measure latency for batches of updates and lookups in the WP8 benchmark
2 parents a0b1abc + 6c49199 commit 637fec5

File tree

2 files changed

+165
-37
lines changed

2 files changed

+165
-37
lines changed

bench/macro/lsm-tree-bench-wp8.hs

Lines changed: 153 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,18 @@ import Control.Concurrent (getNumCapabilities)
4141
import Control.Concurrent.Async
4242
import Control.Concurrent.MVar
4343
import Control.DeepSeq (force)
44-
import Control.Exception (evaluate)
44+
import Control.Exception
4545
import Control.Monad (forM_, unless, void, when)
4646
import Control.Monad.Trans.State.Strict (runState, state)
4747
import Control.Tracer
4848
import qualified Data.ByteString.Short as BS
4949
import qualified Data.Foldable as Fold
5050
import qualified Data.IntSet as IS
51-
import Data.IORef (modifyIORef', newIORef, readIORef, writeIORef)
51+
import Data.IORef
5252
import qualified Data.List.NonEmpty as NE
5353
import Data.Map.Strict (Map)
5454
import qualified Data.Map.Strict as Map
55+
import Data.Monoid
5556
import qualified Data.Primitive as P
5657
import qualified Data.Vector as V
5758
import Data.Void (Void)
@@ -566,30 +567,31 @@ doRun gopts opts = do
566567
name <- maybe (fail "invalid snapshot name") return $
567568
LSM.mkSnapshotName "bench"
568569

569-
LSM.withSession (mkTracer gopts) hasFS hasBlockIO (FS.mkFsPath []) $ \session -> do
570+
LSM.withSession (mkTracer gopts) hasFS hasBlockIO (FS.mkFsPath []) $ \session ->
571+
withLatencyHandle $ \h -> do
570572
-- open snapshot
571573
-- In checking mode we start with an empty table, since our pure
572574
-- reference version starts with empty (as it's not practical or
573575
-- necessary for testing to load the whole snapshot).
574576
tbl <- if check opts
575-
then LSM.new @IO @K @V @B session (mkTableConfigRun gopts LSM.defaultTableConfig)
576-
else LSM.open @IO @K @V @B session (mkTableConfigOverride gopts) name
577+
then LSM.new @IO @K @V @B session (mkTableConfigRun gopts LSM.defaultTableConfig)
578+
else LSM.open @IO @K @V @B session (mkTableConfigOverride gopts) name
577579

578580
-- In checking mode, compare each output against a pure reference.
579581
checkvar <- newIORef $ pureReference
580-
(initialSize gopts) (batchSize opts)
581-
(batchCount opts) (seed opts)
582+
(initialSize gopts) (batchSize opts)
583+
(batchCount opts) (seed opts)
582584
let fcheck | not (check opts) = \_ _ -> return ()
583-
| otherwise = \b y -> do
585+
| otherwise = \b y -> do
584586
(x:xs) <- readIORef checkvar
585587
unless (x == y) $
586588
fail $ "lookup result mismatch in batch " ++ show b
587589
writeIORef checkvar xs
588590

589591
let benchmarkIterations
590-
| pipelined opts = pipelinedIterations
592+
| pipelined opts = pipelinedIterations h
591593
| lookuponly opts= sequentialIterationsLO
592-
| otherwise = sequentialIterations
594+
| otherwise = sequentialIterations h
593595
!progressInterval = max 1 ((batchCount opts) `div` 100)
594596
madeProgress b = b `mod` progressInterval == 0
595597
(time, _, _) <- timed_ $ do
@@ -612,33 +614,39 @@ doRun gopts opts = do
612614
type LookupResults = V.Vector (K, LSM.LookupResult V ())
613615

614616
{-# INLINE sequentialIteration #-}
615-
sequentialIteration :: (Int -> LookupResults -> IO ())
617+
sequentialIteration :: LatencyHandle
618+
-> (Int -> LookupResults -> IO ())
616619
-> Int
617620
-> Int
618621
-> LSM.Table IO K V B
619622
-> Int
620623
-> MCG.MCG
621624
-> IO MCG.MCG
622-
sequentialIteration output !initialSize !batchSize !tbl !b !g = do
625+
sequentialIteration h output !initialSize !batchSize !tbl !b !g =
626+
withTimedBatch h b $ \tref -> do
623627
let (!g', ls, is) = generateBatch initialSize batchSize g b
624628

625629
-- lookups
626-
results <- LSM.lookups ls tbl
630+
results <- timeLatency tref $ LSM.lookups ls tbl
627631
output b (V.zip ls (fmap (fmap (const ())) results))
628632

629633
-- deletes and inserts
630-
LSM.updates is tbl
634+
_ <- timeLatency tref $ LSM.updates is tbl
631635

632636
-- continue to the next batch
633637
return g'
634638

635-
sequentialIterations :: (Int -> LookupResults -> IO ())
639+
640+
sequentialIterations :: LatencyHandle
641+
-> (Int -> LookupResults -> IO ())
636642
-> Int -> Int -> Int -> Word64
637643
-> LSM.Table IO K V B
638644
-> IO ()
639-
sequentialIterations output !initialSize !batchSize !batchCount !seed !tbl =
645+
sequentialIterations h output !initialSize !batchSize !batchCount !seed !tbl = do
646+
createGnuplotExampleFileSequential
647+
hPutHeaderSequential h
640648
void $ forFoldM_ g0 [ 0 .. batchCount - 1 ] $ \b g ->
641-
sequentialIteration output initialSize batchSize tbl b g
649+
sequentialIteration h output initialSize batchSize tbl b g
642650
where
643651
g0 = initGen initialSize batchSize batchCount seed
644652

@@ -719,7 +727,8 @@ And the initial setup looks like this:
719727
Updates (db_3) tx_2
720728
4. Sync ! (db_3, updates) 2. Sync ? (db_3, updates)
721729
-}
722-
pipelinedIteration :: (Int -> LookupResults -> IO ())
730+
pipelinedIteration :: LatencyHandle
731+
-> (Int -> LookupResults -> IO ())
723732
-> Int
724733
-> Int
725734
-> MVar (LSM.Table IO K V B, Map K (LSM.Update V B))
@@ -729,33 +738,39 @@ pipelinedIteration :: (Int -> LookupResults -> IO ())
729738
-> LSM.Table IO K V B
730739
-> Int
731740
-> IO (LSM.Table IO K V B)
732-
pipelinedIteration output !initialSize !batchSize
741+
pipelinedIteration h output !initialSize !batchSize
733742
!syncTblIn !syncTblOut
734743
!syncRngIn !syncRngOut
735-
!tbl_n !b = do
744+
!tbl_n !b =
745+
withTimedBatch h b $ \tref -> do
736746
g <- takeMVar syncRngIn
737747
let (!g', !ls, !is) = generateBatch initialSize batchSize g b
738748

739749
-- 1: perform the lookups
740-
lrs <- LSM.lookups ls tbl_n
750+
lrs <- timeLatency tref $ LSM.lookups ls tbl_n
741751

742752
-- 2. sync: receive updates and new table
743-
putMVar syncRngOut g'
744-
(tbl_n1, delta) <- takeMVar syncTblIn
753+
tbl_n1 <- timeLatency tref $ do
754+
putMVar syncRngOut g'
755+
(tbl_n1, delta) <- takeMVar syncTblIn
745756

746-
-- At this point, after syncing, our peer is guaranteed to no longer be
747-
-- using tbl_n. They used it to generate tbl_n+1 (which they gave us).
748-
LSM.close tbl_n
749-
output b $! applyUpdates delta (V.zip ls lrs)
757+
-- At this point, after syncing, our peer is guaranteed to no longer be
758+
-- using tbl_n. They used it to generate tbl_n+1 (which they gave us).
759+
LSM.close tbl_n
760+
output b $! applyUpdates delta (V.zip ls lrs)
761+
pure tbl_n1
750762

751763
-- 3. perform the inserts and report outputs (in any order)
752-
tbl_n2 <- LSM.duplicate tbl_n1
753-
LSM.updates is tbl_n2
764+
tbl_n2 <- timeLatency tref $ do
765+
tbl_n2 <- LSM.duplicate tbl_n1
766+
LSM.updates is tbl_n2
767+
pure tbl_n2
754768

755769
-- 4. sync: send the updates and new table
756-
let delta' :: Map K (LSM.Update V B)
757-
!delta' = Map.fromList (V.toList is)
758-
putMVar syncTblOut (tbl_n2, delta')
770+
timeLatency tref $ do
771+
let delta' :: Map K (LSM.Update V B)
772+
!delta' = Map.fromList (V.toList is)
773+
putMVar syncTblOut (tbl_n2, delta')
759774

760775
return tbl_n2
761776
where
@@ -768,11 +783,14 @@ pipelinedIteration output !initialSize !batchSize
768783
Nothing -> (k, fmap (const ()) lr)
769784
Just u -> (k, updateToLookupResult u)
770785

771-
pipelinedIterations :: (Int -> LookupResults -> IO ())
786+
pipelinedIterations :: LatencyHandle
787+
-> (Int -> LookupResults -> IO ())
772788
-> Int -> Int -> Int -> Word64
773789
-> LSM.Table IO K V B
774790
-> IO ()
775-
pipelinedIterations output !initialSize !batchSize !batchCount !seed tbl_0 = do
791+
pipelinedIterations h output !initialSize !batchSize !batchCount !seed tbl_0 = do
792+
createGnuplotExampleFilePipelined
793+
hPutHeaderPipelined h
776794
n <- getNumCapabilities
777795
printf "INFO: the pipelined benchmark is running with %d capabilities.\n" n
778796

@@ -795,14 +813,14 @@ pipelinedIterations output !initialSize !batchSize !batchCount !seed tbl_0 = do
795813

796814
threadA =
797815
forFoldM_ tbl_1 [ 2, 4 .. batchCount - 1 ] $ \b tbl_n ->
798-
pipelinedIteration output initialSize batchSize
816+
pipelinedIteration h output initialSize batchSize
799817
syncTblB2A syncTblA2B -- in, out
800818
syncRngB2A syncRngA2B -- in, out
801819
tbl_n b
802820

803821
threadB =
804822
forFoldM_ tbl_0 [ 1, 3 .. batchCount - 1 ] $ \b tbl_n ->
805-
pipelinedIteration output initialSize batchSize
823+
pipelinedIteration h output initialSize batchSize
806824
syncTblA2B syncTblB2A -- in, out
807825
syncRngA2B syncRngB2A -- in, out
808826
tbl_n b
@@ -863,6 +881,105 @@ batchOverlaps initialSize batchSize batchCount seed =
863881

864882
g0 = initGen initialSize batchSize batchCount seed
865883

884+
-------------------------------------------------------------------------------
885+
-- measure batch latency
886+
-------------------------------------------------------------------------------
887+
888+
_mEASURE_BATCH_LATENCY :: Bool
889+
#ifdef MEASURE_BATCH_LATENCY
890+
_mEASURE_BATCH_LATENCY = True
891+
#else
892+
_mEASURE_BATCH_LATENCY = False
893+
#endif
894+
895+
type LatencyHandle = Handle
896+
897+
type TimeRef = IORef [Integer]
898+
899+
withLatencyHandle :: (LatencyHandle -> IO a) -> IO a
900+
withLatencyHandle k
901+
| _mEASURE_BATCH_LATENCY = withFile "latency.dat" WriteMode k
902+
| otherwise = k (error "LatencyHandle: do not use")
903+
904+
{-# INLINE hPutHeaderSequential #-}
905+
hPutHeaderSequential :: LatencyHandle -> IO ()
906+
hPutHeaderSequential h
907+
| _mEASURE_BATCH_LATENCY = do
908+
hPutStrLn h "# batch number \t lookup time (ns) \t update time (ns)"
909+
| otherwise = pure ()
910+
911+
{-# INLINE createGnuplotExampleFileSequential #-}
912+
createGnuplotExampleFileSequential :: IO ()
913+
createGnuplotExampleFileSequential
914+
| _mEASURE_BATCH_LATENCY = do
915+
withFile "latency.gp" WriteMode $ \h -> do
916+
mapM_ (hPutStrLn h) [
917+
"set title \"Latency (sequential)\""
918+
, ""
919+
, "set xlabel \"Batch number\""
920+
, ""
921+
, "set logscale y"
922+
, "set ylabel \"Time (nanoseconds)\""
923+
, ""
924+
, "plot \"latency.dat\" using 1:2 title 'lookups' axis x1y1, \\"
925+
, " \"latency.dat\" using 1:3 title 'updates' axis x1y1"
926+
]
927+
| otherwise = pure ()
928+
929+
{-# INLINE hPutHeaderPipelined #-}
930+
hPutHeaderPipelined :: LatencyHandle -> IO ()
931+
hPutHeaderPipelined h
932+
| _mEASURE_BATCH_LATENCY = do
933+
hPutStr h "# batch number"
934+
hPutStr h "\t lookup time (ns) \t sync receive time (ns) "
935+
hPutStrLn h "\t update time (ns) \t sync send time (ns)"
936+
| otherwise = pure ()
937+
938+
{-# INLINE createGnuplotExampleFilePipelined #-}
939+
createGnuplotExampleFilePipelined :: IO ()
940+
createGnuplotExampleFilePipelined
941+
| _mEASURE_BATCH_LATENCY =
942+
withFile "latency.gp" WriteMode $ \h -> do
943+
mapM_ (hPutStrLn h) [
944+
"set title \"Latency (pipelined)\""
945+
, ""
946+
, "set xlabel \"Batch number\""
947+
, ""
948+
, "set logscale y"
949+
, "set ylabel \"Time (nanoseconds)\""
950+
, ""
951+
, "plot \"latency.dat\" using 1:2 title 'lookups' axis x1y1, \\"
952+
, " \"latency.dat\" using 1:3 title 'sync receive' axis x1y1, \\"
953+
, " \"latency.dat\" using 1:4 title 'updates' axis x1y1, \\"
954+
, " \"latency.dat\" using 1:5 title 'sync send' axis x1y1"
955+
]
956+
| otherwise = pure ()
957+
958+
{-# INLINE withTimedBatch #-}
959+
withTimedBatch :: LatencyHandle -> Int -> (TimeRef -> IO a) -> IO a
960+
withTimedBatch h b k
961+
| _mEASURE_BATCH_LATENCY = do
962+
tref <- newIORef []
963+
x <- k tref
964+
ts <- readIORef tref
965+
let s = shows b
966+
. getDual (foldMap (\t -> Dual (showString "\t" <> shows t)) ts)
967+
hPutStrLn h (s "")
968+
pure x
969+
| otherwise = k (error "TimeRef: do not use")
970+
971+
{-# INLINE timeLatency #-}
972+
timeLatency :: TimeRef -> IO a -> IO a
973+
timeLatency tref k
974+
| _mEASURE_BATCH_LATENCY = do
975+
t1 <- Clock.getTime Clock.Monotonic
976+
x <- k
977+
t2 <- Clock.getTime Clock.Monotonic
978+
let !t = Clock.toNanoSecs (Clock.diffTimeSpec t2 t1)
979+
modifyIORef tref (t :)
980+
pure x
981+
| otherwise = k
982+
866983
-------------------------------------------------------------------------------
867984
-- main
868985
-------------------------------------------------------------------------------

lsm-tree.cabal

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,19 @@ library mcg
521521
, base
522522
, primes
523523

524+
flag measure-batch-latency
525+
description:
526+
Measure the latency for individual batches of updates and lookups
527+
528+
default: False
529+
manual: True
530+
531+
common measure-batch-latency
532+
if flag(measure-batch-latency)
533+
cpp-options: -DMEASURE_BATCH_LATENCY
534+
524535
benchmark lsm-tree-bench-wp8
525-
import: language, warnings, wno-x-partial
536+
import: language, warnings, wno-x-partial, measure-batch-latency
526537
type: exitcode-stdio-1.0
527538
hs-source-dirs: bench/macro
528539
main-is: lsm-tree-bench-wp8.hs

0 commit comments

Comments
 (0)