summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan Hobson Sayers <aidanhs@cantab.net>2024-01-18 09:32:11 +0000
committerGitHub <noreply@github.com>2024-01-18 10:32:11 +0100
commited8ca9383e0530d6b6d54d6b778046690e6d826d (patch)
tree35f55fcab9c20c73c91ab7fc26a64398d563af80
parentba43a4cbc06fb63438f40d816120755a3166b876 (diff)
performance(terminal): improvements to reflow performance by removing O(n^2) behavior (#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
-rw-r--r--zellij-server/src/panes/grid.rs46
1 files changed, 17 insertions, 29 deletions
diff --git a/zellij-server/src/panes/grid.rs b/zellij-server/src/panes/grid.rs
index b7ed874f7..4b950f928 100644
--- a/zellij-server/src/panes/grid.rs
+++ b/zellij-server/src/panes/grid.rs
@@ -166,37 +166,24 @@ fn transfer_rows_from_viewport_to_lines_above(
count: usize,
max_viewport_width: usize,
) -> isize {
- let mut next_lines: Vec<Row> = vec![];
let mut transferred_rows_count: isize = 0;
- for _ in 0..count {
- if next_lines.is_empty() {
- if !viewport.is_empty() {
- let next_line = viewport.remove(0);
- transferred_rows_count +=
- calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
- if !next_line.is_canonical {
- let mut bottom_canonical_row_and_wraps_in_dst =
- get_lines_above_bottom_canonical_row_and_wraps(lines_above);
- next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
- }
- next_lines.push(next_line);
- next_lines = vec![Row::from_rows(next_lines)];
- } else {
- break; // no more rows
- }
- }
- let dropped_line_width = bounded_push(lines_above, sixel_grid, next_lines.remove(0));
+ let drained_lines = std::cmp::min(count, viewport.len());
+ for next_line in viewport.drain(..drained_lines) {
+ let mut next_lines: Vec<Row> = vec![];
+ transferred_rows_count +=
+ calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
+ if !next_line.is_canonical {
+ let mut bottom_canonical_row_and_wraps_in_dst =
+ get_lines_above_bottom_canonical_row_and_wraps(lines_above);
+ next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
+ }
+ next_lines.push(next_line);
+ let dropped_line_width = bounded_push(lines_above, sixel_grid, Row::from_rows(next_lines));
if let Some(width) = dropped_line_width {
transferred_rows_count -=
calculate_row_display_height(width, max_viewport_width) as isize;
}
}
- if !next_lines.is_empty() {
- let excess_rows = Row::from_rows(next_lines).split_to_rows_of_length(max_viewport_width);
- for row in excess_rows {
- viewport.insert(0, row);
- }
- }
transferred_rows_count
}
@@ -3379,19 +3366,20 @@ impl Row {
self.width = None;
}
pub fn drain_until(&mut self, x: usize) -> VecDeque<TerminalCharacter> {
- let mut drained_part: VecDeque<TerminalCharacter> = VecDeque::new();
let mut drained_part_len = 0;
- while let Some(next_character) = self.columns.remove(0) {
+ let mut split_pos = 0;
+ for next_character in self.columns.iter() {
// drained_part_len == 0 here is so that if the grid is resized
// to a size of 1, we won't drop wide characters
if drained_part_len + next_character.width <= x || drained_part_len == 0 {
- drained_part.push_back(next_character);
drained_part_len += next_character.width;
+ split_pos += 1
} else {
- self.columns.push_front(next_character); // put it back
break;
}
}
+ // Can't use split_off because it doesn't reduce capacity, causing OOM with long lines
+ let drained_part = self.columns.drain(..split_pos).collect();
self.width = None;
drained_part
}