From 599c2fb7d90b47112e0274b6f637076c8218c5ae Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 22 Sep 2026 07:54:18 +0000
Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20property=20extra?=
=?UTF-8?q?ction=20in=20Canvas=20DSL=20rendering?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Replaced multiple $O(N)$ `.iter().find()` calls with a single-pass $O(N)$ loop over the properties array in all Canvas geometric shape rendering functions (`logo`, `rings`, `emblem`, `letter`, `core`, `circuits`). This significantly reduces loop overhead during SVG string construction for the canvas DSL.
Co-authored-by: Tcode-Motion <188012755+Tcode-Motion@users.noreply.github.com>
---
.jules/bolt.md | 3 +
stdlib/src/canvas.rs | 245 +++++++++++++++++++++----------------------
test_perf.rs | 3 +
3 files changed, 125 insertions(+), 126 deletions(-)
create mode 100644 test_perf.rs
diff --git a/.jules/bolt.md b/.jules/bolt.md
index 817bbbb5..ff826971 100644
--- a/.jules/bolt.md
+++ b/.jules/bolt.md
@@ -32,3 +32,6 @@
## 2026-09-22 - String allocation optimization in std.web DSL rendering
**Learning:** Generating deep HTML structures in `std.web` heavily penalized performance because `dsl_to_html` allocated and returned a new `String` for every child DSL node. This causes `O(N)` heap allocations and redundant copying in the render tree. By passing a mutable `&mut String` buffer recursively downwards, we avoid all intermediate string heap allocations and significantly improve serialization speed.
**Action:** Always prefer using a recursive builder pattern passing a single mutable `&mut String` buffer to `write!` or `push_str` when rendering nested tree structures (like HTML, JSON, or ASTs) instead of returning newly allocated strings at each layer.
+## 2024-05-24 - Canvas Property Extraction Optimization
+**Learning:** Extracting properties from the `RuntimeValue` DSL property lists in `canvas.rs` was inefficient because the code called `.iter().find()` multiple times for each property in `logo`, `rings`, `emblem`, `letter`, `core`, and `circuits` definitions.
+**Action:** Replace multiple `.find()` calls with a single-pass `for p in &dsl.properties` loop and `match` on the property names, mutating local fallback variables.
diff --git a/stdlib/src/canvas.rs b/stdlib/src/canvas.rs
index 3c4b3302..ffcc7a20 100644
--- a/stdlib/src/canvas.rs
+++ b/stdlib/src/canvas.rs
@@ -21,27 +21,24 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
if is_dragon {
match dsl.kind.as_str() {
"logo" => {
- let text = dsl
- .properties
- .iter()
- .find(|p| p.name == "text")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "Logo".to_string());
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#00d4ff".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(48);
+ let mut text_val = None;
+ let mut color_val = None;
+ let mut size_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "text" => text_val = p.value.as_ref().map(|v| v.to_string()),
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let text = text_val.unwrap_or_else(|| "Logo".to_string());
+ let color = color_val.unwrap_or_else(|| "#00d4ff".to_string());
+ let size = size_val.unwrap_or(48);
let _ = write!(
svg,
r#"{}"#,
@@ -82,27 +79,24 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
// RENDER OLD GEOMETRIC LOGO
match dsl.kind.as_str() {
"logo" => {
- let text = dsl
- .properties
- .iter()
- .find(|p| p.name == "text")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "Logo".to_string());
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#00d4ff".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(48);
+ let mut text_val = None;
+ let mut color_val = None;
+ let mut size_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "text" => text_val = p.value.as_ref().map(|v| v.to_string()),
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let text = text_val.unwrap_or_else(|| "Logo".to_string());
+ let color = color_val.unwrap_or_else(|| "#00d4ff".to_string());
+ let size = size_val.unwrap_or(48);
let _ = write!(
svg,
r#"{}"#,
@@ -110,34 +104,32 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"rings" => {
- let count = dsl
- .properties
- .iter()
- .find(|p| p.name == "count")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(3);
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#00d4ff".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(40);
- let thickness = dsl
- .properties
- .iter()
- .find(|p| p.name == "thickness")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(3);
+ let mut count_val = None;
+ let mut color_val = None;
+ let mut size_val = None;
+ let mut thickness_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "count" => {
+ count_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ "thickness" => {
+ thickness_val =
+ p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let count = count_val.unwrap_or(3);
+ let color = color_val.unwrap_or_else(|| "#00d4ff".to_string());
+ let size = size_val.unwrap_or(40);
+ let thickness = thickness_val.unwrap_or(3);
for i in 0..count {
let r = 80 + i as i64 * (size / 2);
let opacity = 0.4 - (i as f32 * 0.08);
@@ -149,20 +141,21 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
}
}
"emblem" => {
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#0088cc".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(120);
+ let mut color_val = None;
+ let mut size_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let color = color_val.unwrap_or_else(|| "#0088cc".to_string());
+ let size = size_val.unwrap_or(120);
let x = 250 - size / 2;
let y = 180 - size / 2;
let _ = write!(
@@ -177,27 +170,24 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"letter" => {
- let ch = dsl
- .properties
- .iter()
- .find(|p| p.name == "char")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "T".to_string());
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#0a0e27".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(32);
+ let mut ch_val = None;
+ let mut color_val = None;
+ let mut size_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "char" => ch_val = p.value.as_ref().map(|v| v.to_string()),
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let ch = ch_val.unwrap_or_else(|| "T".to_string());
+ let color = color_val.unwrap_or_else(|| "#0a0e27".to_string());
+ let size = size_val.unwrap_or(32);
let _ = write!(
svg,
r#"{}"#,
@@ -205,20 +195,21 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"core" => {
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#66e0ff".to_string());
- let size = dsl
- .properties
- .iter()
- .find(|p| p.name == "size")
- .and_then(|p| p.value.as_ref())
- .and_then(|v| v.try_into_int().ok())
- .unwrap_or(40);
+ let mut color_val = None;
+ let mut size_val = None;
+
+ for p in &dsl.properties {
+ match p.name.as_str() {
+ "color" => color_val = p.value.as_ref().map(|v| v.to_string()),
+ "size" => {
+ size_val = p.value.as_ref().and_then(|v| v.try_into_int().ok())
+ }
+ _ => {}
+ }
+ }
+
+ let color = color_val.unwrap_or_else(|| "#66e0ff".to_string());
+ let size = size_val.unwrap_or(40);
let _ = write!(
svg,
r#""#,
@@ -227,13 +218,15 @@ fn render_dsl_to_svg(svg: &mut String, val: &RuntimeValue, is_dragon: bool) {
);
}
"circuits" => {
- let color = dsl
- .properties
- .iter()
- .find(|p| p.name == "color")
- .and_then(|p| p.value.as_ref())
- .map(|v| v.to_string())
- .unwrap_or_else(|| "#00d4ff".to_string());
+ let mut color_val = None;
+
+ for p in &dsl.properties {
+ if p.name == "color" {
+ color_val = p.value.as_ref().map(|v| v.to_string());
+ }
+ }
+
+ let color = color_val.unwrap_or_else(|| "#00d4ff".to_string());
let _ = write!(
svg,
r#"
diff --git a/test_perf.rs b/test_perf.rs
new file mode 100644
index 00000000..8093e40b
--- /dev/null
+++ b/test_perf.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Testing performance...");
+}