agentsclimarketplace

Swift auth performance benchmarks

Skill esaldgut/ai-native-engineering-workspace/global-skills/apple-auth/swift-auth-performance-benchmarks

Write performance benchmarks for an iOS auth subsystem using ContinuousClock — measure token-refresh latency at p50/p95 with hand-rolled percentiles (Swift Testing ships no percentile helper), verify cold-start Keychain-read + JWT-decode stays under budget, and prove concurrent-refresh coalescence costs ~1x not Nx. Uses ContinuousClock.measure for sync blocks and the now/duration form for async, with first-sample warmup discarding. Use when adding or reviewing latency budgets for auth.From its SKILL.md

Install
npx -y skills add esaldgut/ai-native-engineering-workspace --skill swift-auth-performance-benchmarks

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.4 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

Auth performance benchmarks (ContinuousClock + Swift Testing)

Auth latency is felt: a slow token refresh stalls every gated request, and a slow cold-start auth check delays first paint. This skill measures those paths with ContinuousClock — the monotonic, high-resolution clock that (unlike Date()) is immune to NTP/user clock changes — and computes percentiles by hand, because Swift Testing ships no percentile helper. Budgets are configurable per environment; the examples use realistic OAuth/OIDC envelopes.

When to invoke

  • You're setting or enforcing latency budgets for token refresh, cold-start auth, or Keychain access.
  • You want a CI guard that catches a regression (a synchronous Keychain call sneaking onto the hot path, a refresh that lost its coalescing).
  • You need to prove concurrent-refresh coalescence is real — that 16 callers cost ~1× a single refresh, not 16×.

Announce on invoke: "Using swift-auth-performance-benchmarks to add ContinuousClock p50/p95 latency tests for the auth subsystem."

Do not reach for XCTest here. ContinuousClock keeps these in Swift Testing; the only reason to fall back to XCTest is when you specifically need an Apple-shipped XCTMetric probe (CPU/memory counters), which is a different measurement.

The canonical APIs (verified)

APIForm (verified)Use
ContinuousClocklet clock = ContinuousClock()Monotonic stopwatch; iOS 16+. Not affected by system clock changes.
Clock.measure(_:)func measure(_ work: () throws -> Void) rethrows -> DurationTime a synchronous block.
async measureawait clock.measure { await … } (measure(isolation:_:))Time an async block; or use the now / duration(to:) form below.
clock.now + duration(to:)let start = clock.now; …; let d = start.duration(to: clock.now)Explicit, async-safe interval; unambiguous across overloads.
Duration.milliseconds(50), <Express and compare budgets.
percentile(hand-rolled — sort + index)Swift Testing has no built-in percentile.

The rules

1. Hand-roll percentiles — there is no built-in

Collect N samples (100 for p50/p95; 1000 for a stable p99), sort(), then index: p50 = samples[count/2], p95 = samples[Int(Double(count - 1) * 0.95)]. Don't assume an XCTMeasureOptions-style statistic exists in Swift Testing — it doesn't.

2. Discard warmup samples

First-run effects (class load, code-sign cache, lazy URLSession setup) inflate the first few measurements. Drop the first 3–5 samples before computing percentiles, or the p50 is a lie.

3. ContinuousClock, not Date()

Date() can jump backward on NTP sync, producing negative or absurd intervals. ContinuousClock is monotonic — the only correct primitive for a benchmark.

4. Budgets are configurable, and network-tier-aware

p50 < 1 s / p95 < 3 s is a reasonable LTE envelope for OAuth refresh; on Wi-Fi expect p50 < 400 ms / p95 < 1 s. Cold-start Keychain read + JWT decode should be < 50 ms (Keychain reads are ~1–5 ms; decode is sub-ms for tokens < 4 KB). Read the budget from config so CI, dev, and device can differ.

5. Prove coalescence by wall-clock AND call-count

A coalescence test must assert both: the fan-out's wall-clock ≈ 1× single-refresh latency (not N×), and the network mock observed exactly 1 call. Either alone can pass while the bug hides.

6. Keep RSS measurement out of unit tests

Process-RSS probes (task_info / TASK_VM_INFO) are brittle in unit tests. For a static check use MemoryLayout<T>.size ("the token model is < 256 bytes"); for real memory profiling use Instruments.

Canonical example

import Testing
import Foundation
@testable import MyApp

@Suite("Auth performance")
struct AuthPerformanceTests {

    /// Hand-rolled percentile — Swift Testing has none built in.
    private func percentile(_ p: Double, of samples: [Duration]) -> Duration {
        let sorted = samples.sorted()
        return sorted[Int(Double(sorted.count - 1) * p)]
    }

    @Test func tokenRefreshLatencyMeetsBudget() async throws {
        let clock = ContinuousClock()
        let session = AuthSession()
        var samples: [Duration] = []
        for i in 0..<105 {
            let start = clock.now
            _ = try? await session.refresh()
            let elapsed = start.duration(to: clock.now)   // async-safe interval
            if i >= 5 { samples.append(elapsed) }          // discard 5-sample warmup
        }
        #expect(percentile(0.50, of: samples) < .milliseconds(1000))
        #expect(percentile(0.95, of: samples) < .milliseconds(3000))
    }

    @Test func coldStartKeychainAndDecodeUnderBudget() {
        let clock = ContinuousClock()
        let elapsed = clock.measure {                       // synchronous block
            _ = SecureTokenStore(service: "app", account: "user").readBlocking()
            _ = JWTDecoder.decode(Self.sampleJWT)
        }
        #expect(elapsed < .milliseconds(50))
    }

    @Test func concurrentRefreshIsCoalesced() async {
        let clock = ContinuousClock()
        let session = AuthSession()
        let elapsed = await clock.measure {                 // async measure
            await withTaskGroup(of: Void.self) { group in
                for _ in 0..<16 { group.addTask { _ = try? await session.refresh() } }
            }
        }
        #expect(elapsed < .milliseconds(1500))              // ~1x, NOT 16x
        #expect(session.networkCallCount == 1)              // exactly one network refresh
    }

    @Test func tokenModelStaysSmall() {
        #expect(MemoryLayout<AuthSession.Token>.size < 256) // static check, not RSS
    }
}

Decision aid: where to put a benchmark

  • Latency of an async auth call → Swift Testing + ContinuousClock, percentiles over ≥100 samples.
  • Static size invariantMemoryLayout<T>.size, no clock needed.
  • CPU/memory counters over a flow → XCTest XCTMetric (the one place to leave Swift Testing).
  • Coalescence → wall-clock budget and mock call-count, together.

Related skills

  • global-skills/apple-auth/swift-auth-security-audit-suite/SKILL.md — the correctness counterpart: it asserts coalescence is single-flight; this skill asserts it's also fast.
  • global-skills/apple-auth/swift-testing-framework-conventions-mvvm/SKILL.md — when to stay in Swift Testing vs fall back to XCTest's XCTMetric.
  • global-skills/apple-auth/swift-auth-security-checklist/SKILL.md — the single-flight refresh design whose latency this measures.

Sources


Last verified: 2026-06-03 against Swift ContinuousClock / Clock.measure / Duration (Apple Developer docs, live; sync measure and the async measure(isolation:_:) overload both confirmed). Swift Testing ships no percentile helper — percentiles are hand-rolled here. Re-check after: WWDC26 + any CryptoKit/Security release, or by 2026-12-01. Decay risk: low (ContinuousClock is a stable Swift primitive). Found a drift? Run /skill-pattern-freshness-audit apple-auth.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most performance cost skills give in ~1.9k tokens

Counted across 803 of the 1,058 authors here whose files we hold, read 2026-08-07

  • Keep skill files under 500 lines or tokensin 82 of 803, across 16 files
  • Use imperative form in instructionsin 80 of 803, across 9 files
  • Draft assertions while test runs are in progressin 75 of 803, across 9 files
  • Create two to three realistic test promptsin 74 of 803, across 9 files
  • Write skill descriptions to be pushyin 72 of 803, across 7 files
  • Save test cases to evals JSONin 72 of 803, across 6 files
  • Ask questions about edge cases and input formatsin 72 of 803, across 7 files
  • Save timing data immediately when runs completein 70 of 803, across 5 files
  • Include all trigger conditions in the skill descriptionin 69 of 803, across 3 files
  • Launch all test runs in a single turn or simultaneouslyin 69 of 803, across 3 files
  • Capture intent before writing a skillin 67 of 803, across 1 file
  • Import directly instead of barrel filesin 52 of 803, across 15 files

Said here and by no other author read

  • announce skill usage on invoke
  • use ContinuousClock instead of Date
  • hand-roll percentile calculations
  • discard warmup samples
  • read latency budgets from configuration
  • assert wall-clock time for coalescence

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.