コンパイラはGCCを前提としています。
設定
pch及びccache用の設定をCMakeに追加してあげればOK。
今回は設定するターゲット名を app
としています。
pch
ターゲットに対して target_precompile_headers()
でpch化してあげたいヘッダファイルを設定してあげればOKです。
これで自動的にpchを作成してくれます。
target_precompile_headers( app PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/app.hpp" )
ccache
2つの設定を行います。
-
target_compile_options()
で設定できるコンパイラオプションの設定1 -
set_property()
のRULE_LAUNCH_COMPILE
で設定できるコンパイル実行時のコマンドラインのプレフィックス設定2
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
target_compile_options( app PRIVATE -fpch-preprocess )
set_property(
TARGET app
PROPERTY RULE_LAUNCH_COMPILE "CCACHE_SLOPPINESS=pch_defines,time_macros ccache"
)
endif()
効果測定
実際どのくらいの効果がでるのか測定してみましょう。
測定対象
今回はyaml-cppのビルドを測定します。
$ git clone git@github.com:jbeder/yaml-cpp.git -b yaml-cpp-0.7.0
$ cd ./yaml-cpp/
$ CC=gcc-10 CXX=g++-10 cmake -S . -B ./build/ -G Ninja
# ここで出た値を結果とする
$ time cmake --build ./build/ -- -j16
また、前述の設定を参考にプロジェクトのCMakeLists.txtに以下のような追記を行いました。今回は yaml.h
のみpch化する設定を行っています。
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b230b9e..4b67785 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,6 +71,17 @@ set(backport-msvc-runtime $<VERSION_LESS:${CMAKE_VERSION},3.15>)
add_library(yaml-cpp ${yaml-cpp-type} "")
add_library(yaml-cpp::yaml-cpp ALIAS yaml-cpp)
+find_program(CCACHE_FOUND ccache)
+if(CCACHE_FOUND)
+ add_compile_options( -fpch-preprocess )
+ set_property(
+ GLOBAL
+ PROPERTY RULE_LAUNCH_COMPILE "CCACHE_SLOPPINESS=pch_defines,time_macros ccache"
+ )
+endif()
+
+target_precompile_headers( yaml-cpp PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/yaml-cpp/yaml.h" )
+
set_property(TARGET yaml-cpp
PROPERTY
MSVC_RUNTIME_LIBRARY ${CMAKE_MSVC_RUNTIME_LIBRARY})
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5ebc1a6..8b547f9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -40,6 +40,8 @@ target_link_libraries(yaml-cpp-tests
yaml-cpp
gmock)
+ target_precompile_headers( yaml-cpp-tests REUSE_FROM yaml-cpp )
+
set_property(TARGET yaml-cpp-tests PROPERTY CXX_STANDARD_REQUIRED ON)
if (NOT DEFINED CMAKE_CXX_STANDARD)
set_target_properties(yaml-cpp-tests PROPERTIES CXX_STANDARD 11)
条件
今回はいくつかの条件で測定を行い、それぞれの効果を計ってみようと思います。
条件は以下の通りです。
-
(a)
何も設定しない場合 -
(b)
pchを利用した場合 -
(c)
ccacheを利用した場合(1回目) -
(c')
ccacheを利用した場合(2回目) -
(bc)
pchとccacheを併用した場合(1回目) -
(bc')
pchとccacheを併用した場合(2回目)
これで測定の準備ができました。
さっそく測定してみましょう。
結果
以下のようになりました。
aとの比率は小数第1位までを有効とし、四捨五入で丸めています。
target | real ( user ) | aとの比率 | Ninja実行時のエッジ数 |
---|---|---|---|
a | 0m11.895s ( 0m38.431s ) | 100.0% ( 100.0% ) | 60 |
b | 0m14.316s ( 0m31.802s ) | 120.4% ( 82.75% ) | 64 |
c | 1m27.864s ( 0m37.927s ) | 738.7% ( 98.7% ) | 60 |
c' | 0m4.563s ( 0m1.015s ) | 38.4% ( 2.6% ) | 60 |
bc | 1m30.098s ( 0m35.561s ) | 757.4% ( 92.5% ) | 64 |
bc' | 0m4.964s ( 0m1.870s ) | 41.7% ( 4.9% ) | 64 |
考察
(今回のプロジェクトに限った話なのであまり意味がないかもしれませんが)
aとb、cとbcの結果から、pchを利用するとreal時間が若干伸び、user時間が若干短縮している傾向が読み取れます。数秒だし誤差かなとも思ったのですが、何回やっても同じような傾向だったため、pchを追加したことによる効果が出ていそうです。
real時間が伸びているのはエッジ数が 60
から 64
に増えたことが関係していそうです。user時間は望んでいた結果になっているため、もう少し大きいかつ同じヘッダを使いまわすプロジェクトでないと効果が出にくいのかもしれませんね。
cは思った以上に時間がかかっていますね。公式サイトのパフォーマンス結果3から多くて125%くらいかなと想像していたのですが、なんと738.7%というとんでもない比率が出てしまいました。
実はこの比率、バージョンが v4.5.1
以下のccacheを利用すると、145%程度まで下がります。v4.6.3
で急激に上がることを確認したので、このバージョンまでの間に何かがあったのかもしれません。(デバッグした結果、iiファイルを書き出す部分で時間が跳ね上がっているのを確認したのでここで何かあったのかも)
じゃあバージョンを上げずに古いものを利用すればいいのでは?と思われるかもしれませんが、実は古いバージョンだとpchを利用した際の挙動が不安定になってしまうのです。
(具体的にはこんな感じでうまくキャッシュできない現象が発生してしまいます)
# 1回目実行後に確認した結果
$ ccache -s
Summary:
Hits: 0 / 33 (0.00 %)
Direct: 0 / 55 (0.00 %)
Preprocessed: 0 / 33 (0.00 %)
Misses: 33
Direct: 55
Preprocessed: 33
Uncacheable: 22
Primary storage:
Hits: 0 / 88 (0.00 %)
Misses: 88
Cache size (GB): 0.04 / 5.00 (0.70 %)
# 2回目実行後に確認した結果
$ ccache -s
Summary:
Hits: 33 / 66 (50.00 %)
Direct: 33 / 110 (30.00 %)
Preprocessed: 0 / 33 (0.00 %)
Misses: 33
Direct: 77
Preprocessed: 33
Uncacheable: 44
Primary storage:
Hits: 66 / 176 (37.50 %)
Misses: 110
Cache size (GB): 0.04 / 5.00 (0.70 %)
Uncacheable の項目から22回ほどうまくキャッシュできていないことがわかりますね。
-v
オプションを付けて詳細を確認するとpchに関する情報がでてきます。
Uncacheable:
Could not use precompiled header: 44
また、毎回22ずつ増えるわけではなく、27や17ずつ増える場合もありました。ccache -C
でキャッシュを削除すると増加量が変化したので、キャッシュ周りが怪しそうです。
ちなみに、この記事を執筆した時点の最新のバージョン v4.7.3
ではこの不安定な挙動が解消されていることを確認しました。
(少なくとも2022年11月現在は)pchを安定した環境で利用したい方は最新のバージョンを、特に利用しないよという方は動作が早い古いバージョンのccacheを使うのがいいかもしれません(前者に関してはそこまでしてpchとccacheを併用し続けるのははたして意味があるのかという感じがしますが。。)
おまけ
作業環境
今回作業した環境はこんな感じ。
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"
$ apt update -y \
&& apt upgrade -y \
&& apt install -y gcc-10 g++-10 ninja-build cmake
# v4.6.3 以下では libzstd-dev libhiredis-dev が必要なので注意
$ git clone git@github.com:ccache/ccache.git -b v4.7.3
$ CC=gcc-10 CXX=g++-10 cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ./ccache/ -B ./ccache/build/
$ cmake --build ./ccache/build/
$ ninja -C ./ccache/build/ install
$ g++-10 --version
g++-10 (Ubuntu 10.4.0-4ubuntu1~22.04) 10.4.0
$ cmake --version
cmake version 3.22.1
# version 3.16 より前は target_precompile_headers() が利用できないので注意
ccacheの disable
設定の判定例
ccache の disable
設定が有効かどうかで処理を分けたい方向け。
set( IS_ENABLE OFF )
find_program(CCACHE_PROGRAM ccache)
if( CCACHE_PROGRAM )
execute_process(
COMMAND ${CCACHE_PROGRAM} --get-config disable
OUTPUT_VARIABLE CCACHE_DISABLE
RESULT_VARIABLE CCACHE_IS_ERROR
)
if( ${CCACHE_IS_ERROR} EQUAL "0" )
string(REPLACE "\n" "" CCACHE_DISABLE ${CCACHE_DISABLE})
message(STATUS "CCACHE_DISABLE: ${CCACHE_DISABLE}")
if( ${CCACHE_DISABLE} STREQUAL "false" )
set( IS_ENABLE ON )
endif()
endif()
endif()
IS_ENABLE
が ON
の場合は disable
設定が無効( false
)に設定されています。
-
https://ccache.dev/manual/4.7.3.html#_precompiled_headers
add the compiler option-fpch-preprocess
when compiling. ↩ -
https://ccache.dev/manual/4.7.3.html#_precompiled_headers
You must set sloppiness to pch_defines,time_macros. ↩