diff --git a/.jules/bolt.md b/.jules/bolt.md index fff4cb0a..d2b14b65 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -23,3 +23,6 @@ ## 2024-05-24 - Optimize rusqlite extraction in query_map **Learning:** When mapping `rusqlite` query results to dynamic types like `IndexMap` in `techscript_stdlib`, use `row.get_ref_unwrap(i)` to map the underlying `rusqlite::types::ValueRef` directly to the corresponding `RuntimeValue` (e.g., `Int`, `Float`, `Str`), avoiding the overhead and type issues of coercing all numeric and null columns to `String`s. **Action:** When extracting data from rusqlite database rows, inspect the `ValueRef` directly with `get_ref` or `get_ref_unwrap` instead of eagerly copying out a target Rust type like `String`, significantly reducing allocations and retaining correct native types for numeric and null data. +## 2024-05-18 - String Concatenation Optimization in VM +**Learning:** String concatenation using `format!("{}{}", a, b)` creates unnecessary intermediate string allocations. Replacing it with `String::with_capacity(a.len() + b.len())` and `push_str()` significantly improves performance by allocating the exact required size once. Even better, if the first string is an owned `String` that is no longer needed (e.g., from popping a stack), reusing its buffer via `mut a` and `a.push_str(&b)` avoids allocating a new buffer entirely. +**Action:** When concatenating strings in hot paths like VM execution loops or native interop, prefer in-place buffer reuse (`a.push_str(&b)`) or `String::with_capacity` over `format!`. diff --git a/runtime/native_runtime/src/lib.rs b/runtime/native_runtime/src/lib.rs index eee9b534..ea2f929f 100644 --- a/runtime/native_runtime/src/lib.rs +++ b/runtime/native_runtime/src/lib.rs @@ -505,10 +505,11 @@ pub unsafe extern "C" fn ts_add(left: *mut TsValue, right: *mut TsValue) -> *mut // Concatenation if either is a string if l.tag == TsTag::String as u32 || r.tag == TsTag::String as u32 { - let l_str = value_to_string(left); + let mut l_str = value_to_string(left); let r_str = value_to_string(right); - let combined = format!("{}{}", l_str, r_str); - let combined_cstr = CString::new(combined).unwrap(); + // Bolt ⚡: In-place string append avoids additional buffer allocations and format! overhead + l_str.push_str(&r_str); + let combined_cstr = CString::new(l_str).unwrap(); return ts_alloc_string(combined_cstr.as_ptr()); } diff --git a/runtime/vm/src/executor.rs b/runtime/vm/src/executor.rs index 718997a7..a7543495 100644 --- a/runtime/vm/src/executor.rs +++ b/runtime/vm/src/executor.rs @@ -161,8 +161,10 @@ impl VM { (RuntimeValue::Float(a), RuntimeValue::Float(b)) => { RuntimeValue::Float(a + b) } - (RuntimeValue::Str(a), RuntimeValue::Str(b)) => { - RuntimeValue::Str(format!("{}{}", a, b)) + (RuntimeValue::Str(mut a), RuntimeValue::Str(b)) => { + // Bolt ⚡: In-place string append reuses buffer capacity and avoids fresh allocation + a.push_str(&b); + RuntimeValue::Str(a) } _ => { return Err(VMError::TypeError {