mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
Dynamically initialize grid storage
Previously Alacritty has initialized all lines in the buffer as soon as it is started. This had the effect that terminals which aren't making use of the scrollback buffer yet, would still consume large amounts of memory, potentially even freezing the system at startup. To resolve this problem, the grid is now dynamically resized in chunks of `1000` rows. The initial size is just the visible area itself, then every time lines are written to the terminal emulator, the grid storage is grown when required. With the worst-case scenario of having 100_000 lines scrollback configured, this change improves startup performance at the cost of scrolling performance. On my machine the startup changes from ~0.3 to ~0.2 seconds. The scrolling performance with large throughput is not affected, however it is slowed down when the number of lines scrolled are close to the 100_000 configured as scrollback. The most taxing benchmark I've found for this was running `yes | dd count=500 > 500.txt` (note the relatively small file size). This will cause a slowdown on the first run from 0.05s to 0.15s. While this is significant, it lines up with the time saved at startup. This fixes #1236.
This commit is contained in:
parent
a752066bfa
commit
3d7e88e8a9
6 changed files with 189 additions and 129 deletions
|
@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Inverse/Selection color is now modelled after XTerm/VTE instead of URxvt to improve consistency
|
||||
- First click on unfocused Alacritty windows is no longer ignored on platforms other than macOS
|
||||
- Reduce memory usage significantly by only initializing part of the scrollback buffer at startup
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ mod tests;
|
|||
mod storage;
|
||||
use self::storage::Storage;
|
||||
|
||||
const MIN_INIT_SIZE: usize = 1_000;
|
||||
|
||||
/// Bidirection iterator
|
||||
pub trait BidirectionalIterator: Iterator {
|
||||
fn prev(&mut self) -> Option<Self::Item>;
|
||||
|
@ -92,6 +94,9 @@ pub struct Grid<T> {
|
|||
/// Selected region
|
||||
#[serde(skip)]
|
||||
pub selection: Option<Selection>,
|
||||
|
||||
#[serde(default)]
|
||||
max_scroll_limit: usize,
|
||||
}
|
||||
|
||||
pub struct GridIterator<'a, T: 'a> {
|
||||
|
@ -113,7 +118,7 @@ pub enum Scroll {
|
|||
|
||||
impl<T: Copy + Clone> Grid<T> {
|
||||
pub fn new(lines: index::Line, cols: index::Column, scrollback: usize, template: T) -> Grid<T> {
|
||||
let raw = Storage::with_capacity(*lines + scrollback, lines, Row::new(cols, &template));
|
||||
let raw = Storage::with_capacity(lines, Row::new(cols, &template));
|
||||
Grid {
|
||||
raw,
|
||||
cols,
|
||||
|
@ -121,6 +126,7 @@ impl<T: Copy + Clone> Grid<T> {
|
|||
display_offset: 0,
|
||||
scroll_limit: 0,
|
||||
selection: None,
|
||||
max_scroll_limit: scrollback,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,11 +212,19 @@ impl<T: Copy + Clone> Grid<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn increase_scroll_limit(&mut self, count: usize) {
|
||||
self.scroll_limit = min(
|
||||
self.scroll_limit + count,
|
||||
self.raw.len().saturating_sub(*self.lines),
|
||||
);
|
||||
fn increase_scroll_limit(&mut self, count: usize, template: &T)
|
||||
{
|
||||
self.scroll_limit = min(self.scroll_limit + count, self.max_scroll_limit);
|
||||
|
||||
// Initialize new lines when the history buffer is smaller than the scroll limit
|
||||
let history_size = self.raw.len().saturating_sub(*self.lines);
|
||||
if history_size < self.scroll_limit {
|
||||
let new = min(
|
||||
max(self.scroll_limit - history_size, MIN_INIT_SIZE),
|
||||
self.max_scroll_limit - history_size,
|
||||
);
|
||||
self.raw.initialize(new, Row::new(self.cols, template));
|
||||
}
|
||||
}
|
||||
|
||||
fn decrease_scroll_limit(&mut self, count: usize) {
|
||||
|
@ -356,7 +370,7 @@ impl<T: Copy + Clone> Grid<T> {
|
|||
);
|
||||
}
|
||||
|
||||
self.increase_scroll_limit(*positions);
|
||||
self.increase_scroll_limit(*positions, template);
|
||||
|
||||
// Rotate the entire line buffer. If there's a scrolling region
|
||||
// active, the bottom lines are restored in the next step.
|
||||
|
|
|
@ -79,18 +79,18 @@ impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
|
|||
|
||||
impl<T> Storage<T> {
|
||||
#[inline]
|
||||
pub fn with_capacity(cap: usize, lines: Line, template: Row<T>) -> Storage<T>
|
||||
pub fn with_capacity(lines: Line, template: Row<T>) -> Storage<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
// Allocate all lines in the buffer, including scrollback history
|
||||
let inner = vec![template; cap];
|
||||
// Initialize visible lines, the scrollback buffer is initialized dynamically
|
||||
let inner = vec![template; lines.0];
|
||||
|
||||
Storage {
|
||||
inner,
|
||||
zero: 0,
|
||||
visible_lines: lines - 1,
|
||||
len: cap,
|
||||
len: lines.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,25 +169,26 @@ impl<T> Storage<T> {
|
|||
|
||||
/// Truncate the invisible elements from the raw buffer
|
||||
pub fn truncate(&mut self) {
|
||||
// Calculate shrinkage/offset for indexing
|
||||
let shrinkage = self.inner.len() - self.len;
|
||||
let shrinkage_start = ::std::cmp::min(self.zero, shrinkage);
|
||||
self.inner.rotate_left(self.zero);
|
||||
self.inner.truncate(self.len);
|
||||
|
||||
// Create two vectors with correct ordering
|
||||
let mut split = self.inner.split_off(self.zero);
|
||||
|
||||
// Truncate the buffers
|
||||
let len = self.inner.len();
|
||||
let split_len = split.len();
|
||||
self.inner.truncate(len - shrinkage_start);
|
||||
split.truncate(split_len - (shrinkage - shrinkage_start));
|
||||
|
||||
// Merge buffers again and reset zero
|
||||
split.append(&mut self.inner);
|
||||
self.inner = split;
|
||||
self.zero = 0;
|
||||
}
|
||||
|
||||
/// Dynamically grow the storage buffer at runtime
|
||||
pub fn initialize(&mut self, num_rows: usize, template_row: Row<T>)
|
||||
where T: Clone
|
||||
{
|
||||
let mut new = vec![template_row; num_rows];
|
||||
|
||||
let mut split = self.inner.split_off(self.zero);
|
||||
self.inner.append(&mut new);
|
||||
self.inner.append(&mut split);
|
||||
|
||||
self.zero += num_rows;
|
||||
self.len += num_rows;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
|
@ -649,3 +650,45 @@ fn shrink_then_grow() {
|
|||
assert_eq!(storage.zero, growing_expected.zero);
|
||||
assert_eq!(storage.len, growing_expected.len);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialize() {
|
||||
// Setup storage area
|
||||
let mut storage = Storage {
|
||||
inner: vec![
|
||||
Row::new(Column(1), &'4'),
|
||||
Row::new(Column(1), &'5'),
|
||||
Row::new(Column(1), &'0'),
|
||||
Row::new(Column(1), &'1'),
|
||||
Row::new(Column(1), &'2'),
|
||||
Row::new(Column(1), &'3'),
|
||||
],
|
||||
zero: 2,
|
||||
visible_lines: Line(0),
|
||||
len: 6,
|
||||
};
|
||||
|
||||
// Initialize additional lines
|
||||
storage.initialize(3, Row::new(Column(1), &'-'));
|
||||
|
||||
// Make sure the lines are present and at the right location
|
||||
let shrinking_expected = Storage {
|
||||
inner: vec![
|
||||
Row::new(Column(1), &'4'),
|
||||
Row::new(Column(1), &'5'),
|
||||
Row::new(Column(1), &'-'),
|
||||
Row::new(Column(1), &'-'),
|
||||
Row::new(Column(1), &'-'),
|
||||
Row::new(Column(1), &'0'),
|
||||
Row::new(Column(1), &'1'),
|
||||
Row::new(Column(1), &'2'),
|
||||
Row::new(Column(1), &'3'),
|
||||
],
|
||||
zero: 5,
|
||||
visible_lines: Line(0),
|
||||
len: 9,
|
||||
};
|
||||
assert_eq!(storage.inner, shrinking_expected.inner);
|
||||
assert_eq!(storage.zero, shrinking_expected.zero);
|
||||
assert_eq!(storage.len, shrinking_expected.len);
|
||||
}
|
||||
|
|
|
@ -1,102 +1,104 @@
|
|||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m issue-1244 ↑ [0m [K[?2004hf[90mor i in {100..2}; do echo $i; done[39m[34Df[39mo[39mr[39m [39mi[39m [39mi[39mn[39m [39m{[39m1[39m0[39m0[39m.[39m.[39m2[39m}[39m;[39m [39md[39mo[39m [39me[39mc[39mh[39mo[39m [39m$[39mi[39m;[39m [39md[39mo[39mn[39me[?2004l
|
||||
100
|
||||
99
|
||||
98
|
||||
97
|
||||
96
|
||||
95
|
||||
94
|
||||
93
|
||||
92
|
||||
91
|
||||
90
|
||||
89
|
||||
88
|
||||
87
|
||||
86
|
||||
85
|
||||
84
|
||||
83
|
||||
82
|
||||
81
|
||||
80
|
||||
79
|
||||
78
|
||||
77
|
||||
76
|
||||
75
|
||||
74
|
||||
73
|
||||
72
|
||||
71
|
||||
70
|
||||
69
|
||||
68
|
||||
67
|
||||
66
|
||||
65
|
||||
64
|
||||
63
|
||||
62
|
||||
61
|
||||
60
|
||||
59
|
||||
58
|
||||
57
|
||||
56
|
||||
55
|
||||
54
|
||||
53
|
||||
52
|
||||
51
|
||||
50
|
||||
49
|
||||
48
|
||||
47
|
||||
46
|
||||
45
|
||||
44
|
||||
43
|
||||
42
|
||||
41
|
||||
40
|
||||
39
|
||||
38
|
||||
37
|
||||
36
|
||||
35
|
||||
34
|
||||
33
|
||||
32
|
||||
31
|
||||
30
|
||||
29
|
||||
28
|
||||
27
|
||||
26
|
||||
25
|
||||
24
|
||||
23
|
||||
22
|
||||
21
|
||||
20
|
||||
19
|
||||
18
|
||||
17
|
||||
16
|
||||
15
|
||||
14
|
||||
13
|
||||
12
|
||||
11
|
||||
10
|
||||
9
|
||||
8
|
||||
7
|
||||
6
|
||||
5
|
||||
4
|
||||
3
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m dynamic-alloc [0m [K[?2004hf[90mor i in {0..100}; do echo $i; done[39m[34Df[39mo[39mr[39m [39mi[39m [39mi[39mn[39m [39m{[39m0[39m.[39m.[39m1[39m0[39m0[39m}[39m;[39m [39md[39mo[39m [39me[39mc[39mh[39mo[39m [39m$[39mi[39m;[39m [39md[39mo[39mn[39me[?2004l
|
||||
0
|
||||
1
|
||||
2
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m issue-1244 ↑ [0m [K[?2004hr[90meset[39mr[39me[39ms[39me[39mt[?2004l
|
||||
c]104[!p[?3;4l[4l>
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m issue-1244 ↑ [0m [K[?2004h
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
17
|
||||
18
|
||||
19
|
||||
20
|
||||
21
|
||||
22
|
||||
23
|
||||
24
|
||||
25
|
||||
26
|
||||
27
|
||||
28
|
||||
29
|
||||
30
|
||||
31
|
||||
32
|
||||
33
|
||||
34
|
||||
35
|
||||
36
|
||||
37
|
||||
38
|
||||
39
|
||||
40
|
||||
41
|
||||
42
|
||||
43
|
||||
44
|
||||
45
|
||||
46
|
||||
47
|
||||
48
|
||||
49
|
||||
50
|
||||
51
|
||||
52
|
||||
53
|
||||
54
|
||||
55
|
||||
56
|
||||
57
|
||||
58
|
||||
59
|
||||
60
|
||||
61
|
||||
62
|
||||
63
|
||||
64
|
||||
65
|
||||
66
|
||||
67
|
||||
68
|
||||
69
|
||||
70
|
||||
71
|
||||
72
|
||||
73
|
||||
74
|
||||
75
|
||||
76
|
||||
77
|
||||
78
|
||||
79
|
||||
80
|
||||
81
|
||||
82
|
||||
83
|
||||
84
|
||||
85
|
||||
86
|
||||
87
|
||||
88
|
||||
89
|
||||
90
|
||||
91
|
||||
92
|
||||
93
|
||||
94
|
||||
95
|
||||
96
|
||||
97
|
||||
98
|
||||
99
|
||||
100
|
||||
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m dynamic-alloc [0m [K[?2004hr[90mm *[39mr[39me[90ms[90me[90mt[39m[39ms[39me[39mt[?2004l
|
||||
c]104[!p[?3;4l[4l>
[1m[7m%[27m[1m[0m
[0m[27m[24m[J[0;30;101m UL [0m[0;37;100m ~/…/tests/ref/grid_reset [0m[0;30;101m dynamic-alloc [0m [K[?2004h
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
{"width":939.0,"height":1020.0,"cell_width":8.0,"cell_height":18.0,"padding_x":2.0,"padding_y":2.0}
|
||||
{"width":2532.0,"height":1380.0,"cell_width":9.0,"cell_height":19.0,"padding_x":3.0,"padding_y":3.0}
|
Loading…
Reference in a new issue