# Issue #12: reproducible standalone bookmark/probe benchmark This benchmark checks where the synchronous bookmark resolution and metadata-format probes execute, and whether a MainActor heartbeat is prevented from running while 3,000 files are checked. It reuses the new `EmbeddedMetadataEditabilityChecker.swift` unchanged, with minimal extracted dependencies, and compares it with the old synchronous bulk-sheet filter and `LibraryService` functions from commit `ca98ef4dd63c3f098bb72f76bf54247e18f0063f`. ## Inputs and environment - macOS 27.0.1 (26A434), arm64, Xcode 27.0 (27A266a), Swift 6.4, `swiftc -swift-version 6 -O`. - 3,000 real file copies on the local temporary filesystem: 750 each of the repository's `tag-test.flac`, `tag-test.wav`, `tag-test.ogg`, and `tag-test.opus` fixtures. They contain repeated fixture audio, rather than 3,000 different songs. - Every copy has a distinct path, native security-scoped bookmark, and filesystem inode. Preparation asserts that bookmark resolution returns the copied file, all probes return editable, and all 3,000 inode numbers are distinct. - Bookmark creation and a probe run during preparation warm the filesystem. Every scenario runs three times, alternating the before/after order. The application build was running concurrently, so these timings include uncontrolled scheduling and system contention. - `environment.txt` records the toolchain; `manifest.json` records baseline commit, full original-source hashes, extracted-source hashes, and fixture hashes. The benchmark's machine CPU brand could not be obtained through sandboxed `sysctl`. ## What is measured The old path calls the original synchronous `resolvedURL` and `canEditEmbeddedMetadata` bodies inside the original `Set(items.filter { ... }.map(\.id))` selection expression on MainActor. The service class name and its fallback-directory provider are replaced by standalone equivalents; the function bodies are copied unchanged. The local files resolve successfully, so fallback-directory behavior is outside this benchmark. The new path maps the same MainActor model stand-ins to value snapshots and awaits the unchanged checker. Injected resolver/probe closures count calls, then call the checker's production static resolver/predicate. The extracted production predicates include the original `AdditionalAudioMetadata.canWrite` and `OggMetadataWriter` first-page parsing, bounds checks, and CRC check, plus the original small `MediaFileRewriter.read` and `XiphMetadata.uint32` helpers. Read/write metadata implementation outside these predicates is excluded. Counters record `Thread.isMainThread` before each resolution and format-probe call. In the old synchronous function there are no suspension points between the instrumentation and the original resolution/probe. These are function-call counts, not syscall counts or Instruments measurements. MainActor model stand-ins contain the same ID, bookmark data, and filename fields but exclude SwiftData faulting and observation cost. A separate MainActor task repeatedly requests a 1 ms `Task.sleep`. Its recorded delay is the actual interval between samples minus 1 ms. It is allowed to start before each measurement and to record any blocked sample after the operation finishes. Its maximum delay includes runtime scheduling and system contention; it does not measure frame rendering. The snapshot duration measures only copying IDs/bookmarks/filenames; it is not the total cost of opening the application sheet. ## Results Every run resolved/probed 3,000 files and returned 3,000 editable IDs. The old path recorded 3,000 bookmark resolutions and 3,000 metadata-probe calls on the main thread; the new path recorded zero of each on the main thread, with all calls performed on the detached worker. Values below are the observed min–max across three runs. Full per-run values are in `results.json`; medians are in `summary.json`. | Scenario | Path | Total completion | Maximum MainActor heartbeat delay | MainActor snapshot copy | | --- | --- | ---: | ---: | ---: | | Warm local files | Before | 884.8–937.5 ms | 884.6–939.2 ms | N/A | | Warm local files | After | 896.1–2141.7 ms | 1.1–20.2 ms | 0.106–0.120 ms | | Requested 1 ms latency per probe | Before | 6224.6–8503.7 ms | 6224.5–8503.5 ms | N/A | | Requested 1 ms latency per probe | After | 4804.1–6564.9 ms | 1.2–19.1 ms | 0.114–0.167 ms | The latency scenario explicitly injects `Thread.sleep(forTimeInterval: 0.001)` immediately before each probe in both paths. It is a controlled representation of a slow blocking probe, not a measurement of network or external storage. Scheduler timer coalescing can make each requested sleep exceed 1 ms, explaining why totals exceed a simple 3-second sum. The evidence supports removing the synchronous selection-wide bookmark/probe work from the main thread. It does not establish improved total throughput or a maximum frame-time guarantee; the new path still performs the same work and observed delays can exceed one 60 Hz frame under contention. ## Reproduce Run on macOS outside an execution sandbox that prevents security-scoped bookmark creation: ```sh ./run.sh /absolute/path/to/simple-media-player /tmp/issue12-benchmark-rerun ``` The repository must contain the fixture bytes listed in `manifest.json`. `run.sh` compiles the saved sources, creates 3,000 fixture copies in the chosen output's `media` directory, and writes `results.json`. It recreates that directory on each run. It does not build or modify the application repository. If desired, `extract.py REPOSITORY ca98ef4dd63c3f098bb72f76bf54247e18f0063f` recreates the original baseline extraction and copies the current checker. Omitting the commit argument uses `main`; verify the checker hash against `manifest.json` before comparing reruns. Archive these text files together for reproducibility: `README.md`, `run.sh`, `extract.py`, `Benchmark.swift`, `EmbeddedMetadataEditabilityChecker.swift`, `PredicateExtracts.swift`, `OldLibraryService.swift`, `OldLoopSource.txt`, `manifest.json`, `environment.txt`, `results.json`, and `summary.json`. The generated binary, module cache, and 3,000 media copies are disposable and should not be added to the Wiki. ## Limits This is a standalone macOS concurrency benchmark. It does not import a 3,000-song SwiftData library, open the full SwiftUI metadata sheet, measure application rendering or Instruments CPU/energy traces, exercise iPhone/iPad, or verify the sheet's cancellation and generation-guard behavior. Those require separate application tests. Values are from a warm local temporary filesystem with repeated small fixtures; real storage, bookmark failures, larger Ogg first pages, and model faults can change timings.