アキヤマと申します。
Windowsでの時刻同期精度について、教えて頂きたく投稿させて
頂きました。
#質問するグループが適切ではないかもしれませんが、
#その場合には、ご容赦ください。
一般に、Windowsでの時刻管理(制御)は、LinuxやFreeBSDと比較すると、
あまり精度の良いものではない、ということをよく耳にするのですが、
それは、なぜなのでしょうか?
たとえば、GPS+NTPソフトウェアを駆使してNTPサーバを構築
しようとした場合、同じハードスペックのパソコン(DOS/Vマシン)に、
Linux/FreeBSDを搭載した場合にはmicro-second(百万分の1秒)の精度で、
GPSから得た時間とパソコンの時刻を同期させることが可能なようですが、
Windowsでは、せいぜいmili-second(1000分の1秒)の精度でしか、
時刻同期ができないようです。
色々と調べてみてみると、OSアーキテクチャや、API等に起因する
ことのようなのですが、具体的に(詳細に)そのあたりの事を
解説している書籍や、サイトが見つからず途方にくれております。
#Windows2000/XPを想定しています。
もし、上記についての情報、あるいは情報元などについて、
ご存知でしたら、ご教授頂けるとありがたいです。
よろしくお願いします。
# 絨毯爆撃といった感じのNewsgroups指定でしたので,fj以外は削りました.
# 議論が続くようであれば適切なNewsgroupsでお願いします.
At Fri, 8 Aug 2003 00:53:16 +0900,
Yuuichi Akiyama wrote:
> 一般に、Windowsでの時刻管理(制御)は、LinuxやFreeBSDと比較すると、
> あまり精度の良いものではない、ということをよく耳にするのですが、
> それは、なぜなのでしょうか?
比較した話は実はそれほど聞いたことがなかったのですが,Windows98での
タスク切り替え時間が60msで,というお話は以前読んだことがあります.
http://www.miz.nao.ac.jp/staffs/hisa/00TEsympo.html
--
柏崎 礼生 (Hiroki Kashiwazaki)@HUIIC
Ph.D candidate in the Division of Electronics & Information
Engineering, Hokkaido University
mailto:r...@cc.hokudai.ac.jp
Tel:+81-11-706-2998
> # 絨毯爆撃といった感じのNewsgroups指定でしたので,fj以外は削りました.
> # 議論が続くようであれば適切なNewsgroupsでお願いします.
japan.comp.windows-xp は戻しました。microsoft.* は他のネットワークからの
到達性がよくない(少なくともうちからはあんまり届かない)ので削ったまま。
#こういう状況を見ると Windows 関係ニュースグループの再編の話をしたくなり
#ますね。
> > 一般に、Windowsでの時刻管理(制御)は、LinuxやFreeBSDと比較すると、
> > あまり精度の良いものではない、ということをよく耳にするのですが、
> > それは、なぜなのでしょうか?
>
> 比較した話は実はそれほど聞いたことがなかったのですが,Windows98での
> タスク切り替え時間が60msで,というお話は以前読んだことがあります.
その話とは独立な話のはずです。そもそも AT 互換機はハードウェアレベルで
10ms の精度の時計を持っているはずです。
<余談>
PC-98x1 はこれを持っていなかったから、MS-DOS のシステムコールで時刻を取
得しても 1/100 秒単位を示すフィールドは常にゼロ。FMR/FM TOWNS はハードで
持っているのは 1 秒単位の時計だったけど、タイマー割り込みを使って 1 秒未
満の時計のカウントアップだの画面のリフレッシュだのを実現していたので、割
り込みを禁止すると次にハードウェアの時計がカウントアップするまで時計は止
まったままだし、テキスト VRAM をグラフィック 画面でエミュレートするアー
キテクチャの TOWNS ではテキスト画面の表示も止まる。
</余談>
☆
で、話を元に戻すと、「OS の設計の違い」という話になると思います。
そもそも AT 互換機の BIOS 自体には、1ms 以下で測れるタイマーはついていま
せんので、起動時にだけ BIOS から時刻を取得して、それ以降は OS が任意の精
度で時刻を計測します。その際に、Windows は「1ms 単位で十分」と判断したか
ら 1ms になっている(実際、Windows のシステムコールの時刻関係は 1ms 単位
です)、Linux や FreeBSD では 1μs まで頑張っている(実際に 1μs が出る
かどうかは CPU の速度に依存するでしょうけど)、ということではないでしょ
うか。
ちなみに Windows でも、QueryPerformanceCounter() を使えば、手元の環境では
1μs 以下の測定が可能です。内部的には Pentium 以降の CPU の RDTSC 命令を
使っているのでしょう。
========================================================================
飯嶋 浩光 / でるもんた・いいじま http://www.ht.sakura.ne.jp/~delmonta/
IIJIMA Hiromitsu, aka Delmonta mailto:delm...@ht.sakura.ne.jp
───【宣伝】─────────────────────────────
fj.os.ms-windows.server2003 または fj.os.ms-windows.server の新設の可否
を問う投票を実施中です。
fj.news.group.comp をご参照のうえ、ふるってご投票ください。
投票期限は 8/25(月)です。
────────────────────────────────────
> japan.comp.windows-xp は戻しました。microsoft.* は他のネットワークからの
> 到達性がよくない(少なくともうちからはあんまり届かない)ので削ったまま。
microsoft.public.jp.* はクロスポストは遠慮して欲しい旨の
説明がありますので、基本的にはしない方が良いかと思います。
> #こういう状況を見ると Windows 関係ニュースグループの再編の話をしたくなり
> #ますね。
# 私は現段階では再編に反対だったりします。
> その話とは独立な話のはずです。そもそも AT 互換機はハードウェアレベルで
> 10ms の精度の時計を持っているはずです。
> <余談>
> PC-98x1 はこれを持っていなかったから、MS-DOS のシステムコールで時刻を取
> 得しても 1/100 秒単位を示すフィールドは常にゼロ。
# 古い話でよくおぼえていませんが・・・
AT互換機はハードウェアレベルの時計ってもともとなくて、
それでは不便ということで、[RTCが実装されて、起動時にBIOSが
時刻を取得してから] 自前のカウンタでカウントアップするように
なったんじゃなかったでした?
PC-98x1は元から時計がついていて、時刻は時計から取得するので
1秒単位という話で、それじゃ不便ということで、VSYNCを利用したり
とか、インターバルタイマを利用したりとかしたアプリが
多かったような記憶(気が(^^;)があります。
> で、話を元に戻すと、「OS の設計の違い」という話になると思います。
これもかなり記憶があいまいですが、たしか 10何ミリだったか 50何ミリ
だったかで、マルチメディアを扱うのに不便で、マルチメディアタイマ
で 1ミリが利用できるようになったという話があったような。
Windows だけみても、個々のOSの設計で違いが出てますよね。
> ちなみに Windows でも、QueryPerformanceCounter() を使えば、手元の環境では
> 1μs 以下の測定が可能です。
そうなのですが、タスク切り替え時間(コンテキストスイッチの発生間隔)
が…とあるので、別の心配をしている気がします。
ただ、私は調べたことはないのでなんとも言えないのですが、そんなに
遅いかなと首をかしげてるのですが・・・
# WM_TIMER を使っているとかいう話であれば、まだわかりますが。
Kashiwazakiさん、IIJIMAさん、お返事ありがとうございます。
#なぜか、ニュースグループへの投稿ができなくってしまい、
#リプライが遅れてしまいました。
結論としては、「OS の設計の違い」なのですかね。
質問の発端は、同じハードスペックのPCにWindowsを乗せた場合と、
Linuxを乗せた場合とでの、GPS+NTP (NTPv4)の時刻同期について、
Linuxはusecオーダの同期精度が出ているのに対して、。
Windowsはmsecオーダの同期精度しか出ないことついての疑問でした。
考えられる要因としては、
(1) OSアーキテクチャ(スレッド/タスク切り替え時間等)によるもの
(2) 時刻操作のためのAPI/システムコールによるもの(usecを扱えないなど)
(3) NTPv4の内部処理上の問題(アルゴリズム、使用API等)に起因したもの
(4) その他
と、いくつか仮説を立てたのですが、技術的な裏づけを確認できなかったもので、
関連する質問を投稿させて頂いた次第です。
個人的に色々と調べたり、皆さんからの情報を元に考えてみると、
現時点では、以下のように考えています。
(1)については、Windows/Linuxとで何らかの差はあるものの、
同じような?マルチタスクなOSで、それほど大きな差は無いだろうと考えられる。
Windows95/98は、60msecでしたが、Windows2000/XPだと10msecと早くなって
いるようです(CPUクロックにも因るとは思いますが)
(2)については、Linuxではgettimeofday(), settimeofday()では、usec単位の
時刻を扱える。
一方、Windowsでも、GetSystemTimeAsFileTime, SetSystemTimeAdjustmentを
使えば、100ns(=0.1msec)単位の時刻を扱える。
したがって、API/システムコールの問題では無いと思われる。
#API/システムコールが適切に動作するかどうかは不明ですが・・・
(3)については、NTPv4(URL http://www.ntp.org)にて、Linux用とWindows用とで、
アルゴリズムが異なるかどうかは不明。
質問内容を考えると、こちらのニュースグループではなくて、NTPに関する議論を行
える
グループに投稿するのが適切であるような気もします。
もし、関連情報をお持ちでしたら、よろしくお願いします。
"IIJIMA Hiromitsu" <delm...@ht.sakura.ne.jp> wrote in message
news:3F32E1BB...@ht.sakura.ne.jp...
> (2) 時刻操作のためのAPI/システムコールによるもの(usecを扱えないなど)
...
> (2)については、Linuxではgettimeofday(), settimeofday()では、usec単位の
> 時刻を扱える。
> 一方、Windowsでも、GetSystemTimeAsFileTime, SetSystemTimeAdjustmentを
> 使えば、100ns(=0.1msec)単位の時刻を扱える。
> したがって、API/システムコールの問題では無いと思われる。
> #API/システムコールが適切に動作するかどうかは不明ですが・・・
Windows だと、GetTickCount()、timeGetTime()、Sleep() は ms 単位です。
Linux だと nanosleep() で ns 単位での指定ができますね。
(それだけの精度が出るかどうかは疑問ですが。)
GetSystemTimeAsFileTime() は 100ns ですが、これは 0.1ms ではなく 0.1μs
ですね。これは、将来、何らかの形で高精度のタイマーが実現したときのために
備えて、「どうせ 32 ビットでなく 64 ビットを使うなら」ということで高精度
な表現ができるようにしてあるのだと思います。
In article <3F32E1BB...@ht.sakura.ne.jp>
delm...@ht.sakura.ne.jp writes:
>> > 一般に、Windowsでの時刻管理(制御)は、LinuxやFreeBSDと比較すると、
>> > あまり精度の良いものではない、ということをよく耳にするのですが、
>> > それは、なぜなのでしょうか?
>>
>> 比較した話は実はそれほど聞いたことがなかったのですが,Windows98での
>> タスク切り替え時間が60msで,というお話は以前読んだことがあります.
>
>その話とは独立な話のはずです。そもそも AT 互換機はハードウェアレベルで
>10ms の精度の時計を持っているはずです。
><余談>
>PC-98x1 はこれを持っていなかったから、MS-DOS のシステムコールで時刻を取
>得しても 1/100 秒単位を示すフィールドは常にゼロ。FMR/FM TOWNS はハードで
>持っているのは 1 秒単位の時計だったけど、タイマー割り込みを使って 1 秒未
>満の時計のカウントアップだの画面のリフレッシュだのを実現していたので、割
>り込みを禁止すると次にハードウェアの時計がカウントアップするまで時計は止
>まったままだし、テキスト VRAM をグラフィック 画面でエミュレートするアー
>キテクチャの TOWNS ではテキスト画面の表示も止まる。
></余談>
ダウト!と言うか、逆では?元々IBM-PCは、高精度どころか、いかなるタイマも
持っていませんでした。それでMS-DOSは、起動時に現時刻・日付を必ず入力する
仕様になりました。それに対し、NEC-PC(こんな書方でエエんか?)の方は、
PC-8001の頃からカレンダ・タイマICを持ってました。精度は低かったですが、
1/60sだか1/100sだかは有ったはず。PC-9801VM2/VX2まではその状態が続き
ましたが、PC-9801VM21/VX21から新しいICになりました。
#そしてMINIX(98)移植チームが慌てた…。 ;-)
#MINIXの場合、他と違ってtimerのドライバだけmmやfsではなくkernelに
#有ったので、変更の影響が大きくてドライバの書方も他のドライバと
#かなり違った。
>で、話を元に戻すと、「OS の設計の違い」という話になると思います。
設計と言うより、著作権に対するポリシの違いですね。
Linux,*BSDだと誰でもOSのkernelそのもののソースコードを見て、(新しい
高精度なタイマICに対応した)改良することが可能です。しかし、Windows
ではそれが出来ません。Microsoftがしてくれなきゃ、ハイそれまでです。
そしてMicrosoftとしては、なるべく多くの、つまり古いPCでも動くように
しないといけません。そうすると古いPCに合わせた最悪の精度のものになって
しまいます。
それからWindowsのシステムコールで取れる時間は1ms単位ですが、精度は10ms
(1/100s)です。NTPDを移植しようとしたある人が教えてくれたのですが、
「デーモンがいくら頑張っても、デーモン自身を動かしてくれるタスクスイッチ
の精度が1/100sだ。」とのことです。
*BSDの場合、時刻合わせはデーモン自身でなく、デーモンがkernelに依頼して、
SYNC_PPS付きでkernelが作られている場合は、シリアルなり、パラレルなり、
LANなりから送られて来るPPS(Pulse Per Second)電気信号の割込みを待ち構えて
ダイレクトに同期することが出来ます。
>ちなみに Windows でも、QueryPerformanceCounter() を使えば、手元の環境では
>1μs 以下の測定が可能です。内部的には Pentium 以降の CPU の RDTSC 命令を
>使っているのでしょう。
RDTSC命令ならば、OSに関係無いのでLinux,*BSDでも使えます。問題は、
ある事象
QueryPerformanceCounter()
の二者(順序はどちらでも構いません)の間にタスクスイッチが入ったか
どうか、ユーザランドプロセスからは知り得ません。Linux,*BSDならば、kernel
を一般ユーザに*全て*公開しているので、一般ユーザがkernelのその事象を
扱っている部分に手を加えることが可能です。
余談:手っ取り早いのは、Linux,*BSDの場合、タスクスイッチの精度をHZ=100
からHZ=5000くらいに上げてやるという手が有ります。ロボット屋の後輩の
話では、HZ=5000くらいならPentium2でもロボットを制御しながら、ネット
サーフィンしたり、メールやニュース(:-)の読書きをストレス無く出来た
そうです。…もとい、ロボットを制御可能な精度が得られたそうです。
--
中村和志@神戸 <mailto:k...@kobe1995.net>
NAKAMURA Kazushi@KOBE <http://kobe1995.jp/>
- Be Free(BSD), or Die...
> ダウト!と言うか、逆では?元々IBM-PCは、高精度どころか、いかなるタイマも
> 持っていませんでした。それでMS-DOSは、起動時に現時刻・日付を必ず入力する
> 仕様になりました。それに対し、NEC-PC(こんな書方でエエんか?)の方は、
> PC-8001の頃からカレンダ・タイマICを持ってました。精度は低かったですが、
> 1/60sだか1/100sだかは有ったはず。PC-9801VM2/VX2まではその状態が続き
> ましたが、PC-9801VM21/VX21から新しいICになりました。
おお、そうなのですか。私がまともに 98DOS に触れたのは 1993 年頃で、その
ときのマシンは PC-9801NS とか PC-9821Ce とかでしたから、昔の話は存じません
でした。(通っていた学校に PC-9801VM がありましたけど、その後ろの正確な型
番は覚えていない…)
> それからWindowsのシステムコールで取れる時間は1ms単位ですが、精度は10ms
> (1/100s)です。NTPDを移植しようとしたある人が教えてくれたのですが、
> 「デーモンがいくら頑張っても、デーモン自身を動かしてくれるタスクスイッチ
> の精度が1/100sだ。」とのことです。
いや、経過時間の測定は timeGetTime() を使えば 1ms でできます。
現在時刻の管理が 10ms 単位だというのは存じませんでした。
> >ちなみに Windows でも、QueryPerformanceCounter() を使えば、手元の環境では
> >1μs 以下の測定が可能です。内部的には Pentium 以降の CPU の RDTSC 命令を
> >使っているのでしょう。
> RDTSC命令ならば、OSに関係無いのでLinux,*BSDでも使えます。
これ、マルチプロセッサの場合はどうなるんでしょうか?
つまり、特定のスレッドがあるときはプロセッサ A の上で、別のあるときはプロ
セッサ B の上で動くようなことになってしまうと、2 台のプロセッサで RDTSC の
カウンタが同じ値を示してはいないので、ユーザーランドで RDTSC を実行すると
数字が狂うことになります。
とすると、カーネルのある特定の部分だけは単一のプロセッサで実行させて、
そこで RDTSC を実行することになります。その結果を利用するシステムコールが
ないとアウトですよね…幸い、Linux や *BSD にはありますが。
========================================================================
飯嶋 浩光 / でるもんた・いいじま http://www.ht.sakura.ne.jp/~delmonta/
IIJIMA Hiromitsu, aka Delmonta mailto:delm...@ht.sakura.ne.jp
───【宣伝/ADVERTISEMENT】──────────────────────
>これ、マルチプロセッサの場合はどうなるんでしょうか?
これ面白い話ですね。ちとやってみますか。
記事 <0308280933...@ns.kobe1995.net> で
NAKAMURA Kazushiさんは書きました
> >いや、経過時間の測定は timeGetTime() を使えば 1ms でできます。
> >現在時刻の管理が 10ms 単位だというのは存じませんでした。
> それは逆で、OS内部の時刻管理は1msかもっと細かい精度で管理している
> と思うのですが、その時刻や、時間を取得しようと、timeGetTime()
> に限らず何らかの関数を呼ぼうとしても、実際に呼ぶまでの間に
> 一般プログラマにはいかんともし難いタスクスイッチが入ってしまう
> ので、結局10ms単位になってしまうという話。例え関数の返す値が
> 1ms単位でも、実際にプログラムを動かして何度も測定を繰り返して、
> ドリフトのグラフ描いたりしてみると10ms単位の精度しか出ていないと。
実際にやってみるとわかりますけど、timeGetTime でちゃんと1ms の精度
出ますよ。一方、GetTickCount は精度が全然出ません。Windows の時刻精度
が悪いというのは、GetTickCount を指す言葉だと思います。
以下、テストプログラム
---
#include <stdio.h>
#include <math.h>
#include <windows.h>
int main(int argc, char *argv[])
{
LARGE_INTEGER qpc1, qpc2, qpf;
int step = (argc > 1) ? atoi(argv[1]) : 25;
int count = (argc > 2) ? atoi(argv[2]) : 10;
int i, tgt2, gtc2;
int tgt1 = timeGetTime();
int gtc1 = GetTickCount();
QueryPerformanceFrequency(&qpf);
QueryPerformanceCounter(&qpc1);
printf("QueryPerformanceCounter: %.0fHz\n", (double)qpf.QuadPart);
for (i = 0; i <= count; i++) {
int tgt, gtc;
double qpc;
if (step > 10000) {
int j;
for (j = step; j> 0; j--) {
sqrt(j);
}
} else {
Sleep(step);
}
tgt2 = timeGetTime();
gtc2 = GetTickCount();
QueryPerformanceCounter(&qpc2);
tgt = tgt2 - tgt1;
gtc = gtc2 - gtc1;
qpc = (double)(qpc2.QuadPart - qpc1.QuadPart) / (double)qpf.QuadPart * 1000.0;
tgt1 = tgt2;
gtc1 = gtc2;
qpc1 = qpc2;
if (i > 0) {
printf("timeGetTime=%dms, GetTickCount=%dms, QueryPerformanceCounter=%.3fms\n", tgt, gtc, qpc);
}
}
return 0;
}
---
実行結果(Windows XP、AthlonXP 1800+、Borland C++ Builder 6 でコンパイル)
---
(ループ内では 25ms Sleep する(明示的にタスクスイッチ))
> test 25
QueryPerformanceCounter: 3579545Hz
timeGetTime=26ms, GetTickCount=16ms, QueryPerformanceCounter=25.400ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.393ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.388ms
timeGetTime=26ms, GetTickCount=16ms, QueryPerformanceCounter=25.384ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.426ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.358ms
timeGetTime=26ms, GetTickCount=16ms, QueryPerformanceCounter=25.390ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.392ms
timeGetTime=26ms, GetTickCount=16ms, QueryPerformanceCounter=25.390ms
timeGetTime=25ms, GetTickCount=31ms, QueryPerformanceCounter=25.392ms
(ループ内では 1ms Sleep する)
> test 1
QueryPerformanceCounter: 3579545Hz
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.952ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.954ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.953ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.953ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.954ms
timeGetTime=2ms, GetTickCount=15ms, QueryPerformanceCounter=1.953ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.960ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.960ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=2.008ms
timeGetTime=2ms, GetTickCount=0ms, QueryPerformanceCounter=1.885ms
(明示的にタスクスイッチしないよう、ループ内では 100万回sqrt計算する)
> test 1000000
QueryPerformanceCounter: 3579545Hz
timeGetTime=37ms, GetTickCount=47ms, QueryPerformanceCounter=37.028ms
timeGetTime=37ms, GetTickCount=31ms, QueryPerformanceCounter=37.271ms
timeGetTime=37ms, GetTickCount=32ms, QueryPerformanceCounter=36.994ms
timeGetTime=37ms, GetTickCount=46ms, QueryPerformanceCounter=36.985ms
timeGetTime=37ms, GetTickCount=32ms, QueryPerformanceCounter=37.060ms
timeGetTime=37ms, GetTickCount=31ms, QueryPerformanceCounter=37.020ms
timeGetTime=37ms, GetTickCount=47ms, QueryPerformanceCounter=37.051ms
timeGetTime=37ms, GetTickCount=31ms, QueryPerformanceCounter=36.970ms
timeGetTime=38ms, GetTickCount=47ms, QueryPerformanceCounter=36.922ms
timeGetTime=41ms, GetTickCount=31ms, QueryPerformanceCounter=41.106ms
---
PROJECT TEAM DoGA 高津正道 ta...@doga.jp
TBD0...@nifty.ne.jp
PROJECT TEAM DoGAのホームページ → http://doga.jp/
8月28日(木) 今日のマーフィーの法則 [研究員の法則]
何を実験しているかわからないときは、手際よく実行せよ。
> 実際にやってみるとわかりますけど、timeGetTime でちゃんと1ms の精度
> 出ますよ。一方、GetTickCount は精度が全然出ません。Windows の時刻精度
> が悪いというのは、GetTickCount を指す言葉だと思います。
同意。なお、timeGetTime() に関しては、NT ではデフォルトの精度が 5ms くら
いになっているので timeBeginPeriod で指定すべし、と、関数リファレンスに
あります。
高津さんのサンプルプログラムでは、1 回目の timeGetTime の呼び出しの前に
timeBeginPeriod(1); を、2 回目の呼び出しの後に timeEndPeriod(1); を加筆
してください。
========================================================================
飯嶋 浩光 / でるもんた・いいじま http://www.ht.sakura.ne.jp/~delmonta/
IIJIMA Hiromitsu, aka Delmonta mailto:delm...@ht.sakura.ne.jp
───【宣伝/ADVERTISEMENT】──────────────────────
fj.os.ms-windows.server2003 または fj.os.ms-windows.server の新設の可否
を問う投票の暫定開票結果を fj.news.group.comp で発表しました。
投票されたかたは再度、正しく集計されているかどうかご確認ください。
異議の受付は9/3(水)いっぱいを予定しています。
────────────────────────────────────