mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
wip(tui): measure initial replay scrollback cost
This commit is contained in:
@@ -510,6 +510,18 @@ struct SessionSummary {
|
||||
resume_command: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct InitialHistoryReplayMetrics {
|
||||
replay_elapsed: Option<Duration>,
|
||||
render_elapsed: Duration,
|
||||
enqueue_elapsed: Duration,
|
||||
flush_elapsed: Duration,
|
||||
cell_count: usize,
|
||||
rendered_line_count: usize,
|
||||
flushed_line_count: usize,
|
||||
replay_finished: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct App {
|
||||
model_catalog: Arc<ModelCatalog>,
|
||||
pub(crate) session_telemetry: SessionTelemetry,
|
||||
@@ -532,6 +544,7 @@ pub(crate) struct App {
|
||||
pub(crate) deferred_history_lines: Vec<Line<'static>>,
|
||||
has_emitted_history_lines: bool,
|
||||
transcript_reflow: TranscriptReflowState,
|
||||
initial_history_replay_metrics: Option<InitialHistoryReplayMetrics>,
|
||||
|
||||
pub(crate) enhanced_keys_supported: bool,
|
||||
|
||||
@@ -918,6 +931,7 @@ impl App {
|
||||
deferred_history_lines: Vec::new(),
|
||||
has_emitted_history_lines: false,
|
||||
transcript_reflow: TranscriptReflowState::default(),
|
||||
initial_history_replay_metrics: None,
|
||||
commit_anim_running: Arc::new(AtomicBool::new(false)),
|
||||
status_line_invalid_items_warned: status_line_invalid_items_warned.clone(),
|
||||
terminal_title_invalid_items_warned: terminal_title_invalid_items_warned.clone(),
|
||||
@@ -1159,6 +1173,7 @@ impl App {
|
||||
}
|
||||
})?;
|
||||
self.maybe_disable_slow_resize_reflow_flush(stats);
|
||||
self.maybe_finish_initial_history_replay_measurement(stats);
|
||||
} else {
|
||||
tui.draw(desired_height, |frame| {
|
||||
self.chat_widget.render(frame.area(), frame.buffer);
|
||||
|
||||
@@ -176,6 +176,9 @@ impl App {
|
||||
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
AppEvent::BeginInitialHistoryReplayMeasurement => {
|
||||
self.begin_initial_history_replay_measurement();
|
||||
}
|
||||
AppEvent::InsertHistoryCell(cell) => {
|
||||
let cell: Arc<dyn HistoryCell> = cell.into();
|
||||
if let Some(Overlay::Transcript(t)) = &mut self.overlay {
|
||||
@@ -183,11 +186,22 @@ impl App {
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
self.transcript_cells.push(cell.clone());
|
||||
self.insert_history_cell_lines(
|
||||
tui,
|
||||
cell.as_ref(),
|
||||
tui.terminal.last_known_screen_size.width,
|
||||
);
|
||||
if self.initial_history_replay_metrics.is_some() {
|
||||
self.insert_history_cell_lines_with_initial_replay_measurement(
|
||||
tui,
|
||||
cell.as_ref(),
|
||||
tui.terminal.last_known_screen_size.width,
|
||||
);
|
||||
} else {
|
||||
self.insert_history_cell_lines(
|
||||
tui,
|
||||
cell.as_ref(),
|
||||
tui.terminal.last_known_screen_size.width,
|
||||
);
|
||||
}
|
||||
}
|
||||
AppEvent::EndInitialHistoryReplayMeasurement { replay_elapsed } => {
|
||||
self.finish_initial_history_replay_measurement(replay_elapsed);
|
||||
}
|
||||
AppEvent::ConsolidateAgentMessage { source, cwd } => {
|
||||
if !self.terminal_resize_reflow_enabled() {
|
||||
|
||||
@@ -92,6 +92,7 @@ impl App {
|
||||
self.deferred_history_lines.clear();
|
||||
self.has_emitted_history_lines = false;
|
||||
self.transcript_reflow.clear();
|
||||
self.initial_history_replay_metrics = None;
|
||||
self.backtrack = BacktrackState::default();
|
||||
self.backtrack_render_pending = false;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use color_eyre::eyre::Result;
|
||||
use ratatui::text::Line;
|
||||
|
||||
use super::App;
|
||||
use super::InitialHistoryReplayMetrics;
|
||||
use super::trailing_run_start;
|
||||
use crate::history_cell;
|
||||
use crate::history_cell::HistoryCell;
|
||||
@@ -94,6 +95,93 @@ impl App {
|
||||
self.terminal_resize_reflow_enabled() && !self.transcript_reflow.is_runtime_disabled()
|
||||
}
|
||||
|
||||
pub(super) fn begin_initial_history_replay_measurement(&mut self) {
|
||||
if self.terminal_resize_reflow_active() && self.overlay.is_none() {
|
||||
self.initial_history_replay_metrics = Some(Default::default());
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn finish_initial_history_replay_measurement(&mut self, replay_elapsed: Duration) {
|
||||
let Some(metrics) = &mut self.initial_history_replay_metrics else {
|
||||
return;
|
||||
};
|
||||
metrics.replay_elapsed = Some(replay_elapsed);
|
||||
metrics.replay_finished = true;
|
||||
}
|
||||
|
||||
pub(super) fn insert_history_cell_lines_with_initial_replay_measurement(
|
||||
&mut self,
|
||||
tui: &mut tui::Tui,
|
||||
cell: &dyn HistoryCell,
|
||||
width: u16,
|
||||
) {
|
||||
let render_started = Instant::now();
|
||||
let display = self.display_lines_for_history_insert(cell, width);
|
||||
let render_elapsed = render_started.elapsed();
|
||||
let rendered_line_count = display.len();
|
||||
|
||||
if let Some(metrics) = &mut self.initial_history_replay_metrics {
|
||||
metrics.cell_count += 1;
|
||||
metrics.rendered_line_count += rendered_line_count;
|
||||
metrics.render_elapsed += render_elapsed;
|
||||
}
|
||||
|
||||
if display.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let enqueue_started = Instant::now();
|
||||
if self.overlay.is_some() {
|
||||
self.deferred_history_lines.extend(display);
|
||||
} else {
|
||||
tui.insert_history_lines(display);
|
||||
}
|
||||
if let Some(metrics) = &mut self.initial_history_replay_metrics {
|
||||
metrics.enqueue_elapsed += enqueue_started.elapsed();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn maybe_finish_initial_history_replay_measurement(
|
||||
&mut self,
|
||||
draw_stats: tui::ResizeReflowDrawStats,
|
||||
) {
|
||||
let Some(metrics) = &mut self.initial_history_replay_metrics else {
|
||||
return;
|
||||
};
|
||||
if draw_stats.flushed_history {
|
||||
metrics.flush_elapsed += draw_stats.history_flush_elapsed;
|
||||
metrics.flushed_line_count += draw_stats.history_flush_line_count;
|
||||
}
|
||||
if !metrics.replay_finished
|
||||
|| (metrics.rendered_line_count > 0
|
||||
&& metrics.flushed_line_count < metrics.rendered_line_count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let metrics = self
|
||||
.initial_history_replay_metrics
|
||||
.take()
|
||||
.expect("metrics checked above");
|
||||
self.show_initial_history_replay_measurement(metrics);
|
||||
}
|
||||
|
||||
fn show_initial_history_replay_measurement(&mut self, metrics: InitialHistoryReplayMetrics) {
|
||||
let replay_ms = metrics.replay_elapsed.unwrap_or_default().as_millis();
|
||||
self.chat_widget.add_info_message(
|
||||
format!(
|
||||
"Initial transcript replay took {replay_ms}ms to queue, {}ms to render, {}ms to enqueue, and {}ms to flush ({} rows from {} cells; {} rows flushed).",
|
||||
metrics.render_elapsed.as_millis(),
|
||||
metrics.enqueue_elapsed.as_millis(),
|
||||
metrics.flush_elapsed.as_millis(),
|
||||
metrics.rendered_line_count,
|
||||
metrics.cell_count,
|
||||
metrics.flushed_line_count,
|
||||
),
|
||||
/*hint*/ None,
|
||||
);
|
||||
}
|
||||
|
||||
fn schedule_resize_reflow(
|
||||
&mut self,
|
||||
kind: TranscriptReflowKind,
|
||||
|
||||
@@ -31,6 +31,7 @@ pub(super) async fn make_test_app() -> App {
|
||||
deferred_history_lines: Vec::new(),
|
||||
has_emitted_history_lines: false,
|
||||
transcript_reflow: TranscriptReflowState::default(),
|
||||
initial_history_replay_metrics: None,
|
||||
enhanced_keys_supported: false,
|
||||
commit_anim_running: Arc::new(AtomicBool::new(false)),
|
||||
status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)),
|
||||
|
||||
@@ -3570,6 +3570,7 @@ async fn make_test_app() -> App {
|
||||
deferred_history_lines: Vec::new(),
|
||||
has_emitted_history_lines: false,
|
||||
transcript_reflow: TranscriptReflowState::default(),
|
||||
initial_history_replay_metrics: None,
|
||||
enhanced_keys_supported: false,
|
||||
commit_anim_running: Arc::new(AtomicBool::new(false)),
|
||||
status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)),
|
||||
@@ -3628,6 +3629,7 @@ async fn make_test_app_with_channels() -> (
|
||||
deferred_history_lines: Vec::new(),
|
||||
has_emitted_history_lines: false,
|
||||
transcript_reflow: TranscriptReflowState::default(),
|
||||
initial_history_replay_metrics: None,
|
||||
enhanced_keys_supported: false,
|
||||
commit_anim_running: Arc::new(AtomicBool::new(false)),
|
||||
status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)),
|
||||
|
||||
@@ -1029,8 +1029,21 @@ impl App {
|
||||
self.chat_widget
|
||||
.set_initial_user_message_submit_suppressed(/*suppressed*/ true);
|
||||
self.chat_widget.handle_thread_session(session);
|
||||
let should_measure_initial_replay =
|
||||
self.terminal_resize_reflow_active() && !turns.is_empty();
|
||||
let replay_started = should_measure_initial_replay.then(Instant::now);
|
||||
if should_measure_initial_replay {
|
||||
self.app_event_tx
|
||||
.send(AppEvent::BeginInitialHistoryReplayMeasurement);
|
||||
}
|
||||
self.chat_widget
|
||||
.replay_thread_turns(turns, ReplayKind::ResumeInitialMessages);
|
||||
if let Some(started) = replay_started {
|
||||
self.app_event_tx
|
||||
.send(AppEvent::EndInitialHistoryReplayMeasurement {
|
||||
replay_elapsed: started.elapsed(),
|
||||
});
|
||||
}
|
||||
let pending = std::mem::take(&mut self.pending_primary_events);
|
||||
for pending_event in pending {
|
||||
match pending_event {
|
||||
|
||||
@@ -354,8 +354,16 @@ pub(crate) enum AppEvent {
|
||||
result: Result<SkillsListResponse, String>,
|
||||
},
|
||||
|
||||
/// Begin measuring initial resume replay without changing the replay behavior.
|
||||
BeginInitialHistoryReplayMeasurement,
|
||||
|
||||
InsertHistoryCell(Box<dyn HistoryCell>),
|
||||
|
||||
/// Finish measuring initial resume replay after all replay events have been queued.
|
||||
EndInitialHistoryReplayMeasurement {
|
||||
replay_elapsed: std::time::Duration,
|
||||
},
|
||||
|
||||
/// Replace the contiguous run of streaming `AgentMessageCell`s at the end of
|
||||
/// the transcript with a single `AgentMarkdownCell` that stores the raw
|
||||
/// markdown source and re-renders from it on resize.
|
||||
|
||||
+10
-1
@@ -484,6 +484,9 @@ pub struct Tui {
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(crate) struct ResizeReflowDrawStats {
|
||||
pub(crate) history_flush_elapsed: Duration,
|
||||
pub(crate) history_flush_line_count: usize,
|
||||
pub(crate) flushed_history: bool,
|
||||
pub(crate) reflow_flush_elapsed: Duration,
|
||||
pub(crate) flushed_reflow_history: bool,
|
||||
}
|
||||
@@ -933,11 +936,17 @@ impl Tui {
|
||||
let terminal = &mut self.terminal;
|
||||
let mut needs_full_repaint =
|
||||
Self::update_inline_viewport_for_resize_reflow(terminal, height, self.is_zellij)?;
|
||||
needs_full_repaint |= Self::flush_pending_history_lines(
|
||||
let history_flush_line_count = self.pending_history_lines.len();
|
||||
let history_flush_start = Instant::now();
|
||||
let flushed_history = Self::flush_pending_history_lines(
|
||||
terminal,
|
||||
&mut self.pending_history_lines,
|
||||
self.is_zellij,
|
||||
)?;
|
||||
stats.history_flush_elapsed = history_flush_start.elapsed();
|
||||
stats.history_flush_line_count = history_flush_line_count;
|
||||
stats.flushed_history = history_flush_line_count > 0;
|
||||
needs_full_repaint |= flushed_history;
|
||||
let reflow_flush_start = Instant::now();
|
||||
let flushed_reflow_history = Self::flush_pending_reflow_history_lines(
|
||||
terminal,
|
||||
|
||||
Reference in New Issue
Block a user