あらすじ: 各言語の clock_gettime(2)
の引数の扱いを調査していたら、このような奇っ怪な文章ができあがっていた。そういう事情であるので、Windows関係の記述がありません。後々追加するかもしれません。
clock_gettime
clock_gettime(2)
はSingle Unix Specification v2によって規格化されている時間取得のためのAPIである。 clock_gettime(2)
には clock_id
に渡す定数によって時刻取得のセマンティクスや精度が変わってくる。
一例をLinuxのmanから引用すると、
- CLOCK_REALTIME
実時間を計測するシステム全体で一意な時間。 このクロックを設定するには適切な特権が必要である。 このクロックは、システム時間の不連続な変化 (例えば、システム管理者が システム時間を手動で変更した場合など) や adjtime や NTP が行う 段階的な調整の影響を受ける。
CLOCK_REALTIME_COARSE (Linux 2.6.32 以降; Linux 特有)
高速だが精度が低い CLOCK_REALTIME。速度が非常に必要で、かつ高精度のタイムスタンプが不要な場合に使用するとよい。
CLOCK_MONOTONIC
設定することができないクロックで、ある開始時点からの単調増加の時間で 表現されるクロック (開始時点がどの時点となるかは規定されていない)。 この時計は、システム時間の不連続な変化 (例えば、システム管理者がシステ ム時間を手動で変更した場合など) の影響を受けないが、 adjtime(3) や NTP が行う段階的な調整の影響を受ける。
CLOCK_MONOTONIC_COARSE (Linux 2.6.32 以降; Linux 特有)
高速だが精度が低い CLOCK_MONOTONIC。速度が非常に必要で、かつ高精度のタイムスタンプが不要な場合に使用するとよい。
CLOCK_MONOTONIC_RAW (Linux 2.6.28 以降; Linux 特有)
CLOCK_MONOTONIC と同様だが、NTP による調整や adjtime(3) が行う 段階的な調整の影響を受けない、ハードウェアによる生の時刻へのアクセス ができる。
CLOCK_BOOTTIME (Linux 2.6.39 以降; Linux 固有)
CLOCK_MONOTONIC と同じだが、システムがサスペンドされている時間も含まれる点が異なる。 これを使うと、アプリケーションはサスペンド状態も扱える "monotonic" なクロックを得ることができる。 しかも、 CLOCK_REALTIME における複雑な処理を行う必要もなくなる。 CLOCK_REALTIME では、 settimeofday(2) を使って時刻を変更した場合、時刻に不連続な変化が発生するからだ。
とある。この定数はOSによってまちまちである。またsierraのように clock_gettime_nsec_np
という特殊なAPIを追加しているものもある。
こちらのブログは実装の引用箇所が参考になった。 http://d.hatena.ne.jp/yohei-a/20140913/1410628229
Ruby
- CRuby 2.4.0
Process.clock_gettime
Rubyは直接 clock_gettime(2)
を呼ぶAPIを提供している。Process.clock_gettime の引数に clock_id
を指定する。
指定できるのはprocess.cによると以下の定数。ちゃんとどのバージョンで追加されたのかも書いており大変すばらしい。SolarisのCLOCK_HIGHRESに関しては指定がなかった。またDragonflyBSDも少なくとも直近のバージョンではFreeBSDと同じオプションを使用できるはずであるが言及がなかった。
* [CLOCK_REALTIME] SUSv2 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 2.1, macOS 10.12
* [CLOCK_MONOTONIC] SUSv3 to 4, Linux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4, macOS 10.12
* [CLOCK_PROCESS_CPUTIME_ID] SUSv3 to 4, Linux 2.5.63, OpenBSD 5.4, macOS 10.12
* [CLOCK_THREAD_CPUTIME_ID] SUSv3 to 4, Linux 2.5.63, FreeBSD 7.1, OpenBSD 5.4, macOS 10.12
* [CLOCK_VIRTUAL] FreeBSD 3.0, OpenBSD 2.1
* [CLOCK_PROF] FreeBSD 3.0, OpenBSD 2.1
* [CLOCK_REALTIME_FAST] FreeBSD 8.1
* [CLOCK_REALTIME_PRECISE] FreeBSD 8.1
* [CLOCK_REALTIME_COARSE] Linux 2.6.32
* [CLOCK_REALTIME_ALARM] Linux 3.0
* [CLOCK_MONOTONIC_FAST] FreeBSD 8.1
* [CLOCK_MONOTONIC_PRECISE] FreeBSD 8.1
* [CLOCK_MONOTONIC_COARSE] Linux 2.6.32
* [CLOCK_MONOTONIC_RAW] Linux 2.6.28, macOS 10.12
* [CLOCK_MONOTONIC_RAW_APPROX] macOS 10.12
* [CLOCK_BOOTTIME] Linux 2.6.39
* [CLOCK_BOOTTIME_ALARM] Linux 3.0
* [CLOCK_UPTIME] FreeBSD 7.0, OpenBSD 5.5
* [CLOCK_UPTIME_FAST] FreeBSD 8.1
* [CLOCK_UPTIME_RAW] macOS 10.12
* [CLOCK_UPTIME_RAW_APPROX] macOS 10.12
* [CLOCK_UPTIME_PRECISE] FreeBSD 8.1
* [CLOCK_SECOND] FreeBSD 8.1
Time.now
最もよく使われるであろう Time.now
というか Time#initialize
で呼び出される現在時刻はclock_gettimeが定義されていれば CLOCK_REALTIME
固定で呼び出されている。ない場合はgettimeofday(macOSなど)を使っている。
ドキュメントには書かれていないが、内部でclock_gettime/gettimeofdayが失敗した場合は rb_sys_fail
で例外を出す。
Python3 (3.3以降)
- CPython 3.6.0
time.clock_gettime
Pythonも3.3以降ではRubyと同様に time.clock_gettime(clock_id)
で直接 clock_gettime(2)
を呼ぶことができる。ただしclock_idに渡すことのできるものはアンドキュメンテッドである上に、どうやら CLOCK_MONOTONIC_COARSE
などの定数は渡すことができない(3.6.0現在)。そもそもPythonは内部で CLOCK_MONOTONIC_COARSE
を使ったり外から渡すAPIを提供していないようだ。
pytime.cをみるとpymonotonic関数で CLOCK_MONOTONIC
、pygettimeofday関数で CLOCK_REALTIME
固定でそれぞれ使われている。pymonotonicではmacOSの場合 mach_absolute_time
を、Solarisの場合 CLOCK_HIGHRES
を渡している。
time.time
最もよく使われるであろうtime.time()
は pygetimeofday
関数を内部で呼んでおり、これは time.clock_gettime(CLOCK_REALTIME)
と等価となる。RubyのTime.now同様にfallback先としてgettimeofday(2)を使っている。
システムコール呼び出しに失敗した場合は内部でNULLを返しているがよくわからん。
pep418
Python3.3でtimeモジュールに新設されたclock系の提案PEPはこれ https://www.python.org/dev/peps/pep-0418/
Perl5
- Perl 5.24
本体に付属?しているTime::HiResモジュールがデファクトのようだ。
clock_gettime
clock_gettime
に CLOCK_MONOTONIC
や CLOCK_REALTIME
を渡せる。Ruby,Python同様、clock_gettimeはOS側で定義されていない場合は生えないのでgettimofday(2)にfallbackする仕組みはない。現時点で最新のmacOS Sierraで新設されたAPIには対応してなかったが、FreeBSDの CLOCK_MONOTONIC_FAST
などは定数として定義されている。
time
time関数は内部ではgettimeofday(2)を呼んでいる。gettimeofday(2)の呼び出しに失敗した場合に-1.0を返すようだ。いったいこれはどういうことだろうと考えたところ、システムコールの失敗ステータスをfloatとして表現しているのだろうと思い至った。Perlには直和型も直積型も例外もないのでこのように返り値の意味が混在した表現にするよりないのだろう。
めんどくさくなったので雑になった。
C++
- gcc6.2
STLにあるstd::chrono(libstdc++-v3/src/c++11/chrono.cc) をみる。
std::chrono::system_clock::now
- clock_gettime(2)があれば
CLOCK_REALTIME
を使う - なければ gettimeofday(2) を使う
- それもなければ time(2) を使う
std::chrono::steady_clock::now
- clock_gettime(2)があれば
CLOCK_MONOTONIC
を使う - なければ std::chrono::system_clock を使って差分を求める
となっている。
Go
- Go 1.8rc2
多くのプラットフォーム(Linux,FreeBSD,OpenBSD)では、各アーキテクチャごとに now
の実装に CLOCK_REALTIME
、 nanotime
の実装に CLOCK_MONOTONIC
を使っている。NetBSD,Solarisに関してはnanotimeの実装にも CLOCK_REALTIME
を用いているが、これはよくわからない。Plan9はなんかよくわからない。
静的リンクなバイナリという事情があるためか、macOSでは事情が込み入っている。しかしこちらもよくわからず。参照されていたURLだけはっておく。 https://opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/i386/cpu_capabilities.h
FreeBSDでは 8-STABLEのサポートを切れば CLOCK_MONOTONIC_FAST
を使えるとあるが、Linuxでは CLOCK_MONOTONIC_COARSE
を2.6.32以前のサポートを切ればを使えるということは書かれていなかった。
// func now() (sec int64, nsec int32)
TEXT time·now(SB), NOSPLIT, $32
MOVL $232, AX // clock_gettime
MOVQ $0, DI // CLOCK_REALTIME
LEAQ 8(SP), SI
SYSCALL
MOVQ 8(SP), AX // sec
MOVQ 16(SP), DX // nsec
// sec is in AX, nsec in DX
MOVQ AX, sec+0(FP)
MOVL DX, nsec+8(FP)
RET
TEXT runtime·nanotime(SB), NOSPLIT, $32
MOVL $232, AX
// We can use CLOCK_MONOTONIC_FAST here when we drop
// support for FreeBSD 8-STABLE.
MOVQ $4, DI // CLOCK_MONOTONIC
LEAQ 8(SP), SI
SYSCALL
MOVQ 8(SP), AX // sec
MOVQ 16(SP), DX // nsec
// sec is in AX, nsec in DX
// return nsec in AX
IMULQ $1000000000, AX
ADDQ DX, AX
MOVQ AX, ret+0(FP)
RET
Rust
- 1.16.0-nightly
標準ライブラリstd::timeにおいて多くのプラットフォームでは Instant::now
は CLOCK_MONOTONIC
固定、 SystemTime::now
は CLOCK_REALTIME
固定となっている。macOSに関してはそれぞれ mach_absolute_time
/ gettimeofday
を使用している。Rubyのソースのコメントからみるに CLOCK_MONOTONIC
はLinux 2.5.63, FreeBSD 3.0, NetBSD 2.0, OpenBSD 3.4 (そしていつからかは不明だがここ最近のDragonflyBSD) で問題なく動くはずである。システムコール呼び出しに失敗した場合panicする。
Instant::now
mach_absolute_time (macOS/iOS)
CLOCK_MONOTONIC (Linux/Android)
SystemTime::now
gettimeofday (macOS/iOS)
CLOCK_REALTIME (Linux/Android)
rust-coarsetime crate
精度より速度が欲しい場合はrust-coarsetime が使える。rust-coarsetimeはLinux/OSX/FreeBSD(DragonflyBSD)においてそれぞれ CLOCK_MONOTONIC_COARSE
/ CLOCK_MONOTONIC_RAW_APPROX
/ CLOCK_MONOTONIC_FAST
を使っている。
D言語
- phobos (dmd-2.073.0)
時刻取得のAPIとして標準ライブラリPhobosの std.datetime に Clock.currTime
/ Clock.currStdTime
があり、ClockTypeを指定することができる。いずれも内部的には currStdTime関数を呼んでいる。システムコール呼び出しに失敗した場合TimeException例外を送出する。
ClockTypeの指定によるcurrStdTime各OSごとの実装は以下の表にまとめる。Sierraの新APIサポートがなかったりここの分岐にないOS(例えばOpenBSD)はコンパイル時にUnsupported OSとして死ぬ。
ここで time(2)/gettimeofday(2) はそれぞれシステムコールを表し、定数っぽいやつは clock_gettime(2) の引数に渡される定数である。
- ClockType.second
time(2) (Linux)
time(2) (OSX)
CLOCK_SECOND (FreeBSD 8.1)
time(2) (NetBSD)
time(2) (Solaris)
- ClockType.coarse
CLOCK_REALTIME_COARSE (Linux 2.6.32)
gettimeofday(2) (OSX)
CLOCK_REALTIME_FAST (FreeBSD 8.1)
gettimeofday(2) (NetBSD)
CLOCK_REALTIME (Solaris)
- ClockType.normal
CLOCK_REALTIME (Linux 2.5.63)
gettimeofday(2) (OSX)
CLOCK_REALTIME (FreeBSD 3.0)
gettimeofday(2) (NetBSD)
CLOCK_REALTIME (Solaris)
- ClockType.precise
CLOCK_REALTIME (Linux 2.5.63)
gettimeofday(2) (OSX)
CLOCK_REALTIME_PRECISE (FreeBSD 8.1)
gettimeofday(2) (NetBSD)
CLOCK_REALTIME (Solaris)