厳密な速度までは計測していませんが、体感的には以下の順番でコンパイル時間が短くなります。worldのプログラムですらかなりの時間を費やしていました。zapcczapccとは、LLVMをベースとしたC++のコンパイラです。LLVMのソースコードを書き換えて、時々LLVMのリポジトリとマージするという開発形式をとっているので、最近のLLVMの機能も使用することが出来ます(もちろんC++17も)。具体的に何が違うのかと言うと、キャッシュです。zapccは、zapccsというプロセスと通信することにより、ヘッダーファイルの解析結果、マクロ、関数名リスト、実体化されたテンプレートなどをメモリ上にキャッシュします。以降のビルドでは、そのキャッシュを最大限使用します。ヘッダファイルを特にいじらない限り、2回目以降のコンパイル時間がかなり短縮されます。デモ動画:zapccはソースコードではなく、ヘッダーをキャッシュします。それ故、異なるソースファイルでも、同一のヘッダーを利用している場合はそのキャッシュを利用できますし、ソースコードをある程度編集したとしても同じキャッシュを利用できます。headerを無視します。headerよりも高機能なキャッシュを提供するからです。headerは、マクロ展開後のASTや実体化されたテンプレートを保持しません。headerではコンパイル時間をうまく短縮できない場合があります。zapccのビルド基本的にビルドのやり方もLLVMと同じなので、簡単な説明にとどめます。GitHubからソースをダウンロードします。https://github.com/yrnkrn/zapcc.gitその後、適当なcmakeオプションを利用してビルドの準備を行います。zapccではNinjaを利用することが推奨されているのでそれに従います。..それが終わったら、いよいよビルドします。コンパイル中はかなりメモリを消費する部分があるので注意してください。2時間ほどでビルドが終わります。-j3終わったら、ビルド先/binディレクトリにパスを通しておきましょう。PATH="$PATH:/path/to/zapcc/build/bin"これでzapccコマンドを使用可能になります。使い方はclangとほぼ同じなので解説はしません。zapccの使い方zapccを使ってC++のプログラムをコンパイルすると、zapccsプロセスが立ち上がります。zapccsを放置しているとキャッシュが増えてメモリを圧迫するので、キャッシュをクリアしたい場合はzapccsプロセスをkillします。zapccsまたは、zapccと同じディレクトリにあるzapccs.configというファイルを編集することで、メモリ使用量の最大値などを変更することが出来ます。デフォルトの設定では、zapccの1プロセス辺り2000MBとなっています。*.dat並列でzapccコマンドを使用する場合は、この値の数倍のメモリが消費されうるので気をつけましょう。ちなみにメモリ制限値を超えるとzapccsプロセスが再開され、キャッシュも空になります。デバッグオプションはつけないコンパイラのデバッグオプションを有効化するとコンパイル時間が20%程度増大します。不要ならデバッグオプションは無効化しましょう。開発中にすぐデバッグできるように、という理由でデバッグオプションが必要になる場合もありますが、デバッグオプションは基本的に実行時のエラーを検出する目的で利用します。開発初期の段階などコンパイルエラーを修復することが目的の場合は、デバッグオプションを無効化することでビルド時間を短縮できます。速いリンカーを使う皆さんはgold使ってますか?goldはgccやclangで使用できるリンカーで、デフォルトのリンカーよりも高速にリンクを行うことが出来ます。以下のコマンドを打って、正常にgoldのバーションが表示されれば、goldが使用可能です。-Wl,--version使用方法は、ライブラリのリンク時に-fuse-ld=goldオプションを指定するだけ。cmakeならCMakeLists.txtに以下の記述を追加します。endif()endif()goldを使うとリンクにかかる時間がおよそ半減されます。他にも、LLVMがlldというリンカーを提供しており、大規模なプロジェクトではgoldの倍以上の速度とも言われています。ヘッダーに実装を記述することによってODR違反の重複定義が発生し、それをリンカーが解決するため、リンカーの速度というのも無視できません。makeやmsbuildが比較的良く使われているかと思います。これらのビルド環境では、ソースコードの変更検知の処理などに時間がかかり、特にプロジェクトが膨大な時には余計なコストが発生します。makeになりますけど、cmakeが出力するMakefileは非常に分かりにくい上、余計なcmakeコマンドを呼び出す必要が生じます。そこで、高速なビルド環境Ninjaの登場です。Ninjaではcmakeの呼び出しが最小限に抑えられているので、生成されるプロセスの数も減り、特にWindowsでは顕著な違いが現れるのではないかと思います。cmakeでジェネレータをNinjaに指定するだけです。-j$(nproc)Developモードを設計してビルドするcmakeにはDebugモード、Releaseモード、MinSizeRelモードなどが標準で用意されていますが、ここにDevelopモードというのを自分で追加します。このDevelopモードは自分だけが利用することを想定し、コンパイル時間短縮のための様々な工夫を行います。std::hash#endifというように実装系依存のヘッダをインクルードしてコードを軽量化することも出来ます。実装系依存のヘッダは依存関係が複雑で、予期せぬエラーを吐いたり、あまり効率が改善しなかったりする場合もあるので十分注意します。この方法は、自分だけが得するための手段であって、自分の開発するプロジェクトをビルドしたり利用したりする人が恩恵を得られるわけではありません。TSが導入される予定なので、ここにあまりこだわりすぎると後で損をした気分になります。コンパイル時間が短いライブラリ・フレームワークを選ぶコンパイル時間で困っているときは、なるべくヘッダーとソースが上手く分割できていて、ヘッダー内で思いファイルをインクルードしていないライブラリを選ぶことがポイントです。doctestdoctestは、まさにコンパイル時間の削減の限界に挑んだテストフレームワークです。doctestはシングルヘッダーで書かれたライブラリですが、内部で明確に宣言部と実装部が分かれており、マクロを使用してそれらを制御することが出来ます。doctestはいろいろな使い方があると思いますが、筆者がおすすめする方法は、main.cppにdoctestの実装を埋め込み、その他の翻訳単位にテストケースを記述する方法です。...当然このバイナリにはdoctestの実装部が含まれていないので、最後にmain.cppとリンクします。cmakeだとこんな感じ。main)doctestの実装部はコンパイルに時間を要してしまいますが、宣言部だけだと、非常に高速にコンパイルすることが出来ます。0m0.020sさらに、main.cppはdoctest.hにしか依存していないので、コードの編集によって再コンパイルされることはありません。従って、一度main.cppをコンパイルした後は、コードを書き換えたとしてもコンパイルにはほとんど時間がかからないという結果になります。どのようにしてこのような高速コンパイルを実現しているのか?それはdoctest.hの中身を見れば分かります。宣言と実装を分離するための高等なテクニックが多く用いられているので、参考になると思います。zapccとdoctestを併用する際の問題点doctestの宣言部と実装部は、マクロによって制御します。zapccsはヘッダーの解析結果をキャッシュするので、一度インクルードしてしまうと、別の翻訳単位でも同じマクロの設定が読み込まれます。main.cppをコンパイルした後にtest1.cppをコンパイルしてリンクすると、再び実装部が読み込まれ、多重定義によりエラーとなります。これを防ぐには、zapccsの実行ファイルと同じディレクトリにあるzapccs.configというファイルを編集します。中に[Mutable]というセクションがあるので、そこにdoctest.hを追加しましょう。macros)*/doctest/doctest.hzapccs.configを編集したら必ずzapccsを再起動してください。この他にも、マクロの事前定義により設定を行うヘッダーファイルはキャッシュされるので注意しましょう。ヘッダとソースが分割されたライブラリを選ぶべきか?私もcrowやtiny-dnnでのクソ長いコンパイル時間にイライラした経験はあるので、ヘッダオンリーじゃなくてソースがきちんと分割されたライブラリを使いたくなる気持ちは分かります。無理してそのようなライブラリを利用することには多くのデメリットが伴います。コンパイル時間と実行時パフォーマンスは常にトレードオフの関係にあります。実装を分離すれば、最適化やコンパイル時処理のメリットを荒削りすることになります。DLLを利用することで、DLL地獄という問題が生じる場合があります。DLLは、特にWindowsユーザーから嫌われているようで、x86とx64の互換性や、/MTフラグと/MDフラグの違いによるエラーなどに悩まされるケースもあります。近年のライブラリは明らかにヘッダオンリー、シングルファイルのものが増えているので、期待するようなライブラリは見つからないと思ったほうが良いです。何より、多くの人が利用するライブラリを利用すれば、そのライブラリとのインターフェースを実装して提供することもできるでしょう。しても、ヘッダとソースが分割されたライブラリを使用したい!ということであれば、そのライブラリとのラッパーを自作するのが一番手っ取り早いでしょう。これはあくまで困った時の最終手段です。速いコンピュータを買ういろいろ書きましたが、結局のところ、これが一番簡単な解決方法でしょう。貧しい筆者にはメモリを買い換えるお金すら無いので貧弱なパソコンをずっと使い続けていますが、お金に余裕が有る方はメモリを増設・拡張し、SSDに換装してディスクI/Oを高速化しましょう。余裕があれば複数台のサーバーで分散ビルドしましょう。zapccを利用する場合はメモリ容量が肝心になってきます。メモリは可能な限り容量が大きいものを買いましょう。Goはコンパイルが速いらしい(誘惑)Golangにはジェネリクスが存在しないので、コンパイル時間がかなり短いという話を聞きました。C++で散々非難されている動的例外の問題も、Goならば苦しむ必要はないとか。Goは記述量を減らすことにも焦点を当てていて、ストレスはかなり軽減されるらしいです。時間があればGoについてもう少し調べてみる予定です。まとめzapcc強い。upLoginユーザーは見つかりませんでした。