Skip to content

Commit ed8ca93

Browse files
authored
performance(terminal): improvements to reflow performance by removing O(n^2) behavior (zellij-org#3045)
* refactor: Simplify transfer_rows_from_viewport_to_lines_above next_lines is always consolidated to a single Row, which immediately gets removed - we can remove some dead code as a result * perf: Batch remove rows from the viewport for performance Given a 1MB line catted into the terminal, a toggle-fullscreen + toggle-fullscreen + close-pane + `run true` goes from ~9s to ~3s * perf: Optimize Row::drain_until by splitting chars in one step Given a 10MB line catted into the terminal, a toggle-fullscreen + toggle-fullscreen + close-pane + `run true` goes from ~23s to ~20s
1 parent ba43a4c commit ed8ca93

File tree

1 file changed

+17
-29
lines changed

1 file changed

+17
-29
lines changed

zellij-server/src/panes/grid.rs

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -166,37 +166,24 @@ fn transfer_rows_from_viewport_to_lines_above(
166166
count: usize,
167167
max_viewport_width: usize,
168168
) -> isize {
169-
let mut next_lines: Vec<Row> = vec![];
170169
let mut transferred_rows_count: isize = 0;
171-
for _ in 0..count {
172-
if next_lines.is_empty() {
173-
if !viewport.is_empty() {
174-
let next_line = viewport.remove(0);
175-
transferred_rows_count +=
176-
calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
177-
if !next_line.is_canonical {
178-
let mut bottom_canonical_row_and_wraps_in_dst =
179-
get_lines_above_bottom_canonical_row_and_wraps(lines_above);
180-
next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
181-
}
182-
next_lines.push(next_line);
183-
next_lines = vec![Row::from_rows(next_lines)];
184-
} else {
185-
break; // no more rows
186-
}
187-
}
188-
let dropped_line_width = bounded_push(lines_above, sixel_grid, next_lines.remove(0));
170+
let drained_lines = std::cmp::min(count, viewport.len());
171+
for next_line in viewport.drain(..drained_lines) {
172+
let mut next_lines: Vec<Row> = vec![];
173+
transferred_rows_count +=
174+
calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
175+
if !next_line.is_canonical {
176+
let mut bottom_canonical_row_and_wraps_in_dst =
177+
get_lines_above_bottom_canonical_row_and_wraps(lines_above);
178+
next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
179+
}
180+
next_lines.push(next_line);
181+
let dropped_line_width = bounded_push(lines_above, sixel_grid, Row::from_rows(next_lines));
189182
if let Some(width) = dropped_line_width {
190183
transferred_rows_count -=
191184
calculate_row_display_height(width, max_viewport_width) as isize;
192185
}
193186
}
194-
if !next_lines.is_empty() {
195-
let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_viewport_width);
196-
for row in excess_rows {
197-
viewport.insert(0, row);
198-
}
199-
}
200187
transferred_rows_count
201188
}
202189

@@ -3379,19 +3366,20 @@ impl Row {
33793366
self.width = None;
33803367
}
33813368
pub fn drain_until(&mut self, x: usize) -> VecDeque<TerminalCharacter> {
3382-
let mut drained_part: VecDeque<TerminalCharacter> = VecDeque::new();
33833369
let mut drained_part_len = 0;
3384-
while let Some(next_character) = self.columns.remove(0) {
3370+
let mut split_pos = 0;
3371+
for next_character in self.columns.iter() {
33853372
// drained_part_len == 0 here is so that if the grid is resized
33863373
// to a size of 1, we won't drop wide characters
33873374
if drained_part_len + next_character.width <= x || drained_part_len == 0 {
3388-
drained_part.push_back(next_character);
33893375
drained_part_len += next_character.width;
3376+
split_pos += 1
33903377
} else {
3391-
self.columns.push_front(next_character); // put it back
33923378
break;
33933379
}
33943380
}
3381+
// Can't use split_off because it doesn't reduce capacity, causing OOM with long lines
3382+
let drained_part = self.columns.drain(..split_pos).collect();
33953383
self.width = None;
33963384
drained_part
33973385
}

0 commit comments

Comments
 (0)