**タイトル:**  
C++における時間:クロック間変換、エポック、期間

---  

*本書では、モダン C++ で時間を扱うための主要概念と実践的テクニックについてまとめています。クロック種別、エポック計算、期間演算、および異なる時間表現間の変換方法を網羅しています。*

2025/12/26 13:57

**タイトル:** C++における時間:クロック間変換、エポック、期間 --- *本書では、モダン C++ で時間を扱うための主要概念と実践的テクニックについてまとめています。クロック種別、エポック計算、期間演算、および異なる時間表現間の変換方法を網羅しています。*

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

現在の要約は主要なポイントを正確に反映しているため、そのまま繰り返すことができます。

本文

これまでのシリーズで

ここまで、主要な標準クロックとその振る舞いについて見てきました。壁時計時間(wall‑clock time)、単調増分クロック(monotonic clocks)、そして「高解像度」の神話について語ってきました。本日はもう少し微妙な領域に踏み込みます:クロック同士の関係、エポック(epoch)の違い、そして 変換が必要になったとき に何が起こるかです。

一見シンプルそうに聞こえるでしょう。タイムスタンプはタイムスタンプで、期間は単なる秒数(あるいは他の時間単位)です。しかし

<chrono>
は型安全を重視して設計されており、不適切な使用を防ぐルールが敷かれています。そのルールの意味を理解すれば、C++ での時間扱いはずっと安心し、テストも安定します。


クロックエポック:ゼロが普遍ではない理由

<chrono>
time_point
は常に あるエポック を基準に測られます。
しかしここで注意すべき点があります:各クロックは自分自身のエポックを定義します。

クロックエポック
std::chrono::system_clock
Unix epoch(1970‑01‑01 00:00 UTC)
std::chrono::steady_clock
未指定の単調増分エポック(ブート時刻など)
std::chrono::high_resolution_clock
通常は
system_clock
または
steady_clock
のいずれかへの別名

影響

  • 異なるクロックから得た

    time_point
    は意味のある比較ができません。
    例えば、システムクロックと単調増分クロックのタイムポイントを直接比較すると、何も示しません。

  • steady_clock::time_point
    system_clock::time_point
    から引くことは、実質的に「1970‑01‑01 と任意のブート時刻カウンタとの差分は?」と尋ねるようなものです。答えはマシン・OS・月相まで変わります。

テストでもこの罠に陥りがちです。テストが

steady_clock
がゼロから始まる、またはそのエポックが実行ごとやプラットフォーム間で安定していると仮定すると、テストは脆弱になります。決定的な動作を求めるなら、制御可能なテストクロックを使うか、むしろエポックの露出自体を避ける方がベターです。


クロック間変換:できることとできないこと

クロック間の変換は、そのエポックが異なるため難しいものです。長らく標準ライブラリには

time_point
を別クロックへ変換する仕組みはありませんでした。しかし C++20 で登場した
clock_cast
clock_time_conversion
の特殊化により、クロックが意味のある関係を持つ場合に安全に変換できます。

標準サポートされる変換(
clock_cast
&
clock_time_conversion

C++20 で導入された

std::chrono::clock_cast
は、変換が定義されているときに一方のクロックから別のクロックへ
time_point
を変換します。
変換が定義される条件は次の二つです。

  1. クロック同士に 既知で安定した数式関係 があること。
  2. clock_time_conversion<FromClock, ToClock>
    の特殊化が存在すること。

標準ライブラリはすでに以下のペア間で変換を提供しています:

  • system_clock
    ↔︎
    utc_clock
    ,
    tai_clock
    ,
    gps_clock
    ,
    file_clock
  • カスタムクロック(
    clock_time_conversion
    が指定されている場合)

これらのクロックは共通のエポックとオフセットを持ちます(例:TAI は UTC より 37 秒先にある)。したがってライブラリは安全に変換計算を行えます。

例:
utc_clock
tai_clock
に変換

using namespace std::chrono;

auto utc_now = utc_clock::now();
auto tai_now = clock_cast<tai_clock>(utc_now);

この結果は、TAI と UTC の関係が標準で定義されているため、安定して決まります。

また自分自身の変換を定義することも可能です。

clock_time_conversion
をカスタムクロック用に特殊化すれば、仮想/テストクロックやシミュレーションクロック、あるいはドメイン固有のクロック(フレームカウンタなど)にも対応できます。

変換が存在しない場合の手動相関

system_clock
steady_clock
のように 固定された数式関係 が無いクロック同士では、標準は正確な変換を提供できません。エポックも挙動も異なるため、単純に「推定」しかできません。

auto system_now = std::chrono::system_clock::now();
auto steady_now = std::chrono::steady_clock::now();

auto offset = system_now - steady_now;   // ここでオフセットを取得

// 後で steady を system に近似変換したいときは
auto estimated_system_time = some_steady_tp + offset;

これは、

steady_clock
で正確な時間間隔を測定しつつ、人が読めるタイムスタンプをログに残したい場合に便利です。ただし 注意
system_clock
が NTP 同期や手動変更でジャンプするとオフセットは無効になります。長時間実行するプロセスでは危険な依存関係となります。

テスト側では、依存性注入を利用して「制御可能なクロック」を二つ渡し、変換ロジックを明示的に検証すべきです。実際のクロック間の相関関係に頼らず、あなたが書いた数学式をテストで確認します。


期間(Duration)のキャストと精度

期間は「数値+単位」だけのように見えますが、単位変換には微妙な精度問題があります。

より粗い単位への変換(切捨て)

using namespace std::chrono_literals;
auto ns = 1500ns;                     // 1500 ナノ秒
auto us = std::chrono::duration_cast<std::chrono::microseconds>(ns);
// us == 1 microsecond (残りの 500 ns は失われる)

より細かい単位への変換(「想像上の精度」)

std::chrono::milliseconds ms{1};
auto ns2 = std::chrono::duration_cast<std::chrono::nanoseconds>(ms);
// ns2 == 1'000'000 ns、しかし実際にナノ秒で測定したわけではない

C++20 では

floor
ceil
round
が追加され、意図を明確化し情報損失の場所を可視化できます。

using namespace std::chrono_literals;
auto original = 1499ns;

// 最も近いマイクロ秒へ丸める
auto rounded_us = std::chrono::round<std::chrono::microseconds>(original);
// rounded_us == 1us

// ここで 1499 ns ≈ 1 µs と決定したことになる
// 残りの 499 ns は意図的に失われる

オーバーフロー・アンダーフローと表現限界

大きな期間を減算または変換すると、オーバーフローやアンダーフローが起こる可能性があります。たとえば、数十年離れた

system_clock
のタイムポイント同士を誤って減算したり、64 ビットの整数上限を超える期間を加算するケースです。

std::chrono::duration<Rep, Period>
は内部で整数型(
Rep
)を使用します。符号付き整数がオーバーフローすると未定義動作に陥ります。符号付き
Rep
を使うメリットは、負の期間が合法的に発生する場合やロジックエラーを早期に検出できる点です。


まとめ

クロック間変換は

<chrono>
の中で最も難しい部分です。クロックごとに異なるエポックがあり、ジャンプするものとしないものがあります。また期間の扱い方によって挙動が大きく変わります。

ベストプラクティス

  1. 時間間隔は常に同じクロックで測定(できれば
    steady_clock
    )。
  2. 人が読める表現へは境界でのみ変換。
  3. エポックの関係を仮定しない。二つのタイムスタンプが「近く見える」からといって、クロック間に安定した関係があるとは限らない。
  4. 切り捨て・切り上げ・丸めは明示的に指定。
    floor
    ceil
    round
    を使う。
  5. クロック自体を検証しない。ロジック(変換計算)を検証する。

これらを守れば、C++ での時間扱いは不思議が少なくなります。


次週予告

次回は C++20 で追加された新しいクロックについて語ります。お楽しみに!

同じ日のほかのニュース

一覧に戻る →

2025/12/29 7:35

未処理の写真は、実際にどのような姿になるのでしょうか。

## Japanese Translation: --- ### 改良された要約 この記事は、カメラのRAWファイルが鈍く緑色がかった見た目になる理由を説明し、その原因をセンサーのADC出力、カラー・フィルタリング、およびその後の処理ステップに追跡しています。 1. **ADC 出力とコントラスト** – 14ビット ADC は理論上 0–16382 の値を出力しますが、実際のデータは約 2110–136000 の範囲にしかわかりません。これらの限界(黒レベル ≈ 2110、白点 ≈ 136000)を \[ V_{\text{new}} = \frac{V_{\text{old}} - \text{Black}}{\text{White} - \text{Black}} \] で再マッピングするとコントラストが向上します。 2. **カラーキャプチャ** – センサーは光の強度を記録し、色ではありません。ベイヤーフィルタグリッドは各ピクセルに単一の RGB コンポーネントを割り当てるため、初期画像にはピクセルあたり真の RGB の 1/3 のみが含まれます。 3. **デモザイキングとダイナミックレンジ** – デモザイキングは隣接ピクセルを平均化してフルカラー画像を作成しますが、依然として動的範囲が限定されます。線形 RAW データは、環境光や画面ガンマを考慮しないため、典型的なディスプレイ上で非常に暗く見えます。 4. **知覚とデータ** – 人間の明るさ知覚は非線形です。したがって、線形 ADC 値はガンマ補正や sRGB カーブを適用しない限り、過度に暗く見えることがあります。 5. **緑色キャストの起源** – 緑色のチントは、センサーの緑光への高感度、ベイヤーピクセルの 2/3 が緑を捕捉している事実、および単純なデモザイキングから生じます。 6. **ホワイトバランスとガンマ** – ホワイトバランスのスケーリングは線形データに対してガンマ補正より先に適用する必要があります。各チャネルに別々にガンマカーブを適用すると、ハイライトが減色(例えば星が黄色くなる)する可能性があります。 7. **最終画像の現在状態** – 著者の最終画像は未加工であり、カラーキャリブレーションも残留ノイズや完璧なホワイトバランスもありません。これにより、カメラ処理がすでにかなりの数学を行っていることが示されています。 8. **写真家とメーカーへの影響** – これらのステップを理解することで、写真家は RAW ファイルをより効果的に処理でき、メーカーはデフォルト設定、デモザイキングアルゴリズム、およびガンマ処理を改善する潜在的な領域を特定できます。 --- このバージョンは主要なポイントすべてを保持し、不必要な推測を避け、メインメッセージを明確に保ちつつ曖昧な表現を排除しています。

2025/12/29 5:14

ミトロリ―(Mockito)のメンテナとして10年後に退任します

## Japanese Translation: 著者は、10年間にわたるMockitoの長期メンテナとしての任務を辞める意向を表明し、2026年3月に引き継ぎが予定されていると述べています。彼は主に三つの懸念点を挙げています: 1. **JVMエージェントへの急激な移行**(Mockito 5で実装された変更は協議もなく、代替案も提示されず)によるエネルギー消耗。 2. **Kotlinとの非互換性**—特にsuspend関数に関連する問題が重複APIやスパゲッティコードを生み出し、Mockitoのアーキテクチャと整合しない点。 3. 彼自身の興味がServoなど他のオープンソースプロジェクトへ移りつつあること。 著者は、志願者が十分なサポートなしに圧力を感じる中で、Mockitoのメンテナンスが楽しみよりも「やらなければならない仕事」になっていると指摘しています。プロジェクトは新しいメンテナーによる方が最善だと考えており、他者にオープンソースの役割へ参加するよう奨励し、その名誉と特権を強調しています。 --- **(元文を保持したい場合)** > 著者は10年後にMockitoのメンテナとして退任すると発表し、2026年3月に移行が予定されていると述べています。彼はこの決定を、最近の変更—特にMockito 5でのJVMエージェントへの切替えや人気が高まるKotlinとの統合困難—による疲労感の増大に結び付けています。これらの変化は複雑さを増し、APIの重複を生じさせ、メンテナンスを楽しい活動よりも「やらなければならない仕事」に感じさせました。また、彼自身の関心がServoなど他のプロジェクトへ移っていることも述べており、これがハンドオーバーへの動機付けとなっています。著者は新たな志願者にメンテナシップを担ってもらうことで、Mockitoが新しいリーダーシップの下で進化し続けることを促しています。この変更は、新しい視点をもたらし、Kotlin統合問題を解決する可能性があり、オープンソースコミュニティにおける堅牢な志願者支援の必要性を強調すると期待されています。

2025/12/29 6:41

## Unity の Mono に関する問題 **C# コードが想定よりも遅く動作する理由** --- ### 1. 背景 - Unity は C# スクリプトの実行に **Mono**(または IL2CPP)をランタイムとして使用しています。 - 開発者は、ネイティブ C++ コードと比べてパフォーマンスが低下することに気づくことが多いです。 ### 2. 遅延の一般的な原因 | カテゴリ | よくある問題 | 発生理由 | |----------|--------------|----------| | **ガベージコレクション (GC)** | ゲームプレイ中に頻繁にメモリ確保 | GC の停止がゲームスレッドを止め、フレームレートの乱れを引き起こします。 | | **Boxing/Unboxing** | 値型をオブジェクトへキャスト | 一時的なヒープオブジェクトが生成され、収集対象になります。 | | **リフレクション** | 実行時に `System.Reflection` を使用 | 動的型解決のため、リフレクションは遅いです。 | | **文字列連結** | ループ内で `+` を繰り返し使用 | 多くの中間文字列が生成され、GC の負荷が増大します。 | | **大型 MonoBehaviour** | 一つのスクリプトに多くの責務を持たせる | フレームごとの作業量が増え、キャッシュミスにつながります。 | ### 3. プロファイリングのヒント 1. **Unity Profiler → CPU Usage を開く** - 「Managed」と「Native」の時間差に注目します。 2. **Memory タブを使用** - ゲームプレイ中に急増する割り当てを探ります。 3. **Profiler: Mono Runtime を有効化** - GC、JIT、メソッド呼び出しの詳細が確認できます。 ### 4. 最適化戦略 - **割り当てを最小限に抑える** - オブジェクトを再利用;頻繁に使うインスタンスはプールします。 - ループ内で文字列を作る場合は `StringBuilder` を使用。 - **Boxing を避ける** - 値型はそのまま保持し、`object` へのキャストは控えます。 - **リフレクション結果をキャッシュ** - 最初の検索後に `MethodInfo` や `FieldInfo` を保存します。 - **MonoBehaviour の複雑さを減らす** - 大きなスクリプトは機能ごとに分割し、専念型コンポーネントへ移行。 - **ホットパスにはネイティブプラグインを使用** - 性能重視のコードは C++ プラグインへオフロードします。 ### 5. ベストプラクティス | 実践 | 実装例 | |------|--------| | **早期にプロファイル** | 開発初期から頻繁にプロファイラを走らせます。 | | **クリーンコードを書く** | 可読性重視だが、割り当てには注意します。 | | **Update ループは軽量化** | 重いロジックは Coroutine やバックグラウンドスレッドへ移行可能です。 | ### 6. リソース - Unity Manual: [Performance Profiling](https://docs.unity3d.com/Manual/Profiler.html) - Unity Blog: 「Reducing GC Allocations in Unity」 - Stack Overflow の Mono vs. IL2CPP パフォーマンスに関する議論 --- **結論:** Mono がメモリと実行を管理する仕組みを理解し、効果的にプロファイルしてターゲット最適化を施すことで、Unity における C# スクリプトのランタイムオーバーヘッドを大幅に削減できます。

## Japanese Translation: Unity の現在の Mono ランタイムは、モダンな .NET と比べて約 2–3 倍遅く、同一ハードウェア上で実行するとベンチマークで最大 ~15 倍の速度向上が確認されています。このギャップは、Mono の JIT コンパイラが高度に最適化されていないアセンブリを生成する一方、.NET の JIT がスカラー化やレジスタベース演算などの高度な最適化を行うためです。 2006 年に導入以来、Mono は Unity のデフォルト C# ランタイムでした。Microsoft は 2014 年に .NET Core をオープンソース化し、2016 年 6 月にクロスプラットフォームサポートをリリースしました。2018 年、Unity はエンジンを Microsoft の CoreCLR(.NET Core 背後の CLR)へ移植する計画を発表し、パフォーマンス向上とプラットフォーム間の差異を縮小するとともに、一部ワークロードで 2–5 倍のブーストが期待できるとしました。 主なベンチマーク結果は次の通りです: - Mono ベースのエディタ起動時間:約 100 秒 - 同等の .NET 単体テスト:約 38 秒 - リリースモードスタンドアロンビルド:Mono 約 30 秒、.NET 約 12 秒 - 4k×4k マップ生成:.NET 約 3 秒 - int.MaxValue イテレーションの緊密ループテスト:Mono 約 11.5 秒、.NET 約 0.75 秒(約 15 倍遅い) - デバッグモード同じループ:約 67 秒(追加チェックが原因) モダンな .NET の JIT は小さな値型をスカラー化し、不変計算をループ外に持ち出し、レジスタベース演算を使用するなど、Mono が適用できない最適化を実行します。CoreCLR は Span<T>、ハードウェアイントリンシック、SIMD パスといった高度な機能も公開し、特定のコード(例:シンプルノイズ)でパフォーマンスが倍増する可能性があります。 Unity の Burst コンパイラは選択された C# メソッドを LLVM 生成ネイティブアセンブリに変換できますが、適用範囲が限定されています。CoreCLR の JIT はこれらの制約なしで同等かそれ以上の性能を提供できる可能性があります。 CoreCLR への移行は Unity 6.x を対象としており、本番稼働準備は 2026 年またはそれ以降になる予定です。採用されれば、開発者は高速なエディタ起動、短縮されたビルド時間、および Just‑In‑Time コンパイルを許可するプラットフォーム上でより効率的なランタイムコードを体験できます。ただし、Ahead‑Of‑Time (AOT) コンパイルが必要なデバイスは引き続き IL2CPP に依存するため、性能向上はターゲットプラットフォームによって異なる可能性があります。

**タイトル:** C++における時間:クロック間変換、エポック、期間 --- *本書では、モダン C++ で時間を扱うための主要概念と実践的テクニックについてまとめています。クロック種別、エポック計算、期間演算、および異なる時間表現間の変換方法を網羅しています。* | そっか~ニュース