inline-asm-0.5.0.0: examples/Rdtsc.hs
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
{-# LANGUAGE GHCForeignImportPrim, UnliftedFFITypes, UnboxedTuples #-}
{-# LANGUAGE LambdaCase, BlockArguments, NumericUnderscores #-}
module Main where
import Control.Monad
import Data.List
import Data.String.Interpolate
import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Cairo
import GHC.Word
import System.Environment
import Language.Asm.Inline
import Language.Asm.Inline.QQ
defineAsmFunM "rdtsc"
[asmTy| | (out : Word64) |]
[asm|
lfence
rdtsc
mov %rdx, {out}
shl $32, {out}
add %rax, {out}
lfence
|]
defineAsmFunM "rdtscP"
[asmTy| | (hi : Word32) (lo : Word32) |]
[asm|
rdtsc
mov %rdx, {hi}
mov %rax, {lo}
|]
defineAsmFunM "rdtsc2"
[asmTy| | (out1 : Word64) (out2 : Word64) |]
[asm|
lfence
rdtsc
mov %rdx, {out1}
shl $32, {out1}
add %rax, {out1}
lfence
lfence
rdtsc
mov %rdx, {out2}
shl $32, {out2}
add %rax, {out2}
lfence
|]
example :: IO ()
example = do
v1 <- rdtsc
v2 <- rdtsc
print v1
print v2
print $ v2 - v1
p1 <- rdtscP
p2 <- rdtscP
print p1
print p2
(o1, o2) <- rdtsc2
print $ o2 - o1
main :: IO ()
main = getArgs >>= \case [] -> example
["bench"] -> bench
_ -> putStrLn "Unknown args"
---- Benchmarking stuff
bench :: IO ()
bench = do
baselineMeas <- removeOutliers <$> replicateM count do
(v1, v2) <- rdtsc2
pure $ v2 - v1
asmMeas <- removeOutliers <$> replicateM count do
v1 <- rdtsc
v2 <- rdtsc
pure $ v2 - v1
cMeas <- removeOutliers <$> replicateM count do
v1 <- rdtscC
v2 <- rdtscC
pure $ v2 - v1
printPlot [("only asm", baselineMeas), ("inline-asm", asmMeas), ("c", cMeas)]
where
count = 1_000_000 :: Int
foreign import ccall unsafe "rdtscC"
rdtscC :: IO Word64
printPlot :: [(String, [Word64])] -> IO ()
printPlot allStats = do
forM_ allStats $ uncurry printStats
toFile def "out.svg" $ do
layout_title .= "rdtsc diff time"
mapM_ (plot . uncurry histPlot) allStats
where
minVal = fromIntegral $ minimum $ concat $ snd <$> allStats
maxVal = fromIntegral $ maximum $ concat $ snd <$> allStats
histPlot :: String -> [Word64] -> EC l (Plot Double Int)
histPlot name vals = do
color <- takeColor
pure $ histToPlot $ plot_hist_title .~ name
$ plot_hist_values .~ (fromIntegral <$> vals)
$ plot_hist_fill_style.fill_color .~ dissolve 0.2 color
$ plot_hist_line_style.line_color .~ color
$ plot_hist_range ?~ (minVal, maxVal)
$ defaultPlotHist
printStats :: String -> [Word64] -> IO ()
printStats name allRuns = putStrLn [i|#{name}:\n min: #{minimum runs}; max: #{maximum runs}; avg: #{avg}|]
where
runs = removeOutliers allRuns
avg = sum runs `div` genericLength runs
removeOutliers :: [Word64] -> [Word64]
removeOutliers allRuns = drop cutoff $ take (runsLen - cutoff) $ sort allRuns
where
runsLen = genericLength allRuns
cutoff = runsLen `div` 10000