7. 時刻管理
NetZ では、システムクロック、Chrono、セッションクロック、Pacer の 4 つの時間測定を用意しています。 システムクロックと Chrono は単一のステーションでのイベントの時間を測定するのに使用され、 セッションクロックは異なるステーションでのイベントを同期し、比較するのに使用されます。 セッションクロックは接続されているすべてのステーションで同期されます。 Pacer はイベントの速度を調整するのに使用されます。
SystemClock オブジェクトと SessionClock オブジェクトは、Time オブジェクトに値を指定するのに使用できます。 例えば、tNow 変数の値をセッション開始から経過したミリ秒数に設定するには、次を使用します。
Time tNow=SessionClock::GetTime();
7.1. システムクロック
システムクロックは、NetZ プロセスが開始されてからの経過時間を ms で取得するのに使用されます。 このクロックによって提示される時間は、個別のステーションでのプロセス開始時にゼロミリ秒に設定されます。 時間は 64 ビットを使用して計算され、保存されるので、回りこむことは事実上ありません。 SystemClock によって提示される時間は、接続されているステーション間(セッションに属するステーション)では同期されていません。 同期されている時間については、 7.3. を参照してください。 通常、システムクロックは、Chrono クラスや Pacer クラスで実行される場合など、ローカルステーションでの複数の操作の実行にかかる時間を計測するのに使用されます。
Time オブジェクト値の精度は、NetZ のさまざまなプラットフォームに実装されているタイムプロバイダの性能によって異なります。 NetZ ではデフォルトのタイムプロバイダを提供していますが、SystemClock::RegisterTimeProvider を使用して独自のタイムプロバイダを登録することも可能です。
7.2. Chrono
Chrono クラスはローカルステーションで時間を計測し、ストップウォッチのように機能します。 このクラスを使用すると、ローカルステーションの特定のイベント間の経過時間をミリ秒で正確に測定できます。 Chrono クラスのインスタンスが作成されると、Chrono::Start、Chrono::Stop、 Chrono::Pause()、Chrono::Resume()、Chrono::Reset() の各メソッドを使用してストップウォッチと同様に操作します。 Chrono::Check() を使用すると、経過時間をいつでも確認することもできます。
7.3. セッションクロック
セッションクロックは、セッション全体で同期されるため、異なるステーションのイベントの同期や比較に使用します。 SessionClock::GetTime()で、取得可能です。Session::CreateSession() を使用してセッションを作成する際に、セッションマスターによって自動的にゼロに設定され、 セッションマスターのみが、SessionClock::SetTime() を使用して調整できます。
各ステーションのセッションクロックは、ステーションが参加した時に、セッションマスターと同期して、以後定期的にセッションマスターとの同期処理を行います。 セッションクロックが正確に同期されていないと、特に推測航法など、さまざまな NetZ プロセスに影響します。
同期の精度は、ネットワークのレイテンシとジッタによって左右されます。 レイテンシはメッセージが宛先に届くまでの時間差で、ジッタはレイテンシのばらつきです。 同期アルゴリズムは、2 つのステーション間のレイテンシが対称的であると仮定しています。 レイテンシは、ネットワーク中の中継器の混雑などのネットワーク環境や、ファイルの読み込みなどのシステム動作の遅延によって、 非対称になることがあります。2 つのステーション間のレイテンシが非対称的な場合は、ローカルステーションのセッションクロックに誤差が発生します。
7.3.1. 技術的な詳細説明
ステーションが参加すると、Session::JoinSession() がセッションマスターと参加するステーション間でセッションクロックを自動的に同期します。
ローカルステーションでセッションクロックを計算するため、 Figure 7.1 に示すように、 ローカルステーションはセッションマスターにメッセージを送信し、セッションマスターはセッションマスターのセッションクロックの値を返します。 レイテンシのため、ローカルステーションは、セッションクロックの値よりも一定時間後に、セッションクロックの値を含むメッセージをセッションマスターから受信します。 そのため、この値はローカルステーションのセッションクロックの値を更新するためには直接使用できません。 この理由により、ローカルステーションでは、セッションマスターから受信するセッションクロックの値は、次の式を使用してパルスの往復遅延時間に従って調整されます。 ここで tS と tL はそれぞれセッションクロックタイムとシステムクロックタイムです。


Figure 7.1 ローカルステーションのセッションクロック計算
同期アルゴリズムは、2 つのステーション間のレイテンシが対称的であると仮定しています。 つまり、特定のステーションからセッションマスターへ送信されるメッセージにかかる時間が、 セッションマスターからステーションにメッセージを送信する場合と同じであると仮定します。 そのため、2 つのステーション間のレイテンシが非対称的な場合、ローカルステーションのセッションクロックにエラーが発生します。 非対称的なレイテンシは、同期パルスを遅延させ、セッションクロックの同期精度を低下させる、 ファイルの読み込みなど(特に参加時)の追加タスクを 1 つのステーションが実行することによって起こる場合があります。
セッションマスターのセッションクロックタイムが予測されると、ローカルステーションのシステムクロックの時間と比較されます。 これらの 2 つの値の間の差異はオフセットと呼ばれます。 このオフセットはシステムクロックによって与えられた時間に加えられ、セッションクロックの新しい同期時刻が決定されます。 この例では、ローカルステーションのセッションクロックの同期に 1 回のパルスのみが使用されています。 計算にさらに多くのパルスが使用されると、セッションマスターのセッションクロックタイムとローカルステーションの時間の過去15サンプルの中央値の移動平均が使用され、より正確な同期が得られます。
セッションクロックの同期不良の一般的な原因はあるステーションがファイルの読み込みなど追加タスクを行うための非対称的レイテンシです。 これが発生している場合、あるステーションがセッションに参加すると、初期セッションクロック同期が劣化する可能性が高くなります。
7.4. Pacer
Pacer クラスは、特定のイベントが指定した周波数より早く行われないように保証する簡単な方法です。 イベントが指定された周波数内で行われる場合、ペーサーは次のイベントに進むことが許可されるまで、一定期間スリープ状態になります。 例えば、周波数を 2 Hz に設定すると、 Pacer::Pace()はペースを設定されたイベントの繰り返し間にスリープ状態になり、 連続するイベントが 0.5 秒の最小時間差で行われるように保証します。 そのため、Pace の 2 回の連続する呼び出し間の遅延が 0.2 秒の場合、Pace は 0.3 秒間スリープになり、 次のイベントが前のイベントの 0.5 秒後に実行されるようにします。 Pace の 2 回の連続する呼び出し間の時間は、処理時間と呼ばれます。 この場合の処理時間は 0.2 秒です。
ペーサーが操作するヘルツ単位の最大周波数は、次のように Pacer::SetFrequency()で指定できます。
Pacer::SetFrequency(qDouble dFrequency)
または、クラスがインスタンス化されるときは次のように指定します。
Pacer::Pacer(qDouble dFrequency=0)
周波数をゼロに設定すると、Pacer::Pace()の呼び出しは遅延されず、イベントのペースは指定されません。
Pace メソッドは Pacer::Interrupt()を呼び出すと割り込むことができます。 この呼び出しは、ペーサーがスリープの間に Pacer::Pace()を終了するのに使用されます。 これは、別のスレッドから呼び出され、 Pacer::Pace()を呼び出したスレッドをメソッドから終了させます。
Pacer::GetStats()を呼び出すと、Pacer クラスに関する統計が返されます。 統計はペーサーの作成または Pacer::ResetStats() への前回の呼び出しから計算され、合計処理時間、 最小処理時間、最大処理時間、 Pacer::Pace()の呼び出し回数、合計経過時間などが示されます。