Hello *, I've been experimenting with an alternative implementation of 'System.Timeout.timeout'[1] which avoids the overhead of spawning a new thread for each invocation.
Part of my motivation is to see if I can implement a faster version of threadWaitReadTimeout :: Int -> Fd -> IO Bool threadWaitReadTimeout to = liftM (maybe False (const True)) . timeout to . threadWaitRead and thus exploit GHC's event notification system instead of having to reimplement a timeout-manager myself (like popular HTTP server libraries such as Snap or Warp do currently). The following Haskell program shows a proof-of-concept implementation derived directly from 'System.Timeout.timeout' together with a Criterion benchmark comparing the performance between the original and the alternative 'timeout' function wrapping a 'readMVar' call.
{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-} -- ghc -threaded --make -O2 -rtsopts -Wall timeout2.hs && ./timeout2 +RTS -A4m -N4 import Control.Concurrent import Control.Exception import Criterion.Main import Data.Typeable import Data.Unique (Unique, newUnique) import qualified GHC.Event as E import System.Timeout (timeout) newtype Timeout2 = Timeout2 Unique deriving (Eq,Typeable) instance Exception Timeout2 instance Show Timeout2 where show _ = "<<timeout2>>" -- | Alternative implementation of 'System.Timeout.timeout' using -- 'GHC.Event.registerTimeout' directly instead of spawning a -- watchdog-thread. timeout2 :: Int -> IO a -> IO (Maybe a) timeout2 to f | to < 0 = fmap Just f | to == 0 = return Nothing | otherwise = do tid <- myThreadId ex <- fmap Timeout2 newUnique Just em <- E.getSystemEventManager -- FIXME handleJust (\e -> if e == ex then Just () else Nothing) (\_ -> return Nothing) (bracket (E.registerTimeout em to (throwTo tid ex)) (E.unregisterTimeout em) (\_ -> fmap Just f)) main :: IO () main = do mv <- newMVar () defaultMain [ bench "id" $ readMVar mv , bench "timeout_1ms" $ readMVarTO 1000 mv , bench "timeout2_1ms" $ readMVarTO2 1000 mv , bench "timeout_100us" $ readMVarTO 100 mv , bench "timeout2_100us" $ readMVarTO2 100 mv , bench "timeout_10us" $ readMVarTO 10 mv , bench "timeout2_10us" $ readMVarTO2 10 mv , bench "timeout_1us" $ readMVarTO 1 mv , bench "timeout2_1us" $ readMVarTO2 1 mv ] where readMVarTO to = timeout to . readMVar readMVarTO2 to = timeout2 to . readMVar
On a i7-3770 with GHC-7.6.2/Linux/64bit ran with "+RTS -A4m -N4", the benchmark shows a 15x improvement for the new implementation (below 1 uS) compared to the original implementation (~13 uS): ,---- | benchmarking id | mean: 22.60933 ns, lb 22.50331 ns, ub 22.73515 ns, ci 0.950 | std dev: 591.0383 ps, lb 509.6189 ps, ub 663.2670 ps, ci 0.950 | found 17 outliers among 100 samples (17.0%) | 17 (17.0%) high mild | variance introduced by outliers: 19.992% | variance is moderately inflated by outliers | | benchmarking timeout_1ms | mean: 13.79584 us, lb 13.62939 us, ub 13.92814 us, ci 0.950 | std dev: 756.3080 ns, lb 524.7628 ns, ub 1.068547 us, ci 0.950 | found 14 outliers among 100 samples (14.0%) | 4 (4.0%) low severe | 5 (5.0%) high mild | 5 (5.0%) high severe | variance introduced by outliers: 52.484% | variance is severely inflated by outliers | | benchmarking timeout2_1ms | mean: 879.8152 ns, lb 874.5223 ns, ub 885.9759 ns, ci 0.950 | std dev: 29.31963 ns, lb 25.65941 ns, ub 32.98116 ns, ci 0.950 | found 9 outliers among 100 samples (9.0%) | 9 (9.0%) high mild | variance introduced by outliers: 28.734% | variance is moderately inflated by outliers | ... `---- Alas there's a race-condition hidden somewhere I'm struggling with; When the timeout is set low enough, the internal 'Timeout2' exceptions leaks outside the 'timeout2' wrapper: ,---- | ... | benchmarking timeout2_10us | newtimeout: <<timeout2>> `---- I've tried rewriting the code but couldn't figure out a way to keep the exception from escaping 'timeout2'. Does the race-condition actually lie in the 'timeout2' implementation -- and if so, is it possible to rewrite 'timeout2' to solve it? [1]: http://hackage.haskell.org/packages/archive/base/latest/doc/html/System-Timeout.html#v:timeout cheers, hvr
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users