Mac in the headset: final benchmark numbers; relay delay is a delay line

Review round 8 (GPT-6 Astra xhigh): --delay paused upstream reads, so busy
streams saw 30-60 ms instead of 30. Verified locally: 60-62 ms round trip
with 30 ms each way under load. No saved result used --delay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-28 21:02:26 +10:00
1 parent 9e4dcdd147
commit 00b45bafc5
3 files changed
+1211 -5

No files matched your search

File diff suppressed because it is too large. Load diff
+15
View File
@@ -196,6 +196,21 @@ Baseline on 2026-09-28 (**verified**, home Wi-Fi, Tailscale, Balanced,
| scroll (1920×1290) | 14.7 | – | 50.4 | encode 6.7, decode 6.4 (software), 9.4 Mbit/s, 16% late | | scroll (1920×1290) | 14.7 | – | 50.4 | encode 6.7, decode 6.4 (software), 9.4 Mbit/s, 16% late |
| type (1920×1290) | 16.7 | 51.2 / 73.2 | – | most of the input time is the Mac app reacting | | type (1920×1290) | 16.7 | 51.2 / 73.2 | – | most of the input time is the Mac app reacting |
After this work, with the controller on (**verified**, same setup,
`bench/results/2026-09-28-9e4dcdd-final.json`; ms p50/p95). Content is
now measured from the earlier of display time and delivery, which adds
about 5 ms to scroll compared with the baseline's way of measuring:
| Scenario | Content | Input to drawn | fps drawn | Grades |
|---|---|---|---|---|
| test | 10.5 / 16.7 | 29.6 / 36.3 | 60 | all within target |
| scroll | 19.8 / 27.4 | – | 55.9 | fps, late frames (4.8%) and worst gap (222 ms) only "acceptable": Wi-Fi stalls and ScreenCaptureKit's 46–53 fps from virtual displays |
| type | 15.0 / 21.4 | 43.0 / 60.5 | – | all within target |
Of the targets, click to photon is met without the Frame's compositor (the
headset has to be worn to measure its share), and so is content latency.
The frame-rate and no-stall targets aren't yet met while scrolling.
What was learned (all **verified**, unless marked): What was learned (all **verified**, unless marked):
- The biggest costs are encoding (4–7 ms), network (4–6 ms), and decoding - The biggest costs are encoding (4–7 ms), network (4–6 ms), and decoding
+23 -5
View File
@@ -153,12 +153,30 @@ class Relay:
w.close() w.close()
async def _plain(self, reader, writer): # Frame -> agent: delay only async def _plain(self, reader, writer): # Frame -> agent: delay only
if not self.delay:
while data := await reader.read(65536):
writer.write(data)
await writer.drain()
writer.close()
return
# Each chunk leaves `delay` after it arrived, while reading goes on:
# a delay line, not a pause (which would add up behind a busy stream).
queue = asyncio.Queue()
async def send():
while (item := await queue.get())[1] is not None:
wait = item[0] - self.loop.time()
if wait > 0:
await asyncio.sleep(wait)
writer.write(item[1])
await writer.drain()
writer.close()
sender = asyncio.ensure_future(send())
while data := await reader.read(65536): while data := await reader.read(65536):
if self.delay: await queue.put((self.loop.time() + self.delay, data))
await asyncio.sleep(self.delay) await queue.put((0, None))
writer.write(data) await sender
await writer.drain()
writer.close()
async def _shaped(self, reader, writer): # agent -> Frame async def _shaped(self, reader, writer): # agent -> Frame
queue = asyncio.Queue() queue = asyncio.Queue()