oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

Native replica typing probe — 2026-09-13

This bounded review experiment exercises the real CRDTReplica actor and its statically linked WASM host. It is not a UI responsiveness, device, release-build, or web/native performance parity benchmark. No production algorithm was changed. The executable was an arm64 macOS Swift debug test build. Other integration work was running on the machine, so these single samples are directional evidence.

| Mode | Characters | Elapsed seconds | Snapshot bytes | Total emitted update bytes | Largest update bytes | Client IDs | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | Fresh presentation after every character | 100 | 1.221 | 1,635 | 1,802 | 27 | 101 | | Fresh presentation after every character | 1,000 | 26.734 | 15,901 | 17,867 | 27 | 1,001 | | Fresh presentation after every character | 5,000 | stopped, no completed sample | — | — | — | — | | Fresh presentation after each 100-character batch | 100 | 0.014 | 159 | 127 | 127 | 2 | | Fresh presentation after each 100-character batch | 1,000 | 0.120 | 1,192 | 1,178 | 127 | 11 | | Fresh presentation after each 100-character batch | 5,000 | 0.673 | 5,792 | 5,858 | 127 | 51 | | One retained source, individual character commands | 100 | 0.137 | 159 | 1,810 | 28 | 2 | | One retained source, individual character commands | 1,000 | 1.813 | 1,060 | 19,755 | 28 | 2 | | One retained source, individual character commands | 5,000 | 11.442 | 5,060 | 99,755 | 28 | 2 |

The fresh-source mode acknowledges each presentation immediately and retires the old generation before the next publication. The retained-source control never publishes another presentation, approximating a burst whose callbacks still refer to one displayed generation; it is not a proposed safe replacement algorithm. Batch mode inserts 100 ASCII characters per command and then presents. All modes start with one paragraph and assert the resulting text length at every milestone. Wall time includes actor commands, internal snapshots/projections, source retention, and (where specified) presentations and their selection-anchor resolution. It excludes initial open/paragraph creation, network, native text layout, and rendering. Milestone snapshot/client queries add a little work to subsequent cumulative times.

Per-character new client IDs materially increase work and persisted CRDT overhead in this workload. At 1,000 characters the fresh-source snapshot is 15,901 bytes, about 0.19% of the 8 MiB bridge boundary. The bridge does not expose an encoded state-vector command, so exact vector bytes were not measured. For these fresh workloads, client IDs and clocks fit unsigned 32-bit varints: a conservative encoded-vector bound is 5 + 10 * clientCount bytes (five bytes each for ID and clock plus five for count). That is at most 10,015 bytes for the measured 1,001 clients. This is a bound, not an observed size or a long-document capacity claim. Latency becomes material well before the measured byte boundary. Rich formatting, large documents, long sessions, peer updates, and device memory limits remain unmeasured; batching is not validated here as a correctness-preserving fix.

The first 5,000 fresh-generation run was stopped after roughly four minutes to release SwiftPM's build lock for integration. SwiftPM buffered its XCTest output, so no completed 5,000 result or intermediate metrics were recovered. The smaller fresh-source sample and both controls were then run directly with xctest, which does not hold the SwiftPM lock and emits milestones immediately. A separate 100 character sample took 1.244 seconds with a 1,638-byte snapshot/101 clients; small size differences reflect randomly assigned client IDs.

Repeating the probe

NativePerformanceProbeTests.swift is intentionally outside the normal test source tree. Temporarily copy it into native/apple/Tests/DocsKitTests/, build the test bundle once with a 100-character limit, and remove the copied source after measurement. Use the actual built test-bundle path for your machine:

DOCS_PROBE_MODE=fresh DOCS_PROBE_COUNT=100 swift test --package-path native/apple --filter NativePerformanceProbeTests
DOCS_PROBE_MODE=fresh DOCS_PROBE_COUNT=1000 /Applications/Xcode.app/Contents/Developer/usr/bin/xctest -XCTest DocsKitTests.NativePerformanceProbeTests/testTypingProbe native/apple/.build/arm64-apple-macosx/debug/DocsNativePackageTests.xctest

Set mode to batch or retained and count to 5000 for the controls. Run modes sequentially and bound runtime. Recorded executable SHA-256: 3ce4222fa0981fc444f85bb858cdebca921eecf26c5eb50d4e44657ed62b2485. Original local logs: /private/tmp/docs-probe-fresh1000.log, /private/tmp/docs-probe-batch.log, /private/tmp/docs-probe-retained.log.

Acknowledged identical-source reuse followup

After the independently reviewed presentation(..., allowReuse: true) change, mode reuse explicitly acknowledges the current published generation before every next character and requests reuse. Every character still performs a presentation and selection-anchor resolution. Only the same live source with byte-identical committed state can be returned; this does not batch away commands or render steps.

| Characters | Elapsed seconds | Snapshot bytes | Total update bytes | Largest update | Client IDs | | --- | ---: | ---: | ---: | ---: | ---: | | 100 | 0.329 | 159 | 1,810 | 28 | 2 | | 1,000 | 3.303 | 1,060 | 19,755 | 28 | 2 | | 5,000 | 30.845 | 5,060 | 99,755 | 28 | 2 |

The 1,000-character sample falls from 26.734 to 3.303 seconds (about 8.1 times faster in these individual runs), with client count bounded at two. The 5,000 sample now completes, but there is no completed pre-change 5,000 timing to form a ratio. It remains slower than the retained-source control, which omits all presentation/anchor work, and much slower than 100-character batches. These data support reduced source churn; they do not establish a device latency budget or solve the cost of projecting and snapshotting long documents on every keystroke. The source-reuse regression separately proves peer divergence and undo still publish fresh generations and preserve peer content, and default/no-acknowledgement calls still use fresh sources.

This followup was also measured amid integration builds. Its initial separate 100-character compile-phase sample took 2.727 seconds, versus 0.329 seconds in the direct run, demonstrating machine-load sensitivity. The table is the single direct run's cumulative milestones, not selected best timings. Direct log: /private/tmp/docs-probe-reuse5000.log. Repeat using DOCS_PROBE_MODE=reuse. Followup executable SHA-256: b08998210ac83bec46a8cb2d62c51c0e2792b0c65e647f52fa58eaee879a3c55.