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
This commit is contained in:
parent
ba43a4cbc0
commit
ed8ca9383e
1 changed files with 16 additions and 28 deletions
|
|
@ -166,12 +166,10 @@ fn transfer_rows_from_viewport_to_lines_above(
|
||||||
count: usize,
|
count: usize,
|
||||||
max_viewport_width: usize,
|
max_viewport_width: usize,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
let mut next_lines: Vec<Row> = vec![];
|
|
||||||
let mut transferred_rows_count: isize = 0;
|
let mut transferred_rows_count: isize = 0;
|
||||||
for _ in 0..count {
|
let drained_lines = std::cmp::min(count, viewport.len());
|
||||||
if next_lines.is_empty() {
|
for next_line in viewport.drain(..drained_lines) {
|
||||||
if !viewport.is_empty() {
|
let mut next_lines: Vec<Row> = vec![];
|
||||||
let next_line = viewport.remove(0);
|
|
||||||
transferred_rows_count +=
|
transferred_rows_count +=
|
||||||
calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
|
calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
|
||||||
if !next_line.is_canonical {
|
if !next_line.is_canonical {
|
||||||
|
|
@ -180,23 +178,12 @@ fn transfer_rows_from_viewport_to_lines_above(
|
||||||
next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
|
next_lines.append(&mut bottom_canonical_row_and_wraps_in_dst);
|
||||||
}
|
}
|
||||||
next_lines.push(next_line);
|
next_lines.push(next_line);
|
||||||
next_lines = vec![Row::from_rows(next_lines)];
|
let dropped_line_width = bounded_push(lines_above, sixel_grid, Row::from_rows(next_lines));
|
||||||
} else {
|
|
||||||
break; // no more rows
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let dropped_line_width = bounded_push(lines_above, sixel_grid, next_lines.remove(0));
|
|
||||||
if let Some(width) = dropped_line_width {
|
if let Some(width) = dropped_line_width {
|
||||||
transferred_rows_count -=
|
transferred_rows_count -=
|
||||||
calculate_row_display_height(width, max_viewport_width) as isize;
|
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
|
transferred_rows_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3379,19 +3366,20 @@ impl Row {
|
||||||
self.width = None;
|
self.width = None;
|
||||||
}
|
}
|
||||||
pub fn drain_until(&mut self, x: usize) -> VecDeque<TerminalCharacter> {
|
pub fn drain_until(&mut self, x: usize) -> VecDeque<TerminalCharacter> {
|
||||||
let mut drained_part: VecDeque<TerminalCharacter> = VecDeque::new();
|
|
||||||
let mut drained_part_len = 0;
|
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
|
// drained_part_len == 0 here is so that if the grid is resized
|
||||||
// to a size of 1, we won't drop wide characters
|
// to a size of 1, we won't drop wide characters
|
||||||
if drained_part_len + next_character.width <= x || drained_part_len == 0 {
|
if drained_part_len + next_character.width <= x || drained_part_len == 0 {
|
||||||
drained_part.push_back(next_character);
|
|
||||||
drained_part_len += next_character.width;
|
drained_part_len += next_character.width;
|
||||||
|
split_pos += 1
|
||||||
} else {
|
} else {
|
||||||
self.columns.push_front(next_character); // put it back
|
|
||||||
break;
|
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;
|
self.width = None;
|
||||||
drained_part
|
drained_part
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue