【ビルド環境編】C++の高速化まとめ

C++ c++ build fast

はじめに

最近は業務系・組み込み系でC++をよく使うようになりました。

度重なる改訂を経て、構文の複雑さ故に魔境のような言語仕様となっているC++ですが、速度を重視する開発では最も使われている言語といえます。

これを機に、ビルド時間の短縮といった開発環境の最適化や、実行速度の向上など、C++高速化の方法を簡単にまとめていきます。

 

記事が長くなりそうなので、今回はコンパイルからリンクまでのビルド時間についてです。

 

前置き
今回まとめた方法は、C++の高速化に効果のある方法をまとめたものであって、可読性や保守性に関して多くは言及していません。何かを得ると何かを失うように、パフォーマンスと管理のしやすさはトレードオフの関係にあるため、環境や要件にあった方法を参考にしてもらえばと思います。
また、ビルド時間については、ヘッダーに実装やらインクルードが殴り書きされており、各ファイルからヘッダへの依存性が著しく高いといったような「設計の問題」であることが多いです。この辺りのコーディング面での高速化は別の記事でまとめていきます。

環境・前提条件

本記事でパフォーマンス(コンパイラの速度比較)の比較を行っているWindows環境が以下です。僕がよく使っているVisual Studioでの高速化がメインです。

  • Windows10 Pro 64bit
    バージョン:20H2
    OSビルド:19042.985
  • Visual Studio 2019(Visual C++)
    バージョン:16.9.6
  • MinGW(GCC・Clang)
    バージョン:8.1.0
  • 言語標準:C++17
  • CPU:Ryzen 9 5900X
  • RAM:32GB
  • ストレージ:NvMe SSD(WD Black SN850)

 

結構パワーのあるマシンなため、重いプロジェクトを使っていきます。

 

C++のビルド時間を高速化する

速いコンパイラに変える

Windows環境でC++を書くとなると、MSが提供するVisual Studioか、エディタのVSCodeを使っている人が多いかと思われます。

MSの公式サイトからインストールできるビルドツールを使えば簡単にコンパイルできますが、いかんせんこのVisualC++標準のコンパイラ(cl.exe)が結構遅いです。

 

小さいコードならcl.exeでも全く気になりませんが、規模が大きくなっていくとリビルドに数十分・数時間~、はたまた1日掛かることなんてザラにあります。まずは、コンパイラの変更でどれだけビルド時間が改善するのかを試してみます。

 

以下は、GCC・Clang・Visual C++(cl.exe)のそれぞれのビルド時間の比較です。GitHubから落とした、cmakeが使えてビルドまでが簡単、そして少し重めなライブラリ「quickfixを使用しています。

 

visualc++_gcc_clang

 

GCCはUNIX系OSから作られた歴史の長いコンパイラ、ClangはGCCの置き換えを目指した後発のコンパイラですが、今回の環境ではGCCの方が速かったようです。GCCやClangに変更することで、10%~20%程度の速度UPが見込めます。

 

インテルコンパイラーというのもあるみたいですが、ライセンス云々で導入が面倒そうなので試していません。あしからず。

MacやLinuxにはGCCが最初から入っているようですが、WindowsにはGCCコンパイラがインストールされていないため、開発環境を準備する必要があります。

僕の環境では、MinGWをインストールしてCmakeというビルドツールを用いていますが、Visual Studio 2019では、Clangをサポートしているようなのでそちらを使ってみてもいいかもしれません。↓

 

Visual Studio プロジェクトでの Clang/LLVM のサポート
https://docs.microsoft.com/ja-jp/cpp/build/clang-support-msbuild?view=msvc-160

 

プリコンパイル済みヘッダーを設定する

安定的な動作が保証され、コードの変更を行わないソースに対しては、プリコンパイル済みヘッダーとして登録すると、ビルド時間の短縮が見込めます。

ビルドは大きく分けて、コンパイル・リンクという2つの機能を実行させています。コンパイルはソースファイルを機械語に翻訳してオブジェクトファイルを生成し、リンクはそのオブジェクトファイルを用いて実行ファイル(.exe)を作成します。

このビルドの流れのうち、コンパイル時のソースコードの構文解析に時間が掛かるため、変更されることのない安定したコードに関しては解析対象から除外することで、全体的なコンパイル時間の削減に繋がるわけです。

 

その時に使用するのがプリコンパイル済みヘッダーです。

pre compile header

 

プリコンパイル済みヘッダーは、stdafxまたはpchという名前でcpp、.hファイルを用意し、中身は除外する項目のインクルード文で構成します。(正確には、名前は何でもいいのですがプリコンパイル済みヘッダということを明示的に示すためのお作法です)

以下が記述例です。

 

#pragma onceで、念の為二重インクルードを防止し、除外するソースのインクルード文を追加しただけのシンプルなものです。実際には重いライブラリを指定することのほうが多いかもしれません。

.hと.cppが作成できたら、プロジェクトの設定を行っていきます。

 

クリック・タップで拡大できます↓

visual studio pre compile

プロジェクトを右クリック→「プロパティ」→「構成プロパティ」→「C/C++」→「プリコンパイル済みヘッダー

プリコンパイル済みヘッダーの項目を「使用(/Yu)」に変更し、作成した.hファイル名を指定します。この時、出力ファイルパスが間違っていれば修正します。

 

また、この状態ではコンパイラが、プリコンパイル済みヘッダーが既に存在するものとして判断してしまうため、個別にpchファイルを作成する必要があります。↓

visual studio pre compile header

stdafx.cppを左クリックして同様の設定を開き、プリコンパイル済みヘッダー項目で「作成(/Yc)」を選択します。これで、stdafx.cppのみ、プリコンパイル済みヘッダーを作成することができます。

 

これで、プロジェクトがプリコンパイル済みヘッダーを利用することをコンパイラに伝えるようになります。では、これで設定完了かと思いきや、まだ必要な操作が残っています。

pre compile error

エラー文にもある通りですが、プロジェクトでプリコンパイル済みヘッダーを使用する場合、全てのソースファイルでstdafx.hを見るようにという制約があります。各ソースファイルからstdafx.hをインクルードしましょう。↓

 

visual studio add include

プロジェクトを右クリック→「プロパティ」→「構成プロパティ」→「C/C++」→「詳細設定

「必ず使用されるインクルードファイル」という項目に追加することで、全てのソースファイルがstdafx.hをインクルードするようになります。この手順を行えば、ビルドが通るようになるはずです。初回ビルド時は、プリコンパイル済みヘッダーの準備等で時間が掛かりますが、2回目以降はビルド時間が短縮されます。

 

 

プリコンパイル済みヘッダーは、ソリューション単位ではなくプロジェクト単位で作成します。
また、.cppや.h内で関数や変数の宣言・実装が記述されることはなく、もし記述するとエラーが発生するか、未定義動作を引き起こす可能性があります。

プリコンパイル済みヘッダーは、頻繁にコードの変更が行われない安定したヘッダーであることが望ましいです。一部のヘッダーが変更されると、pchファイル内の全てのヘッダーで外部参照の解決をし始めるため、本来のビルドパフォーマンス向上の恩恵を受けられません。

具体的には、短期間で変更の行われない枯れたOSSライブラリやC++の標準ライブラリ(重いregexやfunctionalなど)が挙げられます。

 

並列ビルドする

コンパイルオプションで特に何も設定しない場合、CPUは限られたリソースでしかビルドを行いません。ビルドを高速化したい場合は、暇してるコアを叩き起こして並列ビルドさせましょう。

multi build

プロジェクトを右クリック→「プロパティ」→「構成プロパティ」→「C/C++」→「全般

複数プロセッサによるコンパイル項目を「はい(/MP)」にすることで、並列ビルドが可能になります。

 

Visual C++で、先ほどコンパイラの変更で試したライブラリを使ってビルド時間を比較します。

multi process build

 

並列化することで、なんとビルド時間が約7分の1まで短縮されました。目測でタスクマネージャーでしか確認していませんが、論理プロセッサ2,3使用しているところが、12コア24スレッドPCで全プロセッサを使うようになったので爆速化したようです。

 

最近ではCPU性能も飛躍的に向上しているので、ビルドの並列化は簡単な設定ながら、非常に効果がある高速化方法といえます。コンパイラの変更やプリコンパイル済みヘッダーよりも、まずは並列ビルドですね。

 

 

ビルドとリビルドの違いを知る

visual-studio-build

開発途中でソースコードの変更のみを行い、逐次デバッグを行っていくときは「リビルド」よりも「ビルド」の方が効率的です。

  • ビルド・・・・前回の正常なコンパイル以降に変更されたソースを確認し、成果物・出力ファイルの生成を行う
  • リビルド・・・全てのソースを確認し、成果物・出力ファイルの生成を行う
  • クリーン・・・過去のビルドによって生成された出力ファイルを全て削除する

 

「リビルド」は全てのソースをコンパイル対象にしますが、「ビルド」は変更された差分のみをコンパイル対象にするため、開発中は基本的に「ビルド」を使用したほうがいいです。

ただ、「ビルド」で行う変更された差分については、ソースファイルの中身のコードではなくタイムスタンプで比較しているため、何らかの要因でタイムスタンプの順序に不都合が生じてしまうと、ビルドに失敗します。その際は、「リビルド」を選択して再度コンパイルを行う必要があります。

 

ちなみに「クリーン」はビルドによって生成されたobjファイル(コードを機械語に翻訳したファイル)等の生成物を削除する操作です。ビルドに失敗してしまう、gitリポジトリの作成に不要なファイルの削除、他の人へのソースコードの配布などで使いますね。

 

 

最新のVisual Studioを利用する

Visual Studioのプレビュー版1.16.1から、デバッグビルド時のパフォーマンスが向上します。

デバッグビルドは、ステップ実行や変数の値を確認するウォッチなど、デバッグ情報を付加するコンパイル方式なため、リリースビルドよりも完了までが長いです。

そして、テスト工程に入るまでは必然的にデバッグビルドで開発している時間が圧倒的に多いですから、この修正により作業効率向上の恩恵を得られそうです。

 

リリース・納品が近かったり、特定のライブラリが動かなくなるといった限定的なケースでない限りは、パフォーマンスの改善を含む安定版がリリースされ次第、アップデートしていったほうがいいでしょう。

このようにパフォーマンスが向上したり、Git連携といった新機能、インテリセンス(コード補完機能)が使いやすくなったりたくさんのメリットがあります。

 

Visual StudioのC++デバッグビルド、2倍〜3倍高速化する方法発見
https://news.mynavi.jp/article/20210510-1885784/

 

高性能なPCに新調する

やはりハードウェアを強化することが一番効果がありますね。

 

ビルド時には、ファイルの入出力が頻繁に発生するので、読み書き速度の速いSSDを搭載するといいでしょう。HDDからSSDに変更することは、ビルドだけに留まらず、PC全体のパフォーマンスが向上します。

また、ビルド時の構文解析やリンクはCPUのシングルスレッド性能に左右されるため、CPUを高性能なものにするのもありです。多コアなCPUだと、先述したように並列コンパイルで真価を発揮します。

 

 

さいごに

コンパイラの変更は、ビルドが通らないといった互換性の問題もあり(最近は少ない?)、ビルド時間短縮という面ではそこまで重要視する必要はないかもしれません。簡単に設定できる並列ビルドをまずは試し、それからプリコンパイル済みヘッダーを視野に入れていくといいですね。

 

また、ビルド時間の短縮には、Includeを適切に利用するといったコーディング面でのテクニックも非常に重要です。このあたりは実際のコードを見たほうがスッと理解できるため、後日記事を作成します。

 

参考

https://docs.microsoft.com/ja-jp/cpp/build/creating-precompiled-header-files?view=msvc-160

 

https://docs.microsoft.com/ja-jp/cpp/build/optimizing-your-code?view=msvc-160

 

https://docs.microsoft.com/ja-jp/cpp/build/clang-support-msbuild?view=msvc-160

 

https://news.mynavi.jp/article/20190425-814773/

 

https://docs.microsoft.com/ja-jp/cpp/build/reference/compiler-options?view=msvc-160

 

https://docs.microsoft.com/ja-jp/visualstudio/ide/building-and-cleaning-projects-and-solutions-in-visual-studio?view=vs-2019

 

https://docs.microsoft.com/ja-jp/cpp/build/reference/mp-build-with-multiple-processes?view=msvc-160

C++カテゴリの最新記事4件