Skip to content

Commit 4cd939a

Browse files
committed
Improve ParseError::from_error_kind() performance
According to the flamegraph produced by the following command: cat large-yarn.nix | cargo flamegraph -p nix-parser --example viewer The most siginificant amount of time and resources being spent parsing this very large Nix expression is actually in the error handling code, specifically in `<Errors as ParseError>::from_error_kind()` and `<Errors as ParseError>::append()`, mostly in the conversion of the input fragment into a heap-allocated string for `Error::Nom`. The specific call dominating the flamegraph was `core::fmt::write::hbe8b9e0d6296c582`. Since we are already preserving the span of the input fragment, there technically is no need to preserve the source text itself since we can retrieve it later in `Error::to_diagnostic()`. Removing the `String` payload from the `Error::Nom` variant and removing all occurrences of `.into()` and `.to_string()` where `Error::Nom` occurs nets a massive performance win on my MacBook Pro, shown by the Criterion benchmark below: ``` parse example.nix time: [722.02 us 724.88 us 729.24 us] change: [-53.820% -51.759% -48.149%] (p = 0.00 < 0.05) Performance has improved. Found 10 outliers among 100 measurements (10.00%) 3 (3.00%) high mild 7 (7.00%) high severe ``` This is a reduction from an average time of 1.5558 ms on `cargo bench`. Additionally, the time spent parsing the `large-yarn.nix` file above was reduced drastically as well: ``` $ cat large-yarn.nix | time cargo run -p nix-parser --example viewer # Old 1667.33 real 1262.42 user 10.53 sys $ cat large-yarn.nix | time cargo run -p nix-parser --example viewer # New 0.87 real 0.52 user 0.05 sys ```
1 parent d0cfe90 commit 4cd939a

File tree

2 files changed

+6
-19
lines changed

2 files changed

+6
-19
lines changed

nix-parser/src/error.rs

+5-14
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ where
133133
{
134134
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
135135
Errors {
136-
errors: vec![Error::Nom(input.to_span(), input.to_string(), kind)],
136+
errors: vec![Error::Nom(input.to_span(), kind)],
137137
}
138138
}
139139

140140
fn append(input: I, kind: ErrorKind, mut other: Self) -> Self {
141-
other.push(Error::Nom(input.to_span(), input.to_string(), kind));
141+
other.push(Error::Nom(input.to_span(), kind));
142142
other
143143
}
144144
}
@@ -149,27 +149,18 @@ pub enum Error {
149149
IncorrectDelim(IncorrectDelimError),
150150
UnclosedDelim(UnclosedDelimError),
151151
Unexpected(UnexpectedError),
152-
Nom(Span, String, ErrorKind),
152+
Nom(Span, ErrorKind),
153153
Message(Span, String),
154154
}
155155

156-
impl Error {
157-
pub fn as_nom_error(&self) -> Option<(Span, &str, ErrorKind)> {
158-
match *self {
159-
Error::Nom(ref span, ref frag, ref kind) => Some((*span, frag, *kind)),
160-
_ => None,
161-
}
162-
}
163-
}
164-
165156
impl Display for Error {
166157
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
167158
match *self {
168159
Error::ExpectedFound(ref e) => write!(fmt, "{}", e),
169160
Error::IncorrectDelim(ref e) => write!(fmt, "{}", e),
170161
Error::UnclosedDelim(ref e) => write!(fmt, "{}", e),
171162
Error::Unexpected(ref e) => write!(fmt, "{}", e),
172-
Error::Nom(_, ref frag, ref e) => write!(fmt, "nom error: (\"{}\", {:?})", frag, e),
163+
Error::Nom(_, ref e) => write!(fmt, "nom error: {:?}", e),
173164
Error::Message(_, ref e) => write!(fmt, "{}", e),
174165
}
175166
}
@@ -208,7 +199,7 @@ impl ToDiagnostic for Error {
208199
Error::IncorrectDelim(ref e) => e.to_diagnostic(file),
209200
Error::Unexpected(ref e) => e.to_diagnostic(file),
210201
Error::UnclosedDelim(ref e) => e.to_diagnostic(file),
211-
Error::Nom(ref span, ref _frag, ref kind) => {
202+
Error::Nom(ref span, ref kind) => {
212203
let label = Label::new(file, *span, self.to_string());
213204
let mut diag = Diagnostic::new_bug(format!("nom error: {:?}", kind), label);
214205
let note = "note: this indicates an unhandled case in the parser".to_string();

nix-parser/src/lexer/lexers/number.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ pub fn float(input: LocatedSpan) -> IResult<Token> {
2424
Ok((remaining, Token::Float(float, span.to_span())))
2525
} else {
2626
let mut errors = Errors::new();
27-
errors.push(Error::Nom(
28-
input.to_span(),
29-
input.fragment.into(),
30-
ErrorKind::Float,
31-
));
27+
errors.push(Error::Nom(input.to_span(), ErrorKind::Float));
3228
Err(nom::Err::Error(errors))
3329
}
3430
}

0 commit comments

Comments
 (0)