Optimized C++ で色々ある Windows の時刻取得関数の精度を測っているのを見て iOS の時刻取得関数を測ってみました。通常、Swift で時間を扱う時には Date/NSDate を使いますが、iOS ではこのほかに UNIX 由来の C の関数を使って時刻を取得できます。
Java や C# と違ってラッパーコードを書かずに C 言語の関数を直接呼べるのは Swift のいいところですね。
time()
1970/1/1 00:00:00 からの経過秒数を取得します。Swift から呼ぶとこんな感じです。
let t = Double(time(nil))
仕様により精度は 1 秒です。
getimeofday()
1970/1/1 00:00:00 からの経過秒数をマイクロ秒の精度で取得します。
var tv = timeval() // これは関数呼び出しではなくイニシャライザ
gettimeofday(&tv, nil)
let t = Double(tv.tv_sec) + Double(tv.tv_usec) / 1000000.0
clock_gettime()
この関数は実時間、プロセスの CPU 時間、スレッドの CPU 時間など取得する時間の種類を指定できます。性能計測のため実時間を取得するならこのようになります。UNIX では古くからある関数ですが、iOS で使えるようになったのは iOS10 からです。
var tp = timespec() // これもイニシャライザ
clock_gettime(_CLOCK_REALTIME, &tp)
let t = Double(tp.tv_sec) + Double(tp.tv_nsec) / 1000000000.0
clock_gettime_nsec_np()
clock_gettime() の結果を 64bit 整数として取得できます。
let t = Double(clock_gettime_nsec_np(_CLOCK_REALTIME)) / 1000000000.0
mach_absolute_time()
CPU のタイムスタンプカウンタ(CPU が一定周期でインクリメントするレジスタ)の値を取得します。他より高精度です。
var tb = mach_timebase_info() // イニシャライザ
mach_timebase_info(&tb) // こっちは関数呼び出し
let tsc = mach_absolute_time()
let t = Double(tsc) * Double(tb.numer) / Double(tb.denom) / 1000000000.0
精度は CPU 依存なので定数を掛けたり割ったりしても秒に変換できません。まず、mach_timebase_info()
でタイムスタンプカウンタの値をナノ秒に変換するための値を取得して変換します。
iOS の生い立ちを感じさせる名前ですね。
Date/NSDate
これは説明不要ですね。Swift 標準の時刻を表す型です。
gettimeofday()
にシンボリックブレークポイントを設定して Date
の引数なしイニシャライザを呼んでみるとそのブレークポイントにヒットします。Date
は gettimeofday()
で実装されていることがわかります。したがって同じ精度を持ちます。
実測
Optimized C++ での測り方で各関数の精度を測ってみました。環境は iPhone 6s、iOS 10.2.1、Xcode 8.2.1 です。
関数/型 | 精度 |
---|---|
time() | 1.00s |
gettimeofday() | 1.01μs |
clock_gettime() | 1.00μs |
clock_gettime_nsec_np() | 1.01μs |
mach_absolute_time() | 41.7ns |
Date | 1.09μs |
time()
は仕様通り 1s、mach_absolute_time()
は圧倒的に高精度で、あとはだいたい 1μs という結果になりました。clock_gettime()
, clock_gettime_nsec_np()
は 1ns の精度を持っていそうな型名、関数名をしていますが、少なくとも今の iOS の実装では違うようです。
また iPad Mini 4 で測ってみると mach_absolute_time()
は 76.7ns であとはだいたい一緒でした。
通常のプログラムの実行時間計測では 1ms の精度があれば十分なので使いやすい Date
を使えばよく、本当に精度が必要な時だけ mach_absolute_time()
を使えばよさそうです。
計測に使ったコードは https://github.com/yamoridon/TimeResolution に置いておきますね。