perf: parallelize daily/weekly lock queries in list command

- Modified `src/lib.mjs` to execute `queryLocks` for all cadences in parallel.
- Added `scripts/perf-benchmark.mjs` for measuring CLI performance.
- Added `weekly-perf-report-2026-02-14.md` with baseline and verification data.
- Added `task-logs/weekly/2026-02-14T13-14-38Z__perf-deepdive-agent__completed.md`.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-14 13:17:27 +00:00
1 parent c731f2b09d
commit 9716fe3812
6 files changed
+147 -1

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
Benchmarking "node bin/torch-lock.mjs list" (5 iterations)...
Run 1: 15199.04ms
Run 2: 15186.28ms
Run 3: 15188.74ms
Run 4: 15191.66ms
Run 5: 15184.86ms
Results:
Count: 5
Average: 15190.12ms
Min: 15184.86ms
Max: 15199.04ms
+12
View File
@@ -0,0 +1,12 @@
Benchmarking "node bin/torch-lock.mjs list" (5 iterations)...
Run 1: 15775.77ms
Run 2: 15773.07ms
Run 3: 15800.54ms
Run 4: 15770.65ms
Run 5: 16013.24ms
Results:
Count: 5
Average: 15826.65ms
Min: 15770.65ms
Max: 16013.24ms
+57
View File
@@ -0,0 +1,57 @@
import { spawn } from 'node:child_process';
import { performance } from 'node:perf_hooks';
const ITERATIONS = 5;
const COMMAND = 'node';
const ARGS = ['bin/torch-lock.mjs', 'list'];
async function runOnce(i) {
return new Promise((resolve, reject) => {
const start = performance.now();
const child = spawn(COMMAND, ARGS, { stdio: 'ignore' });
child.on('close', (code) => {
const duration = performance.now() - start;
if (code === 0) {
console.log(`Run ${i + 1}: ${duration.toFixed(2)}ms`);
resolve(duration);
} else {
reject(new Error(`Process exited with code ${code}`));
}
});
child.on('error', (err) => reject(err));
});
}
async function main() {
console.log(`Benchmarking "${COMMAND} ${ARGS.join(' ')}" (${ITERATIONS} iterations)...`);
const times = [];
for (let i = 0; i < ITERATIONS; i++) {
try {
const duration = await runOnce(i);
times.push(duration);
} catch (err) {
console.error(`Run ${i + 1} failed:`, err);
}
}
if (times.length === 0) {
console.error('No successful runs.');
process.exit(1);
}
const sum = times.reduce((a, b) => a + b, 0);
const avg = sum / times.length;
const min = Math.min(...times);
const max = Math.max(...times);
console.log('\nResults:');
console.log(` Count: ${times.length}`);
console.log(` Average: ${avg.toFixed(2)}ms`);
console.log(` Min: ${min.toFixed(2)}ms`);
console.log(` Max: ${max.toFixed(2)}ms`);
}
main().catch(console.error);
+6 -1
View File
@@ -439,9 +439,14 @@ export async function cmdList(cadence) {
console.error(`Listing active locks: namespace=${namespace}, cadences=${cadences.join(', ')}`);
for (const c of cadences) {
const results = await Promise.all(
cadences.map(async (c) => {
const locks = await queryLocks(relays, c, dateStr, namespace);
return { c, locks };
}),
);
for (const { c, locks } of results) {
console.log(`\n${'='.repeat(72)}`);
console.log(`Active ${namespace} ${c} locks (${dateStr})`);
console.log('='.repeat(72));
@@ -0,0 +1,20 @@
---
agent: perf-deepdive-agent
status: completed
date: 2026-02-14
---
# Weekly Performance Deep Dive
## Summary
- **Scenario**: Listing active locks (`torch-lock list`), which involves querying relays for both daily and weekly cadences.
- **Optimization**: Parallelized the relay queries in `src/lib.mjs`.
- **Baseline Duration**: ~15.8s
- **Post-Optimization Duration**: ~15.2s
- **Improvement**: ~4%
## Analysis
The performance is dominated by a 15s timeout on one of the queries. Parallelization ensures that the total duration is determined by the slowest query, rather than the sum of sequential query times (which would be ~30s if both timed out).
## Artifacts
- `weekly-perf-report-2026-02-14.md`: Detailed report with benchmark data.
+40
View File
@@ -0,0 +1,40 @@
# Weekly Performance Report - 2026-02-14
## Scenario
Running `node bin/torch-lock.mjs list` to list active locks for both daily and weekly cadences.
This command executes two sequential queries to Nostr relays.
## Baseline (Before Optimization)
Measured using `scripts/perf-benchmark.mjs` (5 iterations).
- **Average Duration**: 15826.65ms
- **Min Duration**: 15770.65ms
- **Max Duration**: 16013.24ms
The command appears to be network-bound, likely due to sequential execution of relay queries for each cadence.
## After Optimization
Modified `src/lib.mjs` to execute relay queries in parallel using `Promise.all`.
- **Average Duration**: 15190.12ms
- **Min Duration**: 15184.86ms
- **Max Duration**: 15199.04ms
## Analysis
The speedup is approximately 4% (636ms).
While theoretically this should parallelize the latency, the observed improvement is small.
This suggests that either:
1. The relays are responding very quickly but one query dominates the time (e.g. hitting a 15s timeout).
2. The `SimplePool` instantiation overhead is significant.
3. Network bandwidth or local resource contention limits the concurrency gains.
Given the timeout in `src/lib.mjs` is 15000ms, and the duration is just over 15000ms, it is highly likely that at least one query is timing out.
If both queries were timing out sequentially, the duration would be ~30s.
Since sequential was ~16s, it implies one query timed out (15s) and the other was very fast (<1s), OR they overlapped in some way (unlikely in sequential).
If one query takes 15s (timeout) and the other takes 0.5s:
- Sequential: 15s + 0.5s = 15.5s.
- Parallel: max(15s, 0.5s) = 15s.
This matches the data perfectly.
Conclusion: One cadence query (likely 'daily' or 'weekly' specifically) is timing out or very slow, while the other is fast. Parallelization ensures we pay the "slowest" cost, not the sum.