C++のビルド高速化



             2013/04/08
      Aiming大阪スタジオ
              後藤 文典
概要
• make、コンパイル、リンクのphaseを短くする為
  のツールや小技、手法についてを解説していき
  ます
• ※環境について
 – 以下の環境で測定しました(ccacheを除く)
    • Core i5 2500S
    • 2年前頃の2.5inchHDD
  • Windows8 & MinGW4.7.2
  • VMWare & Ubuntu12.10 & gcc4.8.0
make
• 並列化オプション
 – make –j[N]
 – ソースを並列にコンパイルする
  • ○CPUのコア数が多い程並列度を上げられる
  • 注)使用メモリがコンパイル数分必要になる
      – templateを駆使したソースだと結構メモリが必要
コンパイル(1/3)(ccache)
• コンパイル時のobjファイルをキャッシュする
 – ○リコンパイル時にコンパイルをスキップ
 – ○Makefileを汚さない
    • チーム開発中でも単独で導入できる
 – ○リビルド時に効果を発揮
 – 注)キャッシュミス時は尐し遅くなる
計測結果




• Gerritトリガビルドに導入したのはかなり効果あった
   – max cache sizeは、総objサイズの4倍あれば良いと思う
      • 過去のソースをキャッシュしてもしょうがない
      • 全書き換えパターン & 複数ブランチ作業を考えて
コンパイル(2/3)(プリコンパイル
       ヘッダ)
• プリコンパイル後のヘッダファイルをincludeする
 – ○単にincludeより速い
 – ○コンパイル時の使用メモリが減るので、並列度を上げや
   すい
 – ×プリコンパイルヘッダのプリコンパイル時間がプラス
 – 注)1つしか使用出来ない
 – 注)対象ヘッダはスリムに保つ必要がある
    • 余り大きいとプリコンパイルヘッダ自体が重くなる
計測結果(使用メモリとコンパイル時
         間)




• STLやboostをincludeして計測                // hoge.cpp
  – メモリはtime                           #include “stdafx.h”
    時間は/usr/bin/time –f “%M KB”
  注)赤丸の箇所が一応の目安?                  // stdafx.h
                                  #include <vector>
     →STL一式とboostを1、2個includeした辺り #include <list>
     →あまり詰め込まず、主要ヘッダだけ            ...
        プリコンパイル対象にするのが良さげ
コンパイル(3/3)(PImplイディオム)

• ヘッダの依存関係を減らす
#include “stack.h“                               #pragma once
// 他ソースでStackを使う時vectorが要らな                      class Stack {
い                                                public:
#include <vector>                                     Stack();
class Stack::Impl {                                   ~Stack();
public:                                               void push(int n);
      void push(int n) { v.push_back(n); }            int pop();
      int pop() {
           int n = v.back();                     private:
           v.pop_back();                              class Impl;
           return n;                                  Impl *impl;
      }                                          };
private:
  std::vector<int> v;
};

Stack::Stack()
 : impl(new Stack::Impl()) {}
Stack::~Stack()         { delete impl; }
void Stack::push(int n) { impl->push(n); }
int Stack::pop()       { return impl->pop(); }
リンク(Dynamic Link Library)

• リンク時間を減らす
• フリーのゲームエンジンIrrlichtをリンクしてみた
 – Windows8 & MinGW4.7.2
    • StaticLib時のリンク時間7.8秒 → DynamicLib時の0.8秒
    • g++ -shared –c mydll.cpp –o mydll.dll –Wl,--out-implib,libmydll.lib
 – Linux & gcc4.8環境
    • StaticLib時のリンク時間2.2秒 → DynamicLib時の0.9秒
    • g++ -shared –c mydll.cpp –o libmydll.so

 ※尐なくともGCCではdeclspec(dllexport | dllimport) _stdcall要らなかった
    • Makefileの書き換えだけでStaticLib、DynamicLibを切り替えられた
 注)dll地獄に注意
まとめ


– ボトルネック解決案やツールは大抵出そろってる
 • ボトルネックを探ればビルド時間減らせる
– 枯れた部分はLibに追い出す
 • リンク時間減らせる


※環境一式をmakeするのも良いかもしれない
参考URL

• ccache
   – http://ccache.samba.org/
• Irrlicht
   – http://irrlicht.sourceforge.net/
ありがとうございました

C++のビルド高速化について

  • 1.
    C++のビルド高速化 2013/04/08 Aiming大阪スタジオ 後藤 文典
  • 2.
    概要 • make、コンパイル、リンクのphaseを短くする為 のツールや小技、手法についてを解説していき ます
  • 3.
    • ※環境について –以下の環境で測定しました(ccacheを除く) • Core i5 2500S • 2年前頃の2.5inchHDD • Windows8 & MinGW4.7.2 • VMWare & Ubuntu12.10 & gcc4.8.0
  • 4.
    make • 並列化オプション –make –j[N] – ソースを並列にコンパイルする • ○CPUのコア数が多い程並列度を上げられる • 注)使用メモリがコンパイル数分必要になる – templateを駆使したソースだと結構メモリが必要
  • 5.
    コンパイル(1/3)(ccache) • コンパイル時のobjファイルをキャッシュする –○リコンパイル時にコンパイルをスキップ – ○Makefileを汚さない • チーム開発中でも単独で導入できる – ○リビルド時に効果を発揮 – 注)キャッシュミス時は尐し遅くなる
  • 6.
    計測結果 • Gerritトリガビルドに導入したのはかなり効果あった – max cache sizeは、総objサイズの4倍あれば良いと思う • 過去のソースをキャッシュしてもしょうがない • 全書き換えパターン & 複数ブランチ作業を考えて
  • 7.
    コンパイル(2/3)(プリコンパイル ヘッダ) • プリコンパイル後のヘッダファイルをincludeする – ○単にincludeより速い – ○コンパイル時の使用メモリが減るので、並列度を上げや すい – ×プリコンパイルヘッダのプリコンパイル時間がプラス – 注)1つしか使用出来ない – 注)対象ヘッダはスリムに保つ必要がある • 余り大きいとプリコンパイルヘッダ自体が重くなる
  • 8.
    計測結果(使用メモリとコンパイル時 間) • STLやboostをincludeして計測 // hoge.cpp – メモリはtime #include “stdafx.h” 時間は/usr/bin/time –f “%M KB” 注)赤丸の箇所が一応の目安? // stdafx.h #include <vector> →STL一式とboostを1、2個includeした辺り #include <list> →あまり詰め込まず、主要ヘッダだけ ... プリコンパイル対象にするのが良さげ
  • 9.
  • 10.
    #include “stack.h“ #pragma once // 他ソースでStackを使う時vectorが要らな class Stack { い public: #include <vector> Stack(); class Stack::Impl { ~Stack(); public: void push(int n); void push(int n) { v.push_back(n); } int pop(); int pop() { int n = v.back(); private: v.pop_back(); class Impl; return n; Impl *impl; } }; private: std::vector<int> v; }; Stack::Stack() : impl(new Stack::Impl()) {} Stack::~Stack() { delete impl; } void Stack::push(int n) { impl->push(n); } int Stack::pop() { return impl->pop(); }
  • 11.
    リンク(Dynamic Link Library) •リンク時間を減らす
  • 12.
    • フリーのゲームエンジンIrrlichtをリンクしてみた –Windows8 & MinGW4.7.2 • StaticLib時のリンク時間7.8秒 → DynamicLib時の0.8秒 • g++ -shared –c mydll.cpp –o mydll.dll –Wl,--out-implib,libmydll.lib – Linux & gcc4.8環境 • StaticLib時のリンク時間2.2秒 → DynamicLib時の0.9秒 • g++ -shared –c mydll.cpp –o libmydll.so ※尐なくともGCCではdeclspec(dllexport | dllimport) _stdcall要らなかった • Makefileの書き換えだけでStaticLib、DynamicLibを切り替えられた 注)dll地獄に注意
  • 13.
    まとめ – ボトルネック解決案やツールは大抵出そろってる •ボトルネックを探ればビルド時間減らせる – 枯れた部分はLibに追い出す • リンク時間減らせる ※環境一式をmakeするのも良いかもしれない
  • 14.
    参考URL • ccache – http://ccache.samba.org/ • Irrlicht – http://irrlicht.sourceforge.net/
  • 15.