@@ -41,17 +41,18 @@ import Control.Concurrent (getNumCapabilities)
4141import Control.Concurrent.Async
4242import Control.Concurrent.MVar
4343import Control.DeepSeq (force )
44- import Control.Exception ( evaluate )
44+ import Control.Exception
4545import Control.Monad (forM_ , unless , void , when )
4646import Control.Monad.Trans.State.Strict (runState , state )
4747import Control.Tracer
4848import qualified Data.ByteString.Short as BS
4949import qualified Data.Foldable as Fold
5050import qualified Data.IntSet as IS
51- import Data.IORef ( modifyIORef' , newIORef , readIORef , writeIORef )
51+ import Data.IORef
5252import qualified Data.List.NonEmpty as NE
5353import Data.Map.Strict (Map )
5454import qualified Data.Map.Strict as Map
55+ import Data.Monoid
5556import qualified Data.Primitive as P
5657import qualified Data.Vector as V
5758import 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
612614type 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
7207284. 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-------------------------------------------------------------------------------
0 commit comments