Progress bar: fresh-this-run counter; fix KeyError 'success'
- Completed X/Y now carries (+N new): samples generated by THIS run, excluding the checkpoint-restored head start (previously 98/164 told you nothing about how much work this invocation actually did) - rate/eta computed over fresh samples only (restored ones counted toward 141/6s = 23/s when 1 sample had been generated) - fix KeyError 'success': advance() still read the success/failed task fields after they were dropped from the column set -- first advance on a resumed bench killed the whole benchmark - set_overall: give the overall task the fields the shared columns actually read (cur/retries/elapsed/eta); the old field set was from a previous column layout Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
20d2d5537a
commit
f5c2e4c5af
@ -43,7 +43,7 @@ class RichTerminalProgress:
|
|||||||
TextColumn("[progress.description]{task.description}"),
|
TextColumn("[progress.description]{task.description}"),
|
||||||
BarColumn(complete_style="green", finished_style="bold green"),
|
BarColumn(complete_style="green", finished_style="bold green"),
|
||||||
TaskProgressColumn(),
|
TaskProgressColumn(),
|
||||||
TextColumn("• Completed {task.completed}/{task.total}"),
|
TextColumn("• Completed {task.completed}/{task.total} [dim]{task.fields[new]}[/dim]"),
|
||||||
TextColumn("• in-flight [yellow]{task.fields[inflight]}[/yellow] ([yellow]{task.fields[cur]}[/yellow])"),
|
TextColumn("• in-flight [yellow]{task.fields[inflight]}[/yellow] ([yellow]{task.fields[cur]}[/yellow])"),
|
||||||
TextColumn("• [red]retries {task.fields[retries]}[/red]"),
|
TextColumn("• [red]retries {task.fields[retries]}[/red]"),
|
||||||
TextColumn("• [dim]{task.fields[rate]}/s[/dim]"),
|
TextColumn("• [dim]{task.fields[rate]}/s[/dim]"),
|
||||||
@ -59,6 +59,7 @@ class RichTerminalProgress:
|
|||||||
self.started = 0.0
|
self.started = 0.0
|
||||||
self.current_started = 0.0
|
self.current_started = 0.0
|
||||||
self.inflight = 0
|
self.inflight = 0
|
||||||
|
self.restored = 0 # checkpoint head start (drives the '+N new' marker)
|
||||||
self.heartbeat_task = None
|
self.heartbeat_task = None
|
||||||
|
|
||||||
def set_overall(self, total: int, done: int, label: str = 'benches'):
|
def set_overall(self, total: int, done: int, label: str = 'benches'):
|
||||||
@ -73,8 +74,8 @@ class RichTerminalProgress:
|
|||||||
# overall task the same fields or rendering raises KeyError
|
# overall task the same fields or rendering raises KeyError
|
||||||
self.overall_id = self.progress.add_task(
|
self.overall_id = self.progress.add_task(
|
||||||
f'[cyan]{label}[/cyan]', total=total, completed=min(done, total),
|
f'[cyan]{label}[/cyan]', total=total, completed=min(done, total),
|
||||||
inflight=0, rate='0.00', success=done, failed=0,
|
new='', inflight=0, cur='0s', retries=0, rate='0.00',
|
||||||
waiting='00:00', last_result='')
|
elapsed='0s', eta='-')
|
||||||
else:
|
else:
|
||||||
self.progress.update(self.overall_id, completed=min(done, total))
|
self.progress.update(self.overall_id, completed=min(done, total))
|
||||||
|
|
||||||
@ -88,6 +89,7 @@ class RichTerminalProgress:
|
|||||||
if self.disabled:
|
if self.disabled:
|
||||||
return
|
return
|
||||||
self.started = time.monotonic()
|
self.started = time.monotonic()
|
||||||
|
self.restored = max(completed, 0)
|
||||||
if self.task_id is not None:
|
if self.task_id is not None:
|
||||||
return # one live reporter at a time; reuse across benchmarks
|
return # one live reporter at a time; reuse across benchmarks
|
||||||
self.progress.start()
|
self.progress.start()
|
||||||
@ -95,18 +97,24 @@ class RichTerminalProgress:
|
|||||||
f"[green]{description}",
|
f"[green]{description}",
|
||||||
total=total,
|
total=total,
|
||||||
completed=min(completed, total),
|
completed=min(completed, total),
|
||||||
success=completed,
|
new=self._new_txt(completed),
|
||||||
failed=0,
|
|
||||||
rate="0.00",
|
rate="0.00",
|
||||||
inflight=0,
|
inflight=0,
|
||||||
cur="0s",
|
cur="0s",
|
||||||
|
retries=0,
|
||||||
elapsed="0s",
|
elapsed="0s",
|
||||||
eta="-",
|
eta="-",
|
||||||
waiting="00:00",
|
|
||||||
last_result="restored",
|
|
||||||
)
|
)
|
||||||
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
self.heartbeat_task = asyncio.create_task(self._heartbeat())
|
||||||
|
|
||||||
|
def _new_txt(self, absolute_done: int) -> str:
|
||||||
|
"""'(+N new)' marker: samples completed by THIS run, i.e. absolute
|
||||||
|
progress minus the checkpoint-restored head start. Empty when the
|
||||||
|
run started fresh (nothing was restored, nothing to distinguish)."""
|
||||||
|
if not getattr(self, 'restored', 0):
|
||||||
|
return ''
|
||||||
|
return f'(+{max(absolute_done - self.restored, 0)} new)'
|
||||||
|
|
||||||
def reset_samples(self, total: int, description: str, completed: int = 0):
|
def reset_samples(self, total: int, description: str, completed: int = 0):
|
||||||
if self.disabled:
|
if self.disabled:
|
||||||
return
|
return
|
||||||
@ -114,20 +122,20 @@ class RichTerminalProgress:
|
|||||||
self.started = time.monotonic()
|
self.started = time.monotonic()
|
||||||
self.inflight = 0
|
self.inflight = 0
|
||||||
self.bench_name = description
|
self.bench_name = description
|
||||||
|
self.restored = max(completed, 0) # checkpoint head start this bench
|
||||||
desc = f'[green]{self.bench_tag}{description} · generating[/green]'
|
desc = f'[green]{self.bench_tag}{description} · generating[/green]'
|
||||||
if self.task_id is None:
|
if self.task_id is None:
|
||||||
self.progress.start()
|
self.progress.start()
|
||||||
self.task_id = self.progress.add_task(
|
self.task_id = self.progress.add_task(
|
||||||
desc, total=total, completed=min(completed, total),
|
desc, total=total, completed=min(completed, total),
|
||||||
success=completed, failed=0, rate='0.00', inflight=0,
|
new=self._new_txt(completed), rate='0.00', inflight=0,
|
||||||
cur='0s', elapsed='0s', eta='-', retries=0,
|
cur='0s', elapsed='0s', eta='-', retries=0)
|
||||||
waiting='00:00', last_result='restored')
|
|
||||||
else:
|
else:
|
||||||
self.progress.update(self.task_id, description=desc,
|
self.progress.update(self.task_id, description=desc,
|
||||||
total=total, completed=min(completed, total),
|
total=total, completed=min(completed, total),
|
||||||
success=completed, failed=0, rate='0.00',
|
new=self._new_txt(completed), rate='0.00',
|
||||||
inflight=0, cur='0s', elapsed='0s', eta='-', retries=0,
|
inflight=0, cur='0s', elapsed='0s', eta='-',
|
||||||
last_result='redone')
|
retries=0)
|
||||||
# ALWAYS recreate the heartbeat: the previous one may have died during
|
# ALWAYS recreate the heartbeat: the previous one may have died during
|
||||||
# pause/resume cycles between benchmarks (stale reference -> silent
|
# pause/resume cycles between benchmarks (stale reference -> silent
|
||||||
# death -> frozen clock while the spinner still animates)
|
# death -> frozen clock while the spinner still animates)
|
||||||
@ -161,8 +169,7 @@ class RichTerminalProgress:
|
|||||||
return
|
return
|
||||||
self.inflight += 1
|
self.inflight += 1
|
||||||
self.current_started = time.monotonic()
|
self.current_started = time.monotonic()
|
||||||
self.progress.update(self.task_id, inflight=self.inflight, cur='0s',
|
self.progress.update(self.task_id, inflight=self.inflight, cur='0s')
|
||||||
waiting="00:00", last_result=f"waiting {label}")
|
|
||||||
|
|
||||||
def set_retries(self, n: int):
|
def set_retries(self, n: int):
|
||||||
"""Show the retry count on the bar (from the adapter's attempt)."""
|
"""Show the retry count on the bar (from the adapter's attempt)."""
|
||||||
@ -185,22 +192,20 @@ class RichTerminalProgress:
|
|||||||
return
|
return
|
||||||
task = self.progress.tasks[self.task_id]
|
task = self.progress.tasks[self.task_id]
|
||||||
completed = task.completed + 1
|
completed = task.completed + 1
|
||||||
ok = task.fields["success"] + (1 if success else 0)
|
|
||||||
failed = task.fields["failed"] + (0 if success else 1)
|
|
||||||
self.inflight = max(0, self.inflight - 1)
|
self.inflight = max(0, self.inflight - 1)
|
||||||
elapsed = max(time.monotonic() - self.started, 1e-6)
|
elapsed = max(time.monotonic() - self.started, 1e-6)
|
||||||
|
# rate/eta over THIS RUN's fresh samples only: counting the restored
|
||||||
|
# head start would print 141/6s = 23/s when 1 sample was generated
|
||||||
|
fresh = max(completed - getattr(self, 'restored', 0), 0)
|
||||||
self.progress.update(
|
self.progress.update(
|
||||||
self.task_id,
|
self.task_id,
|
||||||
advance=1,
|
advance=1,
|
||||||
success=ok,
|
new=self._new_txt(completed),
|
||||||
failed=failed,
|
rate=f"{fresh / elapsed:.2f}",
|
||||||
rate=f"{completed / elapsed:.2f}",
|
|
||||||
inflight=self.inflight, cur='0s',
|
inflight=self.inflight, cur='0s',
|
||||||
elapsed=_fmt(elapsed),
|
elapsed=_fmt(elapsed),
|
||||||
eta=_fmt((task.total - completed) * elapsed / completed)
|
eta=_fmt((task.total - completed) * elapsed / fresh)
|
||||||
if completed and task.total and task.total > completed else '-',
|
if fresh and task.total and task.total > completed else '-',
|
||||||
waiting="00:00",
|
|
||||||
last_result="success" if success else "failed",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _heartbeat(self):
|
async def _heartbeat(self):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user