プログラミング言語における時刻取得APIまわりの話

あらすじ: 各言語の `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など)を使っている。

[ドキュメント](http://ruby-doc.org/core-2.4.0/Time.html#method-c-now)には書かれていないが、内部で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

1. clock_gettime(2)があれば `CLOCK_REALTIME` を使う
2. なければ gettimeofday(2) を使う
3. それもなければ time(2) を使う

### std::chrono::steady_clock::now

1. clock_gettime(2)があれば `CLOCK_MONOTONIC` を使う
2. なければ 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以前のサポートを切ればを使えるということは書かれていなかった。

```asm
// 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](https://github.com/jedisct1/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)`