diff --git a/.jules/bolt.md b/.jules/bolt.md index aca28d8..64b6e1b 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -9,3 +9,6 @@ ## 2024-08-14 - [Rust String Parsing - Whitespace Semantics Regression] **Learning:** When optimizing whitespace scanning in Rust parsing loops by replacing `.char_indices()` with byte-level ASCII checks (e.g., `as_bytes().iter().position(|b| b.is_ascii_whitespace())`), it can introduce a subtle functional regression. Rust's `char::is_whitespace()` matches all Unicode whitespace characters (like non-breaking spaces), whereas `is_ascii_whitespace()` only matches standard ASCII whitespace. **Action:** When exact Unicode semantics must be preserved while optimizing, use `.find()` (e.g., `text.find(char::is_whitespace)`) instead of dropping down to byte-level operations. This leverages internal optimizations while preserving the exact semantic meaning of the original code. +## 2026-08-16 - [Rust String Concatenation and Display Formatting Overhead] +**Learning:** In hot paths like SQL compilation and AST formatting, relying on `.join()` and intermediate `.map().collect::>().join()` operations causes substantial unnecessary heap allocation for short-lived Strings and Vecs. +**Action:** When concatenating multiple strings or formatting values iteratively in Rust, iterate directly over the items and write directly to the buffer or `Formatter` rather than building intermediate `Vec`s and using `.join()`. This reduces memory overhead and improves performance. diff --git a/compiler/rockql-ast/src/lib.rs b/compiler/rockql-ast/src/lib.rs index 52b099a..d873460 100644 --- a/compiler/rockql-ast/src/lib.rs +++ b/compiler/rockql-ast/src/lib.rs @@ -64,17 +64,32 @@ impl Display for Transform { match self { Self::From { source } => write!(formatter, "from {source}"), Self::Filter { expression } => write!(formatter, "filter {expression}"), - Self::Select { columns } => write!(formatter, "select {}", columns.join(", ")), + Self::Select { columns } => { + // ⚡ Bolt Optimization: Prevent intermediate String and Vec allocation + // when formatting selected columns. + write!(formatter, "select ")?; + for (index, column) in columns.iter().enumerate() { + if index > 0 { + write!(formatter, ", ")?; + } + write!(formatter, "{}", column)?; + } + Ok(()) + } Self::Derive { name, expression } => { write!(formatter, "derive {name} = {expression}") } Self::Sort { items } => { - let values = items - .iter() - .map(ToString::to_string) - .collect::>() - .join(", "); - write!(formatter, "sort {{{values}}}") + // ⚡ Bolt Optimization: Prevent temporary vectors and string allocations + // by directly writing sort items to the formatter. + write!(formatter, "sort {{")?; + for (index, item) in items.iter().enumerate() { + if index > 0 { + write!(formatter, ", ")?; + } + write!(formatter, "{}", item)?; + } + write!(formatter, "}}") } Self::Take { count } => write!(formatter, "take {count}"), } diff --git a/compiler/rockql-sql/src/lib.rs b/compiler/rockql-sql/src/lib.rs index 2baee83..dbdf53e 100644 --- a/compiler/rockql-sql/src/lib.rs +++ b/compiler/rockql-sql/src/lib.rs @@ -123,14 +123,28 @@ pub fn compile(query: &Query, dialect: Dialect) -> Result { sql.push('\n'); if !filters.is_empty() { + // ⚡ Bolt Optimization: Avoid intermediate String allocation from `.join()` + // by pushing directly into the output buffer. sql.push_str("WHERE "); - sql.push_str(&filters.join("\n AND ")); + for (index, filter) in filters.iter().enumerate() { + if index > 0 { + sql.push_str("\n AND "); + } + sql.push_str(filter); + } sql.push('\n'); } if !sort_items.is_empty() { + // ⚡ Bolt Optimization: Avoid intermediate String allocation from `.join()` + // by pushing directly into the output buffer. sql.push_str("ORDER BY "); - sql.push_str(&sort_items.join(", ")); + for (index, item) in sort_items.iter().enumerate() { + if index > 0 { + sql.push_str(", "); + } + sql.push_str(item); + } sql.push('\n'); }