diff options
author | Aidan Hobson Sayers <aidanhs@cantab.net> | 2024-01-18 09:32:11 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-18 10:32:11 +0100 |
commit | ed8ca9383e0530d6b6d54d6b778046690e6d826d (patch) | |
tree | 35f55fcab9c20c73c91ab7fc26a64398d563af80 | |
parent | ba43a4cbc06fb63438f40d816120755a3166b876 (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.rs | 46 |
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 } |