{-# LANGUAGE BangPatterns #-} module Main where import Control.Concurrent import Foreign.C import GHC.Clock import GHC.Event import System.CPUTime import System.Posix.Types import Control.Monad import System.Exit foreign import ccall unsafe "socket" c_socket :: CInt -> CInt -> CInt -> IO CInt makeTestSocketFd :: IO Fd makeTestSocketFd = do sockNum <- c_socket 1 -- PF_LOCAL 2 -- SOCK_DGRAM 0 return $ (fromIntegral sockNum :: Fd) callback :: FdKey -> Event -> IO () callback _ _ = return () -- Idle CPU usage with 0 for 0% and 10^12 for 100% idleCpuUsage :: IO Integer idleCpuUsage = do -- measure the process time spent in the rts, not in the mutator -- make sure to disable idle GC (+RTS -I0) !startCPUTime <- getCPUTime threadDelay 100000 !endCPUTime <- getCPUTime let !t = endCPUTime - startCPUTime return $ t main :: IO () main = do (Just eventMgr) <- getSystemEventManager fd <- makeTestSocketFd let getAvgCpuUsage = do let n = 10 let warmup = 2 xs <- drop warmup <$> replicateM (warmup+n) idleCpuUsage return $! fromIntegral (sum xs) / fromIntegral n !before <- getAvgCpuUsage registerFd eventMgr callback fd evtRead OneShot registerFd eventMgr callback fd evtWrite OneShot -- use this to test that this test works --forkIO $ forever $ do -- putStrLn "" -- threadDelay 10000 !after <- getAvgCpuUsage -- CPU consumption should roughly be the same when just idling vs -- when idling after the event has been triggered let r = (after-before) / before * 100 let max_percent = 100 -- max difference (in percent) when (abs r > max_percent) $ do putStrLn $ mconcat [ "Idle CPU consumption too different after event registration: " , if r > 0 then "+" else "" , show (round r) , "% (> +/- " , show (round max_percent) , "%)\n" , "Before: " , show (round before `div` 1000000 :: Integer) , "ms\n" , "After: " , show (round after `div` 1000000 :: Integer) , "ms" ] exitFailure