diff options
author | James Crayne <jim.crayne@gmail.com> | 2020-01-23 19:53:41 -0500 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2020-01-24 03:01:27 -0500 |
commit | 5f2252d21c9996fe6b23654e53c613817ae3b292 (patch) | |
tree | b3daf6e99c3de77aa1f8297efd7b5caa9a4d4d71 | |
parent | fead89918f61272998e968397ad51de097d197ea (diff) |
thread instrumentation: contention reduction experiment
-rw-r--r-- | lifted-concurrent/lifted-concurrent.cabal | 1 | ||||
-rw-r--r-- | lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs | 52 |
2 files changed, 33 insertions, 20 deletions
diff --git a/lifted-concurrent/lifted-concurrent.cabal b/lifted-concurrent/lifted-concurrent.cabal index 26e9df68..78a027db 100644 --- a/lifted-concurrent/lifted-concurrent.cabal +++ b/lifted-concurrent/lifted-concurrent.cabal | |||
@@ -25,6 +25,7 @@ library | |||
25 | other-extensions: FlexibleContexts | 25 | other-extensions: FlexibleContexts |
26 | build-depends: | 26 | build-depends: |
27 | base | 27 | base |
28 | , vector | ||
28 | , containers | 29 | , containers |
29 | , time | 30 | , time |
30 | , lifted-async | 31 | , lifted-async |
diff --git a/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs b/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs index bd6ee4b8..b4a67583 100644 --- a/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs +++ b/lifted-concurrent/src/Control/Concurrent/Lifted/Instrument.hs | |||
@@ -27,7 +27,9 @@ import Data.Time() | |||
27 | import Data.Time.Clock | 27 | import Data.Time.Clock |
28 | import DPut | 28 | import DPut |
29 | import DebugTag | 29 | import DebugTag |
30 | 30 | import qualified Data.Vector as V | |
31 | import Data.Vector (Vector) | ||
32 | import Data.Char | ||
31 | 33 | ||
32 | data PerThread = PerThread | 34 | data PerThread = PerThread |
33 | { lbl :: String | 35 | { lbl :: String |
@@ -35,15 +37,25 @@ data PerThread = PerThread | |||
35 | } | 37 | } |
36 | deriving (Eq,Ord,Show) | 38 | deriving (Eq,Ord,Show) |
37 | 39 | ||
40 | {-# NOINLINE globalMVarArray #-} | ||
41 | globalMVarArray :: Vector (MVar (Map.Map ThreadId PerThread)) | ||
42 | globalMVarArray = unsafePerformIO (sequence (V.replicate 256 (newMVar Map.empty))) | ||
43 | |||
38 | data GlobalState = GlobalState | 44 | data GlobalState = GlobalState |
39 | { threads :: !(Map.Map ThreadId PerThread) | 45 | { reportException :: String -> IO () |
40 | , reportException :: String -> IO () | ||
41 | } | 46 | } |
42 | 47 | ||
48 | hashThreadId :: ThreadId -> Int | ||
49 | hashThreadId tid = hash 0 (dropThreadIdAndSPace (show tid)) | ||
50 | where | ||
51 | dropThreadIdAndSPace ('T':'h':'r':'e':'a':'d':'I':'d':' ':xs) = xs | ||
52 | dropThreadIdAndSPace xs = xs | ||
53 | hash n xs = read xs `mod` 256 | ||
54 | |||
55 | |||
43 | globals :: MVar GlobalState | 56 | globals :: MVar GlobalState |
44 | globals = unsafePerformIO $ newMVar $ GlobalState | 57 | globals = unsafePerformIO $ newMVar $ GlobalState |
45 | { threads = Map.empty | 58 | { reportException = dput XMisc |
46 | , reportException = dput XMisc | ||
47 | } | 59 | } |
48 | {-# NOINLINE globals #-} | 60 | {-# NOINLINE globals #-} |
49 | 61 | ||
@@ -92,24 +104,25 @@ instrumented rawFork action = do | |||
92 | t <- mask_ $ rawFork $ \unmask -> do | 104 | t <- mask_ $ rawFork $ \unmask -> do |
93 | tid <- myThreadId | 105 | tid <- myThreadId |
94 | let scrapIt = do takeMVar mvar | 106 | let scrapIt = do takeMVar mvar |
95 | modifyThreads $! Map.delete tid | 107 | modifyThreads tid $! Map.delete tid |
96 | io <- catch (unmask action >> return scrapIt) $ \e -> case fromException e of | 108 | io <- catch (unmask action >> return scrapIt) $ \e -> case fromException e of |
97 | Just ThreadKilled -> return scrapIt | 109 | Just ThreadKilled -> return scrapIt |
98 | Nothing -> liftBase $ do | 110 | Nothing -> liftBase $ do |
99 | g <- takeMVar globals | 111 | g <- readMVar globals |
112 | mp <- readMVar (globalMVarArray V.! hashThreadId tid) | ||
100 | let l = concat [ show e | 113 | let l = concat [ show e |
101 | , " (" | 114 | , " (" |
102 | , maybe "" lbl $ Map.lookup tid (threads g) | 115 | , maybe "" lbl $ Map.lookup tid mp |
103 | , ")" | 116 | , ")" |
104 | ] | 117 | ] |
105 | reportException g l | 118 | reportException g l |
106 | let l = concat [ show e | 119 | let l = concat [ show e |
107 | , " (" | 120 | , " (" |
108 | , maybe "" lbl $ Map.lookup tid (threads g) | 121 | , maybe "" lbl $ Map.lookup tid mp |
109 | , ")" | 122 | , ")" |
110 | ] | 123 | ] |
111 | foldr seq (return ()) l | 124 | foldr seq (return ()) l |
112 | putMVar globals $! g { threads = Map.insert tid (PerThread l tm) $ threads g } | 125 | modifyThreads tid $! Map.insert tid (PerThread l tm) |
113 | return $ return () -- Remove the thread only if it terminated normally or was killed. | 126 | return $ return () -- Remove the thread only if it terminated normally or was killed. |
114 | io -- scrap record on normal termination | 127 | io -- scrap record on normal termination |
115 | liftBase $ labelThread_ t (defaultLabel callStack) tm | 128 | liftBase $ labelThread_ t (defaultLabel callStack) tm |
@@ -122,25 +135,24 @@ labelThread_ tid s tm = do | |||
122 | GHC.labelThread tid s | 135 | GHC.labelThread tid s |
123 | let updateIt (Just pt) = Just $ pt { lbl = s } | 136 | let updateIt (Just pt) = Just $ pt { lbl = s } |
124 | updateIt Nothing = Just $ PerThread s tm | 137 | updateIt Nothing = Just $ PerThread s tm |
125 | modifyThreads $! Map.alter updateIt tid | 138 | modifyThreads tid $! Map.alter updateIt tid |
126 | 139 | ||
127 | labelThread :: ThreadId -> String -> IO () | 140 | labelThread :: ThreadId -> String -> IO () |
128 | labelThread tid s = do | 141 | labelThread tid s = do |
129 | foldr seq (return ()) s | 142 | foldr seq (return ()) s |
130 | GHC.labelThread tid s | 143 | GHC.labelThread tid s |
131 | modifyThreads $! Map.adjust (\pt -> pt { lbl = s }) tid | 144 | modifyThreads tid $! Map.adjust (\pt -> pt { lbl = s }) tid |
132 | {-# INLINE labelThread #-} | 145 | {-# INLINE labelThread #-} |
133 | 146 | ||
134 | threadsInformation :: IO [(ThreadId,PerThread)] | 147 | threadsInformation :: IO [(ThreadId,PerThread)] |
135 | threadsInformation = do | 148 | threadsInformation = do |
136 | m <- threads <$> readMVar globals | 149 | ms <- mapM readMVar (V.toList globalMVarArray) |
137 | return $ Map.toList m | 150 | return $ Prelude.concatMap Map.toList ms |
138 | 151 | ||
139 | 152 | ||
140 | modifyThreads :: MonadBaseControl IO m => | 153 | modifyThreads :: MonadBaseControl IO m => ThreadId -> |
141 | (Map.Map ThreadId PerThread -> Map.Map ThreadId PerThread) -> m () | 154 | (Map.Map ThreadId PerThread -> Map.Map ThreadId PerThread) -> m () |
142 | modifyThreads f = do | 155 | modifyThreads tid f = do |
143 | let f' st = st { threads = f (threads st) } | 156 | let tidHash = hashThreadId tid |
144 | bracket (takeMVar globals) | 157 | let mvar = globalMVarArray V.! tidHash |
145 | (\g -> putMVar globals $! f' g) | 158 | modifyMVarMasked_ mvar (return . f) |
146 | (\g -> return ()) | ||