当店には、次の特徴を持つ機械があります。
- 256 コアの sparc solaris
- 〜64ギガバイトのRAM
- /tmp の RAM ドライブに使用されるメモリの一部
最初にセットアップされたとき、他のユーザーがその存在を発見する前に、タイミングテストを実行して、どれだけプッシュできるかを確認しました. 問題のビルドは非再帰的であるため、すべてのジョブは単一の make プロセスから開始されます。/tmp
また、RAM ドライブを利用するためにレポをクローンしました。
-j56 まで改善が見られました。それを超えると、私の結果はマキシムのグラフのように横ばいになり、パフォーマンスが低下し始める -j75 のどこか (大まかに) まで続きました。複数の並行ビルドを実行すると、-j56 の明らかな上限を超えることができました。
主要な make プロセスはシングルスレッドです。いくつかのテストを実行した後、到達している上限は、プライマリ スレッドが処理できる子プロセスの数に関係していることに気付きました。これは、解析に余分な時間を必要とするメイクファイル内の何かによってさらに妨げられました (=
たとえば:=
、不必要な遅延評価、複雑なユーザー定義マクロなどを避けるか、または のようなものを使用し$(shell)
ます。
これらは、顕著な影響を与えるビルドを高速化するために私ができることです:
:=
可能な限り使用する
で一度変数に代入し、:=
後で で代入すると+=
、引き続き即時評価が使用されます。ただし、変数が以前に割り当てられていない場合、 and は常に評価を遅らせます?=
。+=
評価の遅延は、ビルドが十分に大きくなるまで大したことではないように思われます。すべてのメイクファイルが解析された後に変数 ( などCFLAGS
) が変更されない場合、遅延評価を使用したくない可能性があります (また、 を使用する場合は、とにかく私が話していることについて十分に知っているでしょう)。私のアドバイスを無視する)。
この機能で実行するマクロを作成する場合$(call)
は、できるだけ多くの評価を事前に行うようにしてください。
私はかつて、頭の中で次の形式のマクロを作成することを考えていました。
IFLINUX = $(strip $(if $(filter Linux,$(shell uname)),$(1),$(2)))
IFCLANG = $(strip $(if $(filter-out undefined,$(origin CLANG_BUILD)),$(1),$(2)))
...
# an example of how I might have made the worst use of it
CXXFLAGS = ${whatever flags} $(call IFCLANG,-fsanitize=undefined)
このビルドでは、10,000 を超えるオブジェクト ファイルが生成され、そのうち約 8,000 は C++ コードからのものです。を使用していれば、すべてのコンパイル手順で、既に評価されたテキストCXXFLAGS := (...)
にすぐに置き換えるだけで済みます。${CXXFLAGS}
代わりに、各コンパイル ステップで 1 回、その変数のテキストを再評価する必要があります。
選択の余地がない場合に、少なくとも再評価の一部を軽減するのに役立つ代替実装:
ifneq 'undefined' '$(origin CLANG_BUILD)'
IFCLANG = $(strip $(1))
else
IFCLANG = $(strip $(2))
endif
...ただし、それは繰り返される$(origin)
and$(if)
呼び出しを回避するのに役立ちます。:=
可能な限り使用に関するアドバイスに従う必要があります。
可能であれば、レシピ内でカスタム マクロを使用しないでください
理由は、上記の後にここでかなり明白なはずです。変数またはマクロをコンパイル/リンク ステップごとに繰り返し評価する必要があるものは、ビルド速度を低下させます。すべてのマクロ/変数の評価は、新しいジョブを開始するものと同じスレッドで発生するため、解析に費やされる時間は、別の並列ジョブの開始を遅らせる時間になります。
コードの再利用を促進したり、可読性を向上させたりするために、カスタム マクロにいくつかのレシピを入れていますが、最小限に抑えるようにしています。