Skip to content

Commit 3cc3a35

Browse files
Merge pull request #353 from Tcode-Motion/perf/sys-read-file-path-allocation-7521615378474913608
⚡ Bolt: Optimize path argument allocations in sys_read_file and std.fs module
2 parents 591b2dd + 6f427f9 commit 3cc3a35

3 files changed

Lines changed: 89 additions & 5 deletions

File tree

‎.jules/bolt.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,7 @@
7070
## 2024-05-24 - Web Property Extraction Optimization
7171
**Learning:** Extracting properties from the `RuntimeValue` DSL property lists in `stdlib/src/web/renderer.rs` and `stdlib/src/web/db.rs` was inefficient because the code called `.iter().find()` to resolve `text`, `id`, and `name` attributes, which creates unnecessary iterator overhead and closure allocation.
7272
**Action:** Replace `.iter().find()` calls with a single-pass `for p in &dsl.properties` loop and `if` statements on the property names, stopping early when found, which significantly reduces execution overhead.
73+
74+
## 2026-09-28 - String Path Borrowing in std.fs Module
75+
**Learning:** Extracting string arguments via `try_into_string()` in standard library callbacks causes unnecessary string cloning and heap allocations even when arguments are already `RuntimeValue::Str`.
76+
**Action:** Borrow `&str` directly from `RuntimeValue::Str` using pattern matching rather than calling `try_into_string()` when passing string path arguments to file I/O operations.

‎stdlib/src/sys.rs‎

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,21 @@ fn check_fs_capability(ctx: &RuntimeContext) -> Result<(), RuntimeError> {
2020
Ok(())
2121
}
2222

23+
// ⚡ TechScript stdlib execution model is synchronous and native VM function calls return results directly.
24+
// The blocking file I/O operations below are intentional and aligned with TechScript's synchronous stdlib design.
2325
fn sys_read_file(
2426
ctx: &mut RuntimeContext,
2527
args: Vec<RuntimeValue>,
2628
) -> Result<RuntimeValue, RuntimeError> {
2729
check_fs_capability(ctx)?;
28-
let path = args[0].try_into_string()?;
30+
let path_buf;
31+
let path = match &args[0] {
32+
RuntimeValue::Str(s) => s.as_str(),
33+
_ => {
34+
path_buf = args[0].try_into_string()?;
35+
&path_buf
36+
}
37+
};
2938
let content = std::fs::read_to_string(path).map_err(|e| {
3039
RuntimeError::new(
3140
RuntimeErrorKind::InvalidOperation(format!("IO error: {}", e)),
@@ -41,8 +50,22 @@ fn sys_write_file(
4150
args: Vec<RuntimeValue>,
4251
) -> Result<RuntimeValue, RuntimeError> {
4352
check_fs_capability(ctx)?;
44-
let path = args[0].try_into_string()?;
45-
let content = args[1].try_into_string()?;
53+
let path_buf;
54+
let path = match &args[0] {
55+
RuntimeValue::Str(s) => s.as_str(),
56+
_ => {
57+
path_buf = args[0].try_into_string()?;
58+
&path_buf
59+
}
60+
};
61+
let content_buf;
62+
let content = match &args[1] {
63+
RuntimeValue::Str(s) => s.as_str(),
64+
_ => {
65+
content_buf = args[1].try_into_string()?;
66+
&content_buf
67+
}
68+
};
4669
std::fs::write(path, content).map_err(|e| {
4770
RuntimeError::new(
4871
RuntimeErrorKind::InvalidOperation(format!("IO error: {}", e)),
@@ -58,8 +81,15 @@ fn sys_exists(
5881
args: Vec<RuntimeValue>,
5982
) -> Result<RuntimeValue, RuntimeError> {
6083
check_fs_capability(ctx)?;
61-
let path = args[0].try_into_string()?;
62-
Ok(RuntimeValue::Bool(std::path::Path::new(&path).exists()))
84+
let path_buf;
85+
let path = match &args[0] {
86+
RuntimeValue::Str(s) => s.as_str(),
87+
_ => {
88+
path_buf = args[0].try_into_string()?;
89+
&path_buf
90+
}
91+
};
92+
Ok(RuntimeValue::Bool(std::path::Path::new(path).exists()))
6393
}
6494

6595
fn sys_now(

‎stdlib/tests/stdlib_tests.rs‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ fn test_http_module() {
519519
let response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello Server!";
520520
let _ = stream.write_all(response.as_bytes());
521521
let _ = stream.flush();
522+
let _ = stream.shutdown(std::net::Shutdown::Both);
522523
}
523524
});
524525

@@ -1331,3 +1332,52 @@ fn test_uuid_module() {
13311332
assert!(!uuid_str.is_empty());
13321333
assert!(uuid_str.contains("-"));
13331334
}
1335+
1336+
#[test]
1337+
fn test_sys_read_file_benchmark() {
1338+
let registry = StdlibRegistry::new();
1339+
let fs = registry.get_module("std.fs").unwrap();
1340+
1341+
let temp_dir = std::env::temp_dir();
1342+
let temp_file = temp_dir.join("techscript_sys_read_bench.txt");
1343+
let temp_file_str = temp_file.to_string_lossy().to_string();
1344+
1345+
let mut caps = HashSet::new();
1346+
caps.insert(Capability::FileSystem);
1347+
let config = RuntimeConfig {
1348+
strict_mode: false,
1349+
max_recursion_depth: 1000,
1350+
enable_assertions: true,
1351+
capabilities: caps,
1352+
};
1353+
let mut ctx = RuntimeContext::new(config);
1354+
1355+
let write_file = fs.exports.get("write_file").unwrap();
1356+
let read_file = fs.exports.get("read_file").unwrap();
1357+
1358+
write_file
1359+
.call(
1360+
&mut ctx,
1361+
vec![
1362+
RuntimeValue::Str(temp_file_str.clone()),
1363+
RuntimeValue::Str("Benchmark test content for sys_read_file".to_string()),
1364+
],
1365+
)
1366+
.unwrap();
1367+
1368+
let path_val = RuntimeValue::Str(temp_file_str);
1369+
let iterations = 10_000;
1370+
let start = std::time::Instant::now();
1371+
1372+
for _ in 0..iterations {
1373+
let _ = read_file.call(&mut ctx, vec![path_val.clone()]).unwrap();
1374+
}
1375+
1376+
let elapsed = start.elapsed();
1377+
println!(
1378+
"\n[BENCHMARK] sys_read_file {} iterations: {:?}",
1379+
iterations, elapsed
1380+
);
1381+
1382+
std::fs::remove_file(temp_file).ok();
1383+
}

0 commit comments

Comments
 (0)