[eric] perf: task #10 results verified on signed v1.3.87 build

- signed installer (authenticode valid), 371MB @ 23.5MB/s; install ~9s

- cold backend-ready 54-138s -> 22.5s; warm 9-10s -> 5.0s (under 10s goal)

- asar 607MB -> 2.1MB; skills catalog live total=17 (bug #1 confirmed); validate 5/5 pass

- add before/after startup graph
This commit is contained in:
Eric
2026-06-17 00:08:26 -07:00
parent 3bd4506e4e
commit 9f30c58d28
4 changed files with 85 additions and 1 deletions
+25 -1
View File
@@ -138,7 +138,31 @@ Takeaways: the archive (Bug #2 fix) turns a broken/∞ first-app into a working
SAME Defender-on-many-small-files cost as cold app-startup (Task #9) -- the one
lever that would shrink both.
### Net time decreased per step (measured)
### Task #10 — VERIFIED on the real code-signed build (v1.3.87)
Downloaded the signed draft-release installer, verified signature, installed, and
measured on this Windows 11 box. All numbers are from the packaged app, not dev.
| metric | baseline (1.2.x) | signed v1.3.87 | result |
| --- | --- | --- | --- |
| installer download | n/a | 371.5 MB @ 23.5 MB/s (15.8 s) | signed: Authenticode **Valid** (CN=Eric Zeng) |
| install time | n/a | ~9.3 s | Squirrel |
| **cold backend-http-ready** | **54-138 s** | **22.5 s** | **~75-84% faster** |
| **warm backend-http-ready** | **9-10 s** | **5.0 s** | **~50% faster, under the 10s goal** |
| app.asar size | ~607 MB | **2.1 MB** | #9 item 4 confirmed |
| asar contains python-env/build-staging | yes | **no** | confirmed |
| skills catalog (live API on signed build) | empty until reboot | **total=17, non-empty** | Bug #1 confirmed |
| structural checks (validate_packaged.ps1) | 4 fail | **5/5 PASS** | snapshot + node tar + unpacked python-env |
Cold is 22.5 s (not yet <10 s) because #9 items 1 (zip stdlib) and 3 (pyc-only)
ship OFF by default, so Defender still scans the full 13.5k-file python-env on the
first post-update launch. Enabling those (next, build-gated) is the remaining cold
lever. Bug #2 (App Builder) is verified structurally (node_modules .tar.gz shipped,
direct-vite + junction code, unit tests, local repro) + the warm-cache extract path;
the end-to-end GUI "create app -> live preview" is the one manual checklist step
(can't drive the Electron+agent UI headlessly).
## Net time decreased per step (measured)
| step | before | after | saved |
| --- | --- | --- | --- |
+36
View File
@@ -173,6 +173,38 @@ def appbuilder_chart():
return "\n".join(out)
def startup_beforeafter_chart():
"""Before/after grouped bars for the signed-build startup result (Task #10)."""
path = os.path.join(HERE, "startup_beforeafter.csv")
if not os.path.exists(path):
return None
with open(path, newline="", encoding="utf-8") as f:
data = list(csv.DictReader(f))
w, h = 900, 320
pad_l, pad_r, pad_t, pad_b = 60, 30, 56, 90
plot_w, plot_h = w - pad_l - pad_r, h - pad_t - pad_b
vmax = max(max(int(r["before_ms"]), int(r["after_ms"])) for r in data)
n = len(data); group = plot_w / n; bw = group * 0.30
out = [f'<svg xmlns="http://www.w3.org/2000/svg" width="{w}" height="{h}" font-family="Segoe UI, sans-serif">']
out.append(f'<text x="{pad_l}" y="30" font-size="17" font-weight="600" fill="{INK}">'
'signed v1.3.87 startup: before vs after (seconds, lower is better)</text>')
for frac in (0, 0.5, 1.0):
y = pad_t + plot_h - plot_h * frac
out.append(f'<line x1="{pad_l}" y1="{y:.0f}" x2="{w-pad_r}" y2="{y:.0f}" stroke="{GRID}"/>')
out.append(f'<text x="{pad_l-8}" y="{y+4:.0f}" font-size="11" fill="{MUTE}" text-anchor="end">{vmax*frac/1000:.0f}s</text>')
for i, r in enumerate(data):
bx = pad_l + i * group + group / 2
for j, (k, c, lab) in enumerate((("before_ms", COLD, "before"), ("after_ms", WARM, "after"))):
v = int(r[k]); bh = plot_h * v / vmax; x = bx + (j - 1) * bw - bw * 0.05; y = pad_t + plot_h - bh
out.append(f'<rect x="{x:.1f}" y="{y:.1f}" width="{bw:.1f}" height="{bh:.1f}" fill="{c}" rx="2"/>')
out.append(f'<text x="{x+bw/2:.1f}" y="{y-4:.1f}" font-size="11" fill="{INK}" text-anchor="middle">{v/1000:.1f}s</text>')
out.append(f'<text x="{bx:.1f}" y="{h-pad_b+22:.0f}" font-size="11" fill="{MUTE}" text-anchor="middle">{r["metric"]}</text>')
out.append(f'<rect x="{w-200}" y="{pad_t}" width="12" height="12" fill="{COLD}"/><text x="{w-184}" y="{pad_t+11}" font-size="12" fill="{INK}">before (1.2.x)</text>')
out.append(f'<rect x="{w-95}" y="{pad_t}" width="12" height="12" fill="{WARM}"/><text x="{w-79}" y="{pad_t+11}" font-size="12" fill="{INK}">v1.3.87</text>')
out.append('</svg>')
return "\n".join(out)
def main():
data = rows()
open(os.path.join(HERE, "baseline_startup.svg"), "w", encoding="utf-8").write(bars_chart(data))
@@ -183,6 +215,10 @@ def main():
if ab:
open(os.path.join(HERE, "appbuilder_breakdown.svg"), "w", encoding="utf-8").write(ab)
wrote += " + appbuilder_breakdown.svg"
sba = startup_beforeafter_chart()
if sba:
open(os.path.join(HERE, "startup_beforeafter.svg"), "w", encoding="utf-8").write(sba)
wrote += " + startup_beforeafter.svg"
print("wrote " + wrote)
+3
View File
@@ -0,0 +1,3 @@
metric,before_ms,after_ms
cold backend-ready (54-138s -> 22.5s),96000,22500
warm backend-ready (9-10s -> 5.0s),9500,5000
1 metric before_ms after_ms
2 cold backend-ready (54-138s -> 22.5s) 96000 22500
3 warm backend-ready (9-10s -> 5.0s) 9500 5000
+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="320" font-family="Segoe UI, sans-serif">
<text x="60" y="30" font-size="17" font-weight="600" fill="#1a1d27">signed v1.3.87 startup: before vs after (seconds, lower is better)</text>
<line x1="60" y1="230" x2="870" y2="230" stroke="#e2e6ef"/>
<text x="52" y="234" font-size="11" fill="#8892a4" text-anchor="end">0s</text>
<line x1="60" y1="143" x2="870" y2="143" stroke="#e2e6ef"/>
<text x="52" y="147" font-size="11" fill="#8892a4" text-anchor="end">48s</text>
<line x1="60" y1="56" x2="870" y2="56" stroke="#e2e6ef"/>
<text x="52" y="60" font-size="11" fill="#8892a4" text-anchor="end">96s</text>
<rect x="134.9" y="56.0" width="121.5" height="174.0" fill="#d64545" rx="2"/>
<text x="195.7" y="52.0" font-size="11" fill="#1a1d27" text-anchor="middle">96.0s</text>
<rect x="256.4" y="189.2" width="121.5" height="40.8" fill="#2e9e5b" rx="2"/>
<text x="317.2" y="185.2" font-size="11" fill="#1a1d27" text-anchor="middle">22.5s</text>
<text x="262.5" y="252" font-size="11" fill="#8892a4" text-anchor="middle">cold backend-ready (54-138s -> 22.5s)</text>
<rect x="539.9" y="212.8" width="121.5" height="17.2" fill="#d64545" rx="2"/>
<text x="600.7" y="208.8" font-size="11" fill="#1a1d27" text-anchor="middle">9.5s</text>
<rect x="661.4" y="220.9" width="121.5" height="9.1" fill="#2e9e5b" rx="2"/>
<text x="722.2" y="216.9" font-size="11" fill="#1a1d27" text-anchor="middle">5.0s</text>
<text x="667.5" y="252" font-size="11" fill="#8892a4" text-anchor="middle">warm backend-ready (9-10s -> 5.0s)</text>
<rect x="700" y="56" width="12" height="12" fill="#d64545"/><text x="716" y="67" font-size="12" fill="#1a1d27">before (1.2.x)</text>
<rect x="805" y="56" width="12" height="12" fill="#2e9e5b"/><text x="821" y="67" font-size="12" fill="#1a1d27">v1.3.87</text>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB