Swift's native Clocks are inefficient
Clarifying the benchmark results
- Some readers misread the table as 19–30 ms per call; others point out it’s the median time for 1,000,000 iterations, so ~19–30 ns per call for
Date/NSDate. - Swift’s new
Clocktypes are much slower than low-level options, but still in the hundreds of nanoseconds per call, not milliseconds.
Swift timing APIs vs other ecosystems
- Developers coming from C# note that
Stopwatchis simple and precise, whereas Swift has many overlapping options (Clock,DispatchTime,Date, etc.), which feels confusing and sometimes inefficient. - Suggestions include using
CACurrentMediaTime,Date, or directlyclock_gettime/clock_gettime_nsec_npfor performance, though each has trade-offs (dependencies, non-monotonicity).
Platform constraints and standard library versioning
- Questions arise why even a protocol like
Clockis documented as iOS 16+. - Explanation: Swift’s standard library is now shipped with the OS as dynamic libraries; you’re tied to the OS’s Swift version. Apple rarely backports, and Apple’s docs only cover Apple platforms.
- There is some criticism of bundling a language stdlib with the OS, but others note this is analogous to
libcor the C runtime on other systems.
Security, fingerprinting, and high‑resolution timers
mach_absolute_timereturns a high-precision monotonic value (effectively uptime/cycles since boot) and is on Apple’s “naughty list” due to fingerprinting risk.- Discussion explains how uptime plus wall-clock time can identify devices (especially behind NAT), and how precise timers enable micro-benchmark–based fingerprinting.
- Some speculate the inefficiency of Swift clocks might be an intentional Spectre/Meltdown mitigation; others strongly dispute this, noting:
- More efficient APIs (
Date,clock_gettime) remain available. - The stack trace just shows layering/object overhead, not randomized delays.
- Browsers mitigate timing attacks differently (fuzzing results, opt-in precision).
- More efficient APIs (
Performance design and async example
- The article’s example reads time inside
for try await byte in bytes, effectively timing per byte. Multiple commenters note this is intentionally contrived for benchmarking and not how production code should be structured. - Still, the overhead of Swift clocks seems high even compared to expected async overhead.
Alternatives, fixes, and comparisons
- Several recommend just using
clock_gettime(or the_npnanosecond variant) directly for high-performance timing. - Linux benchmarks are shared, showing
clock_gettimevia vDSO in single-digit to low tens of nanoseconds; static linking can shave off more. - A Swift pull request is linked, implying the core team is addressing the inefficiency.