From 9886d4f1f51a6f0671d68ac3ba115e02b27ed474 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 6 Nov 2026 21:59:36 -0700 Subject: The wallet paints its balance without deep-copying the whole ark state five to seven times on every launch. "Its so slow to load … I'm using the apk. Compared to Minibits or something its way slower" (a user, 2026-11-05): profiled on Adam's Pixel 9 over DevTools, a launch kept the main thread pegged for ~1.7 s, and 1.4 s of it was loadArkState — every arkStateNow() during the first render (balance, history, notices, the Spending figures) structured-cloned the stored state, ~1 MB on a wallet with deep coins, ~220 ms per copy, before the manager connected and its live state took over. ArkStore.peek() hands back the stored object itself (before the store opens, or on localStorage, the parse is kept until the key is next written); wallet.peekArkState() is loadArkState through it, with the misfiled-state repair still going through loadArkState and each object's owner decoded once; arkStateNow and arkWanted read through it — read-only, as they already were with the live ark.state. Same boot on the phone, live tab with the local build: main-thread busy over the first 4 s 3.7 s → 1.8 s, no pegged stretch. tools/ark-store-test.js: peek returns the same object, sees writes at once, and drops a kept localStorage parse on write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/ark/store.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/ark') diff --git a/src/ark/store.js b/src/ark/store.js index b1caa94..24d8f55 100644 --- a/src/ark/store.js +++ b/src/ark/store.js @@ -139,6 +139,7 @@ export class ArkStore { } } this.ready = true; + this._parsed = null; // peeks now read the opened store if (migrated.length) { this.flush().then(() => { for (const k of migrated) { try { ls.removeItem(k); } catch {} } }).catch(() => {}); } @@ -153,13 +154,32 @@ export class ArkStore { const v = this.mem.get(key); return v ? clone(v) : null; } + // The stored object itself, not a copy — for readers that only look (the + // render path). get() deep-copies, and a big wallet's state is ~1 MB: on a + // Pixel 9 each copy took ~220 ms and the first render asked five to seven + // times before the manager connected, 1.4 s of every launch. Never mutate + // what this returns. Before the store opens (or on localStorage) the parse + // is kept until the key is next written. + peek(key) { + if (!this.ready || this.lsMode) { + const hit = this._parsed && this._parsed.get(key); + if (hit !== undefined) return hit; + let v = null; + try { const raw = this.ls().getItem(key); v = raw ? JSON.parse(raw) : null; } catch { v = null; } + (this._parsed ||= new Map()).set(key, v); + return v; + } + return this.mem.get(key) || null; + } set(key, obj) { + if (this._parsed) this._parsed.delete(key); if (!this.ready || this.lsMode) { try { this.ls().setItem(key, JSON.stringify(obj)); } catch {} return; } this.mem.set(key, obj); this.dirty.add(key); this._schedule(); } remove(key) { + if (this._parsed) this._parsed.delete(key); if (!this.ready || this.lsMode) { try { this.ls().removeItem(key); } catch {} return; } this.mem.delete(key); this.dirty.add(key); -- cgit v1.2.3