@@ -188,7 +188,15 @@ module m_phase_timing
188188 integer(8) :: ncall(PH_N) = 0
189189 integer(8) :: tic_c(PH_N) = 0
190190 integer :: depth(PH_N) = 0
191- real(wp) :: t_wall0 = -1._wp
191+ !> Observed nesting, so the budget validates itself instead of trusting a hand-kept list of top-level rows. open_ids is the
192+ !! stack of brackets open on this rank (each id at most once, by the depth guard, so PH_N bounds it); tier_lo/tier_hi are the
193+ !! shallowest and deepest depth each phase was ever opened at. A phase with tier_lo /= tier_hi is opened both inside and outside
194+ !! another bracket - a shared routine - and cannot be summed at either level. n_interleave counts a toc that was not the
195+ !! innermost open bracket, n_orphan a toc with no tic: either one means some row double counts.
196+ integer :: open_ids(PH_N) = 0, n_open = 0
197+ integer :: tier_lo(PH_N) = huge(1), tier_hi(PH_N) = 0
198+ integer :: n_interleave = 0, n_orphan = 0
199+ real(wp) :: t_wall0 = -1._wp
192200
193201contains
194202
@@ -201,6 +209,8 @@ contains
201209 if (.not. rank_time_wrt) return
202210 depth(id) = depth(id) + 1
203211 if (depth(id) > 1) return ! outermost bracket only, so nesting cannot double count
212+ n_open = n_open + 1; open_ids(n_open) = id
213+ tier_lo(id) = min(tier_lo(id), n_open); tier_hi(id) = max(tier_hi(id), n_open)
204214 do i = 1, 3
205215 if (id == SR_PH(i)) then; sr_t0(i) = mpi_sr_wait; sr_n0(i) = mpi_sr_calls; end if
206216 end do
@@ -219,9 +229,15 @@ contains
219229 integer :: i
220230
221231 if (.not. rank_time_wrt) return
222- if (depth(id) <= 0) then; depth(id) = 0; return; end if
232+ if (depth(id) <= 0) then; depth(id) = 0; n_orphan = n_orphan + 1; return; end if
223233 depth(id) = depth(id) - 1
224234 if (depth(id) > 0) return
235+ if (open_ids(n_open) /= id) n_interleave = n_interleave + 1
236+ do i = n_open, 1, -1
237+ if (open_ids(i) == id) then
238+ open_ids(i:n_open - 1) = open_ids(i + 1:n_open); n_open = n_open - 1; exit
239+ end if
240+ end do
225241 $:GPU_WAIT()
226242 call system_clock(c, rate)
227243 acc(id) = acc(id) + real(c - tic_c(id), wp)/real(rate, wp)
@@ -259,7 +275,9 @@ contains
259275 impure subroutine s_phase_report (wall )
260276
261277 real (wp), intent (in ) :: wall
262- real (wp) :: tot, gmax(PH_N), gsum(PH_N)
278+ real (wp) :: tot, gmax(PH_N), gsum(PH_N), t1sum
279+ integer :: gtlo(PH_N), gthi(PH_N), gbad(2 ), nbad(2 )
280+ character (len= 8 ) :: tl
263281 integer (8 ) :: gcall(PH_N)
264282 integer :: i, ierr, ip
265283 !> Per- rank times for the phases whose IMBALANCE moves with simulation time. mean/ max cannot say WHICH rank is slow or
@@ -280,8 +298,13 @@ contains
280298 call MPI_ALLREDUCE(acc, gmax, PH_N, mpi_p, MPI_MAX, MPI_COMM_WORLD, ierr)
281299 call MPI_ALLREDUCE(acc, gsum, PH_N, mpi_p, MPI_SUM, MPI_COMM_WORLD, ierr)
282300 call MPI_ALLREDUCE(ncall, gcall, PH_N, MPI_INTEGER8 , MPI_SUM, MPI_COMM_WORLD, ierr)
301+ call MPI_ALLREDUCE(tier_lo, gtlo, PH_N, MPI_INTEGER, MPI_MIN, MPI_COMM_WORLD, ierr)
302+ call MPI_ALLREDUCE(tier_hi, gthi, PH_N, MPI_INTEGER, MPI_MAX, MPI_COMM_WORLD, ierr)
303+ nbad = [n_interleave, n_orphan]
304+ call MPI_ALLREDUCE(nbad, gbad, 2 , MPI_INTEGER, MPI_SUM, MPI_COMM_WORLD, ierr)
283305#else
284306 gmax = acc; gsum = acc* real (num_procs, wp); gcall = ncall* int (num_procs, 8 )
307+ gtlo = tier_lo; gthi = tier_hi; gbad = [n_interleave, n_orphan]
285308#endif
286309 allocate (prank(0 :num_procs - 1 ,NPR))
287310 do i = 1 , NPR
@@ -337,16 +360,37 @@ contains
337360 if (proc_rank /= 0 ) return
338361 print ' (A)' , ' [phase] PHASE BUDGET'
339362 print ' (A,F10.3,A)' , ' [phase] step-loop wall = ' , wall, ' s'
340- print ' (A)' , ' [phase] name mean s max s % wall imbalance calls/rank ms/call'
363+ print ' (A)' , ' [phase] name mean s max s % wall imbalance calls/rank ms/call tier '
341364 do i = 1 , PH_N
342365 if (gsum(i) <= 0._wp ) cycle
343- print ' (A,A8,F10.3,F9.3,F9.1,A,F8.3,I12,F11.4)' , ' [phase] ' , PH_NAME(i), gsum(i)/ real (num_procs, wp), gmax(i), &
366+ if (gtlo(i) == gthi(i)) then
367+ write (tl, ' (A,I0)' ) ' T' , gtlo(i)
368+ else
369+ write (tl, ' (A,I0,A,I0)' ) ' T' , gtlo(i), ' -' , gthi(i)
370+ end if
371+ print ' (A,A8,F10.3,F9.3,F9.1,A,F8.3,I12,F11.4,2X,A)' , ' [phase] ' , PH_NAME(i), gsum(i)/ real (num_procs, wp), gmax(i), &
344372 & 100._wp * (gsum(i)/ real (num_procs, wp))/ wall, ' %' , gmax(i)/ max (gsum(i)/ real (num_procs, wp), tiny (1._wp )), &
345373 & gcall(i)/ int (num_procs, 8 ), 1000._wp * (gsum(i)/ real (num_procs, wp))/ max (real (gcall(i)/ int (num_procs, 8 ), wp), &
346- & 1._wp )
374+ & 1._wp ), trim (tl)
347375 end do
348- print ' (A,F10.3,F19.1,A)' , ' [phase] RESIDUAL' , wall - sum (gsum)/ real (num_procs, wp), &
349- & 100._wp * (wall - sum (gsum)/ real (num_procs, wp))/ wall, ' %'
376+ ! Only rows that were top- level on EVERY rank sum against wall; summing nested rows is what used to drive this negative.
377+ t1sum = sum (gsum, mask= (gthi == 1 ))/ real (num_procs, wp)
378+ print ' (A,F10.3,F19.1,A)' , ' [phase] RESIDUAL' , wall - t1sum, 100._wp * (wall - t1sum)/ wall, ' %'
379+ print ' (A,F10.3,A,F6.1,A)' , ' [phase-tier] T1 rows sum to ' , t1sum, ' s =' , 100._wp * t1sum/ wall, &
380+ & ' % of wall; RESIDUAL is wall minus T1 only'
381+ if (any (gsum > 0._wp .and. gtlo /= gthi)) then
382+ write (* , ' (A)' , advance= ' no' ) ' [phase-tier] opened at more than one depth, excluded from T1, not summable:'
383+ do i = 1 , PH_N
384+ if (gsum(i) > 0._wp .and. gtlo(i) /= gthi(i)) write (* , ' (1X,A)' , advance= ' no' ) trim (PH_NAME(i))
385+ end do
386+ write (* , ' (A)' ) ' '
387+ end if
388+ if (any (gbad > 0 )) then
389+ print ' (A,I0,A,I0,A)' , ' [phase-tier] BUDGET INVALID: ' , gbad(1 ), ' interleaved and ' , gbad(2 ), &
390+ & ' orphan brackets, so some row double counts'
391+ else
392+ print ' (A)' , ' [phase-tier] budget valid: every bracket closed innermost-first'
393+ end if
350394
351395 end subroutine s_phase_report
352396
0 commit comments