From 0f3667f6bc69872ca31182caa7b4cd4fa82a36c9 Mon Sep 17 00:00:00 2001 From: SayanthRock <202829406+SayanthRock@users.noreply.github.com> Date: Wed, 12 Aug 2026 22:09:58 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= =?UTF-8?q?=20optimize=20string=20splitting=20and=20whitespace=20scanning?= =?UTF-8?q?=20in=20rockql-parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace `.char_indices()` with `.match_indices('|')` to avoid UTF-8 decoding overhead when splitting pipeline segments. - Replace `.char_indices().find_map(...)` with `.as_bytes().iter().position(...)` to directly scan ASCII whitespace, avoiding string decoding. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .jules/bolt.md | 3 +++ compiler/rockql-parser/src/lib.rs | 27 +++++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 99f4cf5..cb255b7 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -2,3 +2,6 @@ **Learning:** In tight parsing loops in Rust (e.g., `normalize_keywords` and `remove_numeric_separators`), using `.chars().collect::>()` introduces severe performance overhead due to O(N) memory allocation per operation. Similarly, repeatedly allocating new `String` instances for word extraction and `.to_ascii_lowercase()` within loops causes significant heap traffic. **Action:** When performing string transformations or extractions in high-frequency functions, prefer slicing directly with `&str`, checking bytes directly (e.g. `value.as_bytes()`) where possible (such as when scanning for single-byte ASCII characters), and using non-allocating comparison methods like `eq_ignore_ascii_case()` instead of allocating a lowercase string. Always avoid `.chars().collect::>()` when a streaming iterator (`chars()` or `as_bytes()`) suffices. +## 2024-08-07 - [Rust String Decoding Overhead] +**Learning:** Using `.char_indices()` to search for ASCII characters (like `|` or whitespace) introduces unnecessary UTF-8 decoding overhead for every character in the string, which slows down tight parsing loops. +**Action:** Use `.match_indices()` for exact char/string matches, and `.as_bytes().iter().position(...)` for searching by byte predicates (e.g. `b.is_ascii_whitespace()`), bypassing UTF-8 decoding for ASCII boundaries. diff --git a/compiler/rockql-parser/src/lib.rs b/compiler/rockql-parser/src/lib.rs index 04e1d44..5e56f6b 100644 --- a/compiler/rockql-parser/src/lib.rs +++ b/compiler/rockql-parser/src/lib.rs @@ -70,16 +70,16 @@ fn split_segments(source: &str) -> Vec { for (line_index, line) in source.lines().enumerate() { let mut start = 0; - for (byte_index, character) in line.char_indices() { - if character == '|' { - push_segment( - &mut segments, - &line[start..byte_index], - line_index + 1, - start, - ); - start = byte_index + character.len_utf8(); - } + // ⚡ Bolt Optimization: Use `match_indices` instead of `char_indices` + // to avoid decoding overhead for every character in the string. + for (byte_index, _) in line.match_indices('|') { + push_segment( + &mut segments, + &line[start..byte_index], + line_index + 1, + start, + ); + start = byte_index + 1; // '|' is exactly 1 byte } push_segment(&mut segments, &line[start..], line_index + 1, start); @@ -102,9 +102,12 @@ fn push_segment(segments: &mut Vec, raw: &str, line: usize, byte_start: } fn parse_transform(text: &str, span: Span) -> Result { + // ⚡ Bolt Optimization: Use byte iteration to find the first ASCII whitespace, + // avoiding UTF-8 decoding overhead in `.char_indices()`. let keyword_end = text - .char_indices() - .find_map(|(index, character)| character.is_whitespace().then_some(index)) + .as_bytes() + .iter() + .position(|&b| b.is_ascii_whitespace()) .unwrap_or(text.len()); let keyword = &text[..keyword_end];