Conversation
…ation
Several changes were made for this:
- `STATE_CHANGES` now stores a tuple of `(State, Action)` as opposed to `u8`
- The negative aspect of this is that it will now take over twice as
much space in memory (4 KiB) to 8.5 KiB
- On a modern system this should not come at a reasonable preformance cost
- The justification for this is that further `Action`s were needed to help
manage an APC command: `ApcBegin`, `ApcEnd`, and `ApcPut`. Therein it
became necassary to extend the struct from a quasi-`u4` to a `u8` as
these represent values 16, 17, and 18 respectively.
- This also removes the functions `pack` and `unpack`, a possible benefit
of the change.
- This means code was also changed in the macro library
`vte_generate_state_changes`
- `Perform` now includes three new functions with signatures listed below:
fn apc_begin(&mut self) {}
fn apc_end(&mut self) {}
fn apc_put(&mut self) {}
These I believe are the best way to facilitate the use of APC. An
alternative method could exist without `apc_begin`, although I believe
that from a library perspective it is more ergonomic to have such as
function, as it elimintates a potential check required by downstream
libraries.
- For illustration purposes there is a new examples `apc.rs`. I will
delete this example in the next commit and merge its functionality with
the already existing `parselog.rs` and the testing suite. I have merely
kept it to help demonstrate the functionality provided in the commit, and to
facilitate my preliminary testing of it
- This involved:
- Removing its dedicated (and frankly useless) example
- Writing a proper test
- Having it store a buffer. It currently uses its own buffer although
it could fairly easily be merged with that of OSC without any
obvious drawbacks. Next commit?
- Rather than having three functions within `Perform` it now has just
one `acp_dispatch` which should be easier to code with.
| #[inline(always)] | ||
| pub fn unpack(delta: u8) -> (State, Action) { | ||
| unsafe { | ||
| ( | ||
| // State is stored in bottom 4 bits | ||
| mem::transmute(delta & 0x0f), | ||
| // Action is stored in top 4 bits | ||
| mem::transmute(delta >> 4), | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
This needs to be benchmarked. There is no way we'll merge a patch that reduces performance for a feature nobody should be using.
There was a problem hiding this comment.
Running some initial benchmarks I've got the following results from cargo bench:
test bench::state_changes ... bench: 108,197 ns/iter (+/- 3,199)
test bench::testfile ... bench: 121,021 ns/iter (+/- 3,617)
whilst the old code yields the following:
test bench::state_changes ... bench: 95,320 ns/iter (+/- 4,951)
test bench::testfile ... bench: 101,009 ns/iter (+/- 7,342)
Which equates to roughly an 12% ~ 16% decrease in performance. I am using flamegraph now to investigate.
There was a problem hiding this comment.
I'd also recommend testing with https://github.com/alacritty/vtebench. That's ultimately what decides which features get merged into Alacritty or not.
src/lib.rs
Outdated
| /// Generic over the value for the size of the raw Operating System Command | ||
| /// buffer. Only used when the `no_std` feature is enabled. | ||
| /// buffer. Only used when the `no_std` feature is enabled. This buffer is | ||
| /// also used for APC (Application Program Commands.) |
There was a problem hiding this comment.
| /// also used for APC (Application Program Commands.) | |
| /// also used for APC (Application Program Commands). |
src/lib.rs
Outdated
| Action::ApcStart => { | ||
| self.osc_raw.clear(); | ||
| }, | ||
| Action::ApcEnd => { | ||
| self.apc_dispatch(performer); | ||
| }, |
There was a problem hiding this comment.
Statements should be inlined.
src/lib.rs
Outdated
| /// Called at the end of an APC (application program command), where | ||
| /// `bytes` is the content of the APC |
There was a problem hiding this comment.
| /// Called at the end of an APC (application program command), where | |
| /// `bytes` is the content of the APC | |
| /// Called at the end of an APC (application program command), where | |
| /// `bytes` is the content of the APC. |
src/lib.rs
Outdated
| const INPUT_1: &[u8] = b"\x1b_abc\x9c"; | ||
| const INPUT_2: &[u8] = b"\x1b_abc\x1b\\"; | ||
|
|
||
| // Test with ST terminator |
There was a problem hiding this comment.
| // Test with ST terminator | |
| // Test with ST terminator. |
src/lib.rs
Outdated
| } | ||
| assert_eq!(dispatcher.dispatched, vec![Sequence::Apc(b"abc".to_vec()),]); | ||
|
|
||
| // Test with ESC \ terminator |
There was a problem hiding this comment.
| // Test with ESC \ terminator | |
| // Test with ESC \ terminator. |
src/table.rs
Outdated
| 0x00..=0x06 => (Anywhere, Ignore), | ||
| 0x08..=0x17 => (Anywhere, Ignore), | ||
| 0x19 => (Anywhere, Ignore), | ||
| 0x1c..=0x1f => (Anywhere, Ignore), | ||
| 0x20..=0xff => (Anywhere, ApcPut), | ||
| 0x9c => (Ground, None), |
There was a problem hiding this comment.
Haven't checked these yet, just for personal reference.
This has a lower performance overhead. Needs to be tested further.
|
I've worked on performance, and have got the following results (from vtebench): Summary TableTL;DR About the same in both master and the patch
🟢 = fastest Raw ResultsNormal Alacritty:Alacritty + John-Toohey:apcvte's benchmarksTL;DR Approximately the same. alacritty/vte:masterJohn-Toohey:apc |
|
As for how this new solution works, it basically takes the implementation of |
|
@John-Toohey If you have an Alacritty revision that is built against this, could you put that up in a draft PR on the Alacritty repo? That way I can easily run performance tests against it. |
chrisduerr
left a comment
There was a problem hiding this comment.
I've kinda been ignoring this PR for a while, mostly because I'm not particularly happy with the implementation but I also do not have any concrete suggestions without looking into this myself (which I have no interest in doing).
But hopefully this will at least keep the conversation going and provide some feedback on why I think it's not mergeable as-is.
| Ground = 12, | ||
| OscString = 13, | ||
| SosPmApcString = 14, | ||
| Null = 14, |
There was a problem hiding this comment.
This is just a bad name. A "null state" already exists, it's called Ground. Looking at State::Null alone would make it impossible to tell what that state is for.
| match self.str_start { | ||
| 0x1d => { | ||
| match param_idx { | ||
| // Finish last parameter if not already maxed | ||
| MAX_OSC_PARAMS => (), | ||
|
|
||
| // First param is special - 0 to current byte index | ||
| 0 => { | ||
| self.osc_params[param_idx] = (0, idx); | ||
| self.osc_num_params += 1; | ||
| }, | ||
|
|
||
| // All other params depend on previous indexing | ||
| _ => { | ||
| let prev = self.osc_params[param_idx - 1]; | ||
| let begin = prev.1; | ||
| self.osc_params[param_idx] = (begin, idx); | ||
| self.osc_num_params += 1; | ||
| }, | ||
| } | ||
| self.osc_dispatch(performer, byte); | ||
| }, | ||
|
|
||
| // All other params depend on previous indexing | ||
| _ => { | ||
| let prev = self.osc_params[param_idx - 1]; | ||
| let begin = prev.1; | ||
| self.osc_params[param_idx] = (begin, idx); | ||
| self.osc_num_params += 1; | ||
| 0x18 => { | ||
| self.sos_dispatch(performer); | ||
| }, | ||
| 0x1e => { | ||
| self.pm_dispatch(performer); | ||
| }, | ||
| 0x1F => { | ||
| self.apc_dispatch(performer); | ||
| }, | ||
| // This condition should never be hit under the | ||
| // current way in which the code is ran. | ||
| _ => unreachable!(), | ||
| } |
There was a problem hiding this comment.
This basically feels like an embedded parser. Most of our parser is structured in a way to stream through bytes and process them as they're coming in, however rather than creating state transitions you're storing a state transition to later then match on it instead. VTE is already too slow and I'm afraid this will just amplify that problem.
| // This condition should never be hit under the | ||
| // current way in which the code is ran. | ||
| _ => unreachable!(), |
There was a problem hiding this comment.
This comment is basically just a long way to say unreachable!(). It doesn't explain why it is unreachable it just states that it is, which unreachable!() does too. Should either add a useful comment or remove this.
|
|
||
| // Param separator | ||
| if byte == b';' { | ||
| if self.str_start == 0x1d && byte == b';' { |
There was a problem hiding this comment.
One extra branch isn't going to be the end of the world, but I don't see how this isn't just going to be a net-negative for anyone who doesn't use APC escapes (which is basically everyone).
|
Dead PR see #115. |
Fixes #109.
This adds support for APC (Application Program Command) in the same way OSC is implemented. It does, however, have the drawback of (just over) doubling the in-memory footprint of the
table::STATE_CHANGESstatic, for it became necessary to splitStateandActionas the amount of variants withinActionexceeded 15, meaning it was no longer possible for it to be a pseudou4.More detail in the commit messages.
Thank you for your time.