Skip to content

Commit 2824fce

Browse files
authored
Merge pull request #5883 from unisonweb/topic/thread-profile
2 parents 8daf2d2 + 986842e commit 2824fce

File tree

5 files changed

+172
-74
lines changed

5 files changed

+172
-74
lines changed

parser-typechecker/src/Unison/Codebase/Runtime/Profile.hs

Lines changed: 117 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,24 @@ import Unison.Util.Pretty as P
3737
newtype ProfTrie k a = ProfT (Map k (a, ProfTrie k a))
3838
deriving (Functor)
3939

40+
trimEmpty :: (Ord k, Eq a, Num a) => ProfTrie k a -> ProfTrie k a
41+
trimEmpty (ProfT m) = case M.traverseMaybeWithKey f m of
42+
Identity m -> ProfT m
43+
where
44+
f _ (a, trimEmpty -> sub@(ProfT m))
45+
| a == 0, null m = pure Nothing
46+
| otherwise = pure $ Just (a, sub)
47+
48+
demux ::
49+
(Ord k, Eq a, Eq b, Num a, Num b) =>
50+
ProfTrie k (a, b) ->
51+
(ProfTrie k a, ProfTrie k b)
52+
demux tr = (trimEmpty $ fst <$> tr, trimEmpty $ snd <$> tr)
53+
4054
-- A profile pairs the above arbitrary key based profile trie with a
4155
-- decoding of the integers to references and a total sample count.
42-
data Profile k = Prof !Int !(ProfTrie k Int) !(Map k Reference)
56+
data Profile k
57+
= Prof !(Int, Int) !(ProfTrie k (Int, Int)) !(Map k Reference)
4358

4459
-- Abstracts over the exact key type used in a profile.
4560
data SomeProfile = forall k. (Ord k) => SomeProf (Profile k)
@@ -48,28 +63,51 @@ data ProfileSpec = NoProf | MiniProf | FullProf String
4863
deriving (Eq, Ord, Show)
4964

5065
emptyProfile :: Profile k
51-
emptyProfile = Prof 0 (ProfT M.empty) M.empty
66+
emptyProfile = Prof zero (ProfT M.empty) M.empty
5267

53-
-- Creates a singleton profile trie from a path.
54-
singlePath :: (Ord k) => [k] -> (Int, ProfTrie k Int)
55-
singlePath [] = (1, ProfT M.empty)
56-
singlePath (i : is) = (0,) . ProfT $! M.singleton i (singlePath is)
68+
zero :: (Int, Int)
69+
zero = (0, 0)
70+
71+
inc :: Bool -> (Int, Int) -> (Int, Int)
72+
inc b (m, n) = pair (m + 1) (if b then n + 1 else n)
73+
where
74+
pair !x !y = (x, y)
5775

58-
addPath0 :: (Ord k) => [k] -> (Int, ProfTrie k Int) -> (Int, ProfTrie k Int)
59-
addPath0 [] (m, p) = (,p) $! m + 1
60-
addPath0 (i : is) (m, ProfT p) = (m,) . ProfT $! M.alter f i p
76+
-- Creates a singleton profile trie from a path.
77+
singlePath ::
78+
(Ord k) =>
79+
Bool ->
80+
[k] ->
81+
((Int, Int), ProfTrie k (Int, Int))
82+
singlePath b [] = (inc b zero, ProfT M.empty)
83+
singlePath b (i : is) =
84+
(zero,) . ProfT $! M.singleton i (singlePath b is)
85+
86+
addPath0 ::
87+
(Ord k) =>
88+
Bool ->
89+
[k] ->
90+
((Int, Int), ProfTrie k (Int, Int)) ->
91+
((Int, Int), ProfTrie k (Int, Int))
92+
addPath0 b [] (t, p) = (,p) $! inc b t
93+
addPath0 b (i : is) (m, ProfT p) = (m,) . ProfT $! M.alter f i p
6194
where
62-
f Nothing = Just $ singlePath is
63-
f (Just q) = Just $ addPath0 is q
95+
f Nothing = Just $ singlePath b is
96+
f (Just q) = Just $ addPath0 b is q
6497

6598
-- Adds a path to a profile trie, incrementing the count for the given
6699
-- path.
67-
addPath :: (Ord k) => [k] -> ProfTrie k Int -> ProfTrie k Int
68-
addPath [] p = p
69-
addPath (i : is) (ProfT m) = ProfT $ M.alter f i m
100+
addPath ::
101+
(Ord k) =>
102+
Bool ->
103+
[k] ->
104+
ProfTrie k (Int, Int) ->
105+
ProfTrie k (Int, Int)
106+
addPath _ [] p = p
107+
addPath b (i : is) (ProfT m) = ProfT $ M.alter f i m
70108
where
71-
f Nothing = Just $ singlePath is
72-
f (Just q) = Just $ addPath0 is q
109+
f Nothing = Just $ singlePath b is
110+
f (Just q) = Just $ addPath0 b is q
73111

74112
data AggInfo k = Ag
75113
{ -- inherited sample count
@@ -116,6 +154,7 @@ prune keep (ProfT m) = case M.traverseMaybeWithKey (prune0 keep) m of
116154
topN :: (Ord k) => Int -> Map k Int -> [(k, Int)]
117155
topN n0 = M.foldlWithKey (ins n0) []
118156
where
157+
ins _ pss _ 0 = pss
119158
ins 0 _ _ _ = []
120159
ins _ [] k i = [(k, i)]
121160
ins n pss@((k1, j) : ps) k0 i
@@ -193,9 +232,15 @@ dispProfEntry ppe misc refs (k, ks) (inh, self) =
193232
where
194233
ind = fromIntegral $ length ks
195234

196-
dispFunc :: PrettyPrintEnv -> Reference -> Pretty ColorText
197-
dispFunc ppe =
198-
syntaxToColor . prettyHashQualified . termName ppe . Ref
235+
dispFunc ::
236+
PrettyPrintEnv ->
237+
Map Reference (Pretty ColorText) ->
238+
Reference ->
239+
Pretty ColorText
240+
dispFunc ppe misc r
241+
| Just pr <- M.lookup r misc = pr
242+
| otherwise =
243+
syntaxToColor . prettyHashQualified . termName ppe $ Ref r
199244

200245
dispKey ::
201246
(Ord k) =>
@@ -205,9 +250,7 @@ dispKey ::
205250
k ->
206251
Pretty ColorText
207252
dispKey ppe misc refs k = case M.lookup k refs of
208-
Just r
209-
| Just pr <- M.lookup r misc -> pr
210-
| otherwise -> dispFunc ppe r
253+
Just r -> dispFunc ppe misc r
211254
Nothing -> "<unknown>"
212255

213256
dispProfTrie ::
@@ -223,10 +266,11 @@ dispProfTrie ppe misc refs ag =
223266
dispTopEntry ::
224267
(Ord k) =>
225268
PrettyPrintEnv ->
269+
Map Reference (Pretty ColorText) ->
226270
Map k Reference ->
227271
(k, Double) ->
228272
Pretty ColorText
229-
dispTopEntry ppe refs (k, frac) =
273+
dispTopEntry ppe misc refs (k, frac) =
230274
mconcat
231275
[ P.indentN 3 . fromString $ showPercent frac,
232276
P.indentN 4 dr,
@@ -235,16 +279,24 @@ dispTopEntry ppe refs (k, frac) =
235279
where
236280
dr :: Pretty ColorText
237281
dr
238-
| Just r <- M.lookup k refs = dispFunc ppe r
282+
| Just r <- M.lookup k refs = dispFunc ppe misc r
239283
| otherwise = "<unknown>"
240284

241285
dispTop ::
242286
(Ord k) =>
243287
PrettyPrintEnv ->
288+
Map Reference (Pretty ColorText) ->
244289
Map k Reference ->
245290
[(k, Double)] ->
246291
Pretty ColorText
247-
dispTop ppe refs = foldMap (dispTopEntry ppe refs)
292+
dispTop ppe misc refs = foldMap (dispTopEntry ppe misc refs)
293+
294+
overallHeader :: Pretty ColorText -> Int -> Pretty ColorText
295+
overallHeader label samps = label <> ": " <> dsamps <> newline
296+
where
297+
dsamps
298+
| samps == 1 = "1 sample"
299+
| otherwise = fromString (show samps) <> " samples"
248300

249301
profileTopHeader :: Pretty ColorText
250302
profileTopHeader =
@@ -264,38 +316,64 @@ miniProfile ::
264316
Map Reference (Pretty ColorText) ->
265317
Profile k ->
266318
Pretty ColorText
267-
miniProfile ppe misc (Prof total tr refs) =
268-
profileTreeHeader
269-
<> dispProfTrie ppe misc refs ag
319+
miniProfile ppe misc (Prof (total, wtotal) tr refs) =
320+
P.lines
321+
[ overallHeader "Complete Profile" total,
322+
profileTreeHeader <> dispProfTrie ppe misc refs ag,
323+
"",
324+
if wtotal > 0
325+
then
326+
overallHeader "Post-wakeup Profile" wtotal
327+
<> newline
328+
<> profileTreeHeader
329+
<> dispProfTrie ppe misc refs agw
330+
else "Threads never missed ticks"
331+
]
270332
where
271-
ag = aggregatePruned total tr
333+
(full, wait) = demux tr
334+
ag = aggregatePruned total full
335+
agw = aggregatePruned wtotal wait
272336

273337
fullProfile ::
274338
(Ord k) =>
275339
PrettyPrintEnv ->
276340
Map Reference (Pretty ColorText) ->
277341
Profile k ->
278-
Pretty ColorText
279-
fullProfile ppe misc (Prof total tr refs) =
280-
profileTopHeader
281-
<> dispTop ppe refs top
282-
<> "\n\n"
283-
<> profileTreeHeader
284-
<> dispProfTrie ppe misc refs ag
342+
(Pretty ColorText, Pretty ColorText)
343+
fullProfile ppe misc (Prof (total, wtotal) tr0 refs) =
344+
( make "Complete Profile" total comp,
345+
make "Post-wakeup Profile" wtotal wait
346+
)
285347
where
286-
(top, ag) = aggregate total tr
348+
(comp, wait) = demux tr0
349+
350+
make label tot tr =
351+
overallHeader label tot
352+
<> newline
353+
<> profileTopHeader
354+
<> dispTop ppe misc refs top
355+
<> newline
356+
<> newline
357+
<> profileTreeHeader
358+
<> dispProfTrie ppe misc refs ag
359+
where
360+
(top, ag) = aggregate tot tr
287361

288362
foldedProfile ::
289363
(Ord k) =>
290364
PrettyPrintEnv ->
291365
Map Reference (Pretty ColorText) ->
292366
Profile k ->
293-
String
367+
(String, String)
294368
foldedProfile ppe misc (Prof _ tr refs) =
295-
toPlain 0 $ foldMapTrie f tr
369+
( toPlain 0 $ foldMapTrie f comp,
370+
toPlain 0 $ foldMapTrie f wake
371+
)
296372
where
297373
dk = dispKey ppe misc refs
298374

375+
(comp, wake) = demux tr
376+
299377
f (k, ks) n =
300378
mconcat
301379
[ foldl (\tx k -> dk k <> ";" <> tx) (dk k) ks,

unison-runtime/src/Unison/Runtime/Interface.hs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,10 +545,14 @@ profileEval actThr cleanThr ctxVar cl ppe mout tm = do
545545
case mout of
546546
Just loc
547547
| ticky $ takeExtension loc -> do
548-
writeFile loc $ foldedProfile ppe fnames pout
548+
let (comp, wake) = foldedProfile ppe fnames pout
549+
writeFile loc comp
550+
writeFile (loc <.> "wakeup") wake
549551
pure $ Right (errs, tmr)
550552
| otherwise -> do
551-
writeFile loc . toPlain 0 $ fullProfile ppe fnames pout
553+
let (comp, wake) = fullProfile ppe fnames pout
554+
writeFile loc $ toPlain 0 comp
555+
writeFile (loc <.> "wakeup") $ toPlain 0 wake
552556
pure $ Right (errs, tmr)
553557
Nothing ->
554558
pure $ Right (errs <> Profile (miniProfile ppe fnames pout), tmr)

unison-runtime/src/Unison/Runtime/Machine.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,8 @@ eval' !yld env henv !activeThreads !stk !k r (RMatch i pu br) = do
568568
| otherwise -> unhandledAbilityRequest
569569
eval' !yld env henv !activeThreads !stk !k here (Yield args)
570570
| asize stk > 0,
571-
VArg1 i <- args =
571+
VArg1 i <- args = do
572+
checkTicker yld here k
572573
peekOff stk i >>= apply yld env henv activeThreads stk k False ZArgs
573574
| otherwise = do
574575
checkTicker yld here k

unison-runtime/src/Unison/Runtime/Machine/Types.hs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
module Unison.Runtime.Machine.Types where
44

5-
#if !defined(mingw32_HOST_OS)
6-
import Control.Concurrent
7-
(ThreadId, MVar, newEmptyMVar, tryPutMVar, tryTakeMVar)
8-
#else
95
import Control.Concurrent (ThreadId)
10-
#endif
11-
126
import Control.Concurrent.STM as STM
137
import Control.Exception hiding (Handler)
14-
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
8+
#if !defined(mingw32_HOST_OS)
9+
import Data.IORef
10+
(IORef, newIORef, readIORef, writeIORef, atomicModifyIORef)
11+
#else
12+
import Data.IORef
13+
(IORef, newIORef, readIORef, writeIORef)
14+
#endif
1515
import Data.Kind (Type)
1616
import Data.Map.Strict qualified as M
1717
import Data.Set qualified as S
@@ -103,19 +103,20 @@ type Tick = CombIx -> K -> IO ()
103103
#if !defined(mingw32_HOST_OS)
104104
-- GHC.Event, time-baed profiler
105105
instance RuntimeProfiler ProfileComm where
106-
newtype Ticker ProfileComm = ProfTicker (MVar Tick)
106+
newtype Ticker ProfileComm = ProfTicker (IORef (Maybe Tick))
107107

108108
startTicker (PC pf _ _) = do
109-
ticker <- newEmptyMVar
109+
ticker <- newIORef Nothing
110110
cancel <- newIORef False
111111
tm <- getSystemTimerManager
112112
void . registerTimeout tm 100 $
113113
tickCallback 100 pf ticker cancel
114114
pure (ProfTicker ticker, writeIORef cancel True)
115115

116-
checkTicker (ProfTicker tick) cix k = tryTakeMVar tick >>= \case
117-
Nothing -> pure ()
118-
Just pf -> pf cix k
116+
checkTicker (ProfTicker ticker) cix k =
117+
atomicModifyIORef ticker (Nothing,) >>= \case
118+
Nothing -> pure ()
119+
Just pf -> pf cix k
119120
{-# INLINE checkTicker #-}
120121

121122
-- Callback for producing ticks via event manager timeouts. These happen
@@ -126,27 +127,36 @@ instance RuntimeProfiler ProfileComm where
126127
--
127128
-- The callback doesn't block trying to write to the MVar, so if something
128129
-- is already there, a second tick just won't happen.
129-
tickCallback :: Int -> Tick -> MVar Tick -> IORef Bool -> IO ()
130-
tickCallback interval tick ticker cancel = body
130+
tickCallback ::
131+
Int ->
132+
(Bool -> Tick) ->
133+
IORef (Maybe Tick) ->
134+
IORef Bool ->
135+
IO ()
136+
tickCallback interval ptick ticker cancel = body
131137
where
132138
body = do
133-
tryPutMVar ticker tick
139+
_full <- atomicModifyIORef ticker \(isJust -> b) ->
140+
(Just $ ptick b, b)
134141
b <- readIORef cancel
135142
when (not b) do
136143
tm <- getSystemTimerManager
137144
() <$ registerTimeout tm interval body
145+
138146
#else
147+
139148
-- CPUTime based profiler for Windows
140149
instance RuntimeProfiler ProfileComm where
141150
data Ticker ProfileComm = TPC !Tick !(IORef Word8)
142-
startTicker (PC pf _ _) = (, pure ()) . TPC pf <$> newIORef 1
151+
startTicker (PC pf _ _) = (, pure ()) . TPC (pf False) <$> newIORef 1
143152

144153
checkTicker (TPC tick r) cix k = do
145154
n <- readIORef r
146155
when (n `mod` 128 == 0) do
147156
n <- getCPUTime
148157
when (n `mod` 100000 == 0) $ tick cix k
149158
writeIORef r (n+1)
159+
150160
#endif
151161

152162
-- code caching environment

0 commit comments

Comments
 (0)