9898 cmdStats: Table [string , CmdStats ]
9999 heaviest: seq [tuple [kib: int , cmdName, label: string ]]
100100 # # the `HeaviestShown` hungriest nodes, descending
101+ spans: seq [tuple [start, finish: int64 , kib: int ]]
102+ # # lifetime (monotonic nanoseconds) and peak RSS of every executed node;
103+ # # `concurrentPeak` sweeps these for the build's memory high-water mark
101104 execWallTime: float
102105
103106proc addSpace (result: var string ) {.inline .} =
@@ -367,15 +370,16 @@ proc toSeconds(d: Duration): float =
367370const HeaviestShown = 10
368371
369372proc recordCmd (profile: var ProfileData ; cmdName, label: string ;
370- sec: float ; peakKiB: int ) =
373+ start, finish: MonoTime ; peakKiB: int ) =
371374 let e = addr profile.cmdStats.mgetOrPut (cmdName, CmdStats ())
372- e.sec += sec
375+ e.sec += toSeconds (finish - start)
373376 inc e.count
374377 e.sumKiB += peakKiB
375378 if peakKiB > e.peakKiB:
376379 e.peakKiB = peakKiB
377380 e.peakLabel = label
378381 if peakKiB > 0 :
382+ profile.spans.add (start.ticks, finish.ticks, peakKiB)
379383 var i = profile.heaviest.len
380384 while i > 0 and profile.heaviest[i- 1 ].kib < peakKiB: dec i
381385 if i < HeaviestShown :
@@ -641,8 +645,8 @@ proc runDag(dag: var Dag; opt: set[CliOption]; profile: ptr ProfileData = nil;
641645 pool.del k
642646 close job.process
643647 if profile != nil :
644- profile[].recordCmd (job.cmdName, job.label,
645- toSeconds ( getMonoTime () - job.start), peakKiB)
648+ profile[].recordCmd (job.cmdName, job.label, job.start, getMonoTime (),
649+ peakKiB)
646650 inc prog.done
647651 prog.draw job.label
648652 if exitCode == 0 :
@@ -912,6 +916,30 @@ proc printReport(profile: ProfileData) =
912916 stdout.write $ total
913917 stdout.write " \n "
914918
919+ proc concurrentPeak (profile: ProfileData ): tuple [kib, jobs: int ] =
920+ # # Upper bound on the whole build's memory high-water mark: sweeps the node
921+ # # lifetimes and sums the peak RSS of everything alive at the same instant.
922+ # # Pessimistic on purpose -- a process only sits at its peak briefly, but the
923+ # # peaks are all `wait4` hands back, so this is the cheapest honest bound.
924+ # # Note that the `peak` column above is a single process, which is why this
925+ # # number can be much larger with `--parallel`.
926+ result = (0 , 0 )
927+ var events = newSeqOfCap [(int64 , int )](profile.spans.len * 2 )
928+ for s in profile.spans:
929+ events.add (s.start, s.kib)
930+ events.add (s.finish, - s.kib)
931+ # Ties must release before they acquire, otherwise a node that exits exactly
932+ # as its successor starts is counted twice.
933+ events.sort (proc (a, b: (int64 , int )): int =
934+ result = cmp (a[0 ], b[0 ])
935+ if result == 0 : result = cmp (a[1 ], b[1 ]))
936+ var cur = 0
937+ var live = 0
938+ for (_, delta) in events:
939+ cur += delta
940+ if delta > 0 : inc live else : dec live
941+ if cur > result .kib: result = (cur, live)
942+
915943proc fmtKiB (kib: int ): string =
916944 if kib >= 1024 * 1024 : formatFloat (kib / (1024 * 1024 ), ffDecimal, 1 ) & " GiB"
917945 else : $ (kib div 1024 ) & " MiB"
@@ -935,6 +963,10 @@ proc printProfile(profile: ProfileData) =
935963 let execTotal = profile.cmdStats.values.toSeq.foldl (a + b.sec, 0.0 )
936964 stderr.writeLine " exec total: " , execTotal.formatFloat (ffDecimal, 3 ), " s"
937965 stderr.writeLine " wall time: " , profile.execWallTime.formatFloat (ffDecimal, 3 ), " s"
966+ let cp = concurrentPeak (profile)
967+ if cp.kib > 0 :
968+ stderr.writeLine " concurrent RSS: " , fmtKiB (cp.kib),
969+ " (upper bound, " , $ cp.jobs, " jobs live)"
938970 if profile.heaviest.len > 0 :
939971 stderr.writeLine " heaviest nodes (peak RSS):"
940972 for h in profile.heaviest:
0 commit comments