計測プロファイル形式¶
概要¶
Clangは、計測による2種類のプロファイリング[1]をサポートします。フロントエンドベースとIRベースであり、どちらも様々なユースケース[2]に対応できます。このドキュメントでは、IRPGOユースケースに重点を置いて、計測されたプロファイルを保存するための2つのバイナリシリアライゼーション形式(rawとindexed)について説明します。特定のヘッダーフィールドとペイロードセクションがユースケース間で異なる解釈方法を持つ場合、ドキュメントはIRPGOに基づいています。
注記
フロントエンドで生成されたプロファイルは、ソースベースのコードカバレッジのためにカバレッジマッピングと共に使用されます。カバレッジマッピング形式はプロファイル形式とは異なります。
Rawプロファイル形式¶
rawプロファイルは、計測されたバイナリを実行することで生成されます。実行ファイルまたは共有ライブラリ[3]からのrawプロファイルデータは、ヘッダーと複数のセクションで構成され、各セクションはメモリダンプとして扱われます。rawプロファイルデータは、コンパクトで生成速度が速い必要があります。
rawプロファイル形式には、後方互換性または前方互換性の保証はありません。つまり、コンパイラとツールは、プロファイルを解析するために特定のrawプロファイルバージョンを必要とします。
最適化されたビルドのためにプロファイルをコンパイラにフィードバックする(例:IR計測の場合-fprofile-use
)には、rawプロファイルをインデックス形式に変換する必要があります。
一般的なストレージレイアウト¶
rawプロファイルデータ形式のストレージレイアウトを以下に示します。基本的に、rawプロファイルがメモリバッファに読み込まれる場合、セクションの実際のバイトオフセットは、レイアウトにおけるセクションの順序と、その前のすべてのセクションのサイズ情報から推測されます。
+----+-----------------------+
| | Magic |
| +-----------------------+
| | Version |
| +-----------------------+
H | Size Info for |
E | Section 1 |
A +-----------------------+
D | Size Info for |
E | Section 2 |
R +-----------------------+
| | ... |
| +-----------------------+
| | Size Info for |
| | Section N |
+----+-----------------------+
P | Section 1 |
A +-----------------------+
Y | Section 2 |
L +-----------------------+
O | ... |
A +-----------------------+
D | Section N |
+----+-----------------------+
注記
特定の配置要件を満たすために、セクションにパディングが追加される場合があります。簡略化のために、パディングのみを目的としたヘッダーフィールドとデータセクションは、上のデータレイアウトグラフとこのドキュメントの残りの部分では省略されています。
ヘッダー¶
マジックナンバー
マジックナンバーはプロファイル形式(raw、indexed、またはテキスト)をエンコードします。raw形式の場合、マジックナンバーは、プロファイルが生成されたプラットフォームのエンディアン(ビッグエンディアンまたはリトルエンディアン)とCポインタサイズ(4バイトまたは8バイト)もエンコードします。
ファクトリメソッドはマジックナンバーを読み取ってリーダーを適切に構築し、認識できない形式の場合はエラーを返します。具体的には、ファクトリメソッドとrawプロファイルリーダーの実装により、エンディアンが逆またはCポインタサイズが異なるプラットフォームでもrawプロファイルファイルを読み取ることができます。
バージョン
下位32ビットは実際のバージョンを、上位32ビットはプロファイルのバリアントタイプを指定します。IRベースの計測PGOとコンテキストセンシティブなIRベースの計測PGOは、2つのバリアントタイプです。
BinaryIdsSize
バイナリIDセクションのバイトサイズ。
NumData
プロファイルメタデータの数。プロファイルメタデータセクションのバイトサイズは、このフィールドを使用して計算できます。
NumCounter
プロファイルカウンタセクションのエントリ数。カウンタセクションのバイトサイズは、このフィールドを使用して計算できます。
NumBitmapBytes
プロファイルビットマップセクションのバイト数。
NamesSize
名前セクションのバイト数。
CountersDelta
このフィールドは、計測されたバイナリ内のプロファイルメタデータセクションとカウンタセクション間のメモリ内アドレスの差を記録します。つまり、
start(__llvm_prf_cnts) - start(__llvm_prf_data)
です。CounterPtrフィールドと合わせて使用して、
start(__llvm_prf_cnts)
を基準としたカウンタのオフセットを計算します。カウンタオフセットの計算で視覚的な説明を確認してください。注記
__llvm_prf_data
オブジェクトファイルセクションは、計測されたバイナリの実行時にメモリにロードされない場合や、計測されたバイナリにそもそも生成されない場合があります。これらの場合、CountersDelta
は使用されず、他のメカニズムを使用してカウンタと計測されたコードを照合します。軽量計測とバイナリプロファイルの相関関係を参照してください。BitmapDelta
このフィールドは、計測されたバイナリ内のプロファイルメタデータセクションとビットマップセクション間のメモリ内アドレスの差を記録します。つまり、
start(__llvm_prf_bits) - start(__llvm_prf_data)
です。BitmapPtrと合わせて使用して、カウンタオフセットの計算で説明されている方法と同様に、プロファイルデータレコードのビットマップを見つけます。
CountersDeltaフィールドと同様に、このフィールドはPGO以外のバリアントのプロファイルでは使用されない場合があります。
NamesDelta
名前セクションのメモリ内アドレスを記録します。rawプロファイルリーダーのエラーチェック以外では使用されません。
NumVTables
バイナリ内の計測されたvtableエントリ数を記録します。型プロファイリングに使用されます。
VNamesSize
仮想テーブル名セクションのバイトサイズを記録します。型プロファイリングに使用されます。
ValueKindLast
値の種類の数を記録します。マクロVALUE_PROF_KINDは、種類に関する説明とともに値の種類を定義します。
ペイロードセクション¶
バイナリID¶
ソースコードカバレッジのためにバイナリとプロファイルを関連付けるために、計測されたバイナリのバイナリIDを格納します。バイナリIDRFCを参照してください。
プロファイルメタデータ¶
このセクションは、カウンタと値プロファイルを計測されたコード領域(例:IRPGOの場合LLVM IR)にマッピングするためのメタデータを格納します。
メタデータのメモリ内表現は__llvm_profile_dataです。いくつかのフィールドは、プロファイル内の他のセクションからのデータを参照するために使用されます。フィールドは次のように文書化されています。
NameRef
関数のPGO名のMD5。PGO名は
[<filepath><delimiter>]<mangled-name>
形式で、<filepath>
と<delimiter>
は、ローカルリンケージ関数に、可能性のある同一関数を区別するために提供されます。
FuncHash
制御フローグラフと計測された値サイトを考慮した関数のIRのチェックサム。computeCFGHashを参照してください。
CounterPtr
プロファイルデータと対応するカウンタの開始位置間のメモリ内アドレスの差。カウンタの位置はこのように(リンク時定数として)格納されることで、シンボルのアドレスを直接スナップショットするよりも、計測されたバイナリのサイズを削減できます。コミットa1532edを参照してください。
注記
CounterPtr
は、IRPGO以外のユースケースでは異なる値を表す場合があります。例えば、バイナリプロファイルの相関付けでは、カウンタの絶対アドレスを表します。不明な場合は、ソースコードを確認してください。
BitmapPtr
プロファイルデータと対応するビットマップの開始アドレス間のメモリ上のアドレス差。
注記
CounterPtrと同様に、このフィールドはIRPGO以外のユースケースでは異なる値を表す場合があります。
FunctionPointer
インストルメント化されたバイナリの実行時に関数のアドレスを記録します。これは、生のプロファイルからインデックス付きのプロファイルへの変換時に、間接呼び出しのプロファイルされた呼び出し先アドレスを
NameRef
にマッピングするために使用されます。Values
2次元配列で値プロファイルを表現します。最初の次元の要素数は、あらゆる種類のインストルメント化された値サイトの数です。最初の次元の各要素は連結リストの先頭であり、2番目の次元の各要素は連結リストの要素で、
<profiled-value, count>
をペイロードとして持ちます。これは、値プロファイルの書き出し時にコンパイラのランタイムによって使用されます。注記
値プロファイリングは、フロントエンドとIR PGOインストルメンテーションでサポートされていますが、すべての場合でサポートされているわけではありません(例:軽量インストルメンテーション)。
NumCounters
インストルメント化された関数のカウンタの数。
NumValueSites
これはカウンタの配列であり、各カウンタは関数内の特定の種類の値のインストルメント化されたサイト数を表します。
NumBitmapBytes
関数のビットマップバイト数。
プロファイルカウンタ¶
PGO[4]では、特定のFuncHashを持つインストルメント化された関数の内部のカウンタは、連続して、インストルメンテーションポイントの選択と一致する順序で格納されます。
上記のように、記録されたカウンタのオフセットはプロファイルメタデータに対する相対値です。では、生のプロファイルデータ内で関数のカウンタはどのように配置されるのでしょうか?
基本的に、プロファイルリーダーはプロファイルメタデータ(プロファイルメタデータセクションから)を繰り返し処理し、以下に示すように記録された相対距離を利用します。
+ --> start(__llvm_prf_data) --> +---------------------+ ------------+
| | Data 1 | |
| +---------------------+ =====|| |
| | Data 2 | || |
| +---------------------+ || |
| | ... | || |
Counter| +---------------------+ || |
Delta | | Data N | || |
| +---------------------+ || | CounterPtr1
| || |
| CounterPtr2 || |
| || |
| || |
+ --> start(__llvm_prf_cnts) --> +---------------------+ || |
| ... | || |
+---------------------+ -----||----+
| Counter for | ||
| Data 1 | ||
+---------------------+ ||
| ... | ||
+---------------------+ =====||
| Counter for |
| Data 2 |
+---------------------+
| ... |
+---------------------+
| Counter for |
| Data N |
+---------------------+
グラフでは、
プロファイルヘッダーは、値を
start(__llvm_prf_cnts) - start(__llvm_prf_data)
とするCounterDelta
を記録します。便宜上、以下ではこれをCounterDeltaInitVal
と呼びます。各プロファイルデータレコード
ProfileDataN
について、CounterPtr
はstart(CounterN) - start(ProfileDataN)
として記録されます。ここで、ProfileDataN
は__llvm_prf_data
のN番目のエントリであり、CounterN
は対応するプロファイルカウンタを表します。
リーダーが次のデータレコードに進むたびに、更新CounterDelta
は、1つのProfileData
のサイズを引きます。
最初のデータレコードに対応するカウンタについて、カウンタセクションの先頭からのバイトオフセットはCounterPtr1 - CounterDeltaInitVal
として計算されます。プロファイルリーダーが2番目のデータレコードに進むと、CounterDelta
はCounterDeltaInitVal - sizeof(ProfileData)
に更新されます。したがって、カウンタセクションの先頭からの相対バイトオフセットはCounterPtr2 - (CounterDeltaInitVal - sizeof(ProfileData))
として計算されます。
ビットマップ¶
このセクションは、ソースベースの修正条件/決定カバレッジコードカバレッジに使用されます。ビットマップRFCで設計を確認してください。
名前¶
このセクションには、関数のPGO名の圧縮された連結文字列が含まれている可能性があります。圧縮されている場合は、zlibライブラリが使用されます。
関数名は、生のプロファイルがインデックス付きのプロファイルに変換されるときに、PGOデータハッシュテーブルのキーとして機能します。また、llvm-profdata
がプロファイルを人間が読み取れる形式で表示するためにも不可欠です。
仮想テーブルプロファイルデータ¶
このセクションは、型プロファイリングに使用されます。各エントリは1つの仮想テーブルに対応し、次のC++構造体で定義されます。
struct VTableProfData {
// The start address of the vtable, collected at runtime.
uint64_t StartAddress;
// The byte size of the vtable. `StartAddress` and `ByteSize` specifies an address range to look up.
uint32_t ByteSize;
// The hash of vtable's (PGO) name
uint64_t MD5HashOfName;
};
プロファイル使用時、コンパイラはソートされた仮想テーブルアドレス範囲でプロファイルされたアドレスを検索し、ハッシュされた名前を使用してアドレスを特定の仮想テーブルにマッピングします。
仮想テーブル名¶
このセクションは、上記の関数名セクションに似ていますが、プロファイルされた仮想テーブルのPGO名が含まれています。これはスタンドアロンのセクションであるため、生のプロファイルリーダーは対応するプロファイルデータセクションにアクセスすることで、各名前セットを直接見つけることができます。
このセクションは生のプロファイルに格納されているため、`llvm-profdata`はプロファイルを人間が読み取れる形式で表示できます。
値プロファイルデータ¶
このセクションには、値プロファイリングのプロファイルデータが含まれています。
プロファイルメタデータに対応する値プロファイルは、1つのレコードとして連続してシリアル化され、値プロファイルレコードはそれぞれのプロファイルデータと同じ順序で格納されるため、生のプロファイルリーダーはポインタをプロファイルデータと値プロファイルレコードの両方に同時に[5]進めることで、関数ごと、FuncHashプロファイルデータごとの値プロファイルを見つけることができます。
インデックス付きプロファイル形式¶
インデックス付きのプロファイルはllvm-profdata
から生成されます。インデックス付きのプロファイルでは、関数データはオンディスクハッシュテーブルとして整理されているため、コンパイラはIRモジュールの関数のプロファイルデータを探すことができます。
コンパイラとツールは、インデックス付きのプロファイルとの下位互換性を維持する必要があります。つまり、新しいバージョンのコードでビルドされたツールまたはコンパイラは、古いツールまたはコンパイラによって生成されたプロファイルの理解が必要です。
一般的なストレージレイアウト¶
ASCIIアートは、インデックス付きのプロファイルの一般的なストレージレイアウトを示しています。具体的には、インデックス付きのプロファイルヘッダーは、個々のペイロードセクションのバイトオフセットを記述しています。
+-----------------------+---+
| Magic | |
+-----------------------+ |
| Version | |
+-----------------------+ |
| HashType | H
+-----------------------+ E
| Byte Offset | A
+------ | of section A | D
| +-----------------------+ E
| | Byte Of fset | R
+-----------| of section B | |
| | +-----------------------+ |
| | | ... | |
| | +-----------------------+ |
| | | Byte Offset | |
+---------------| of section Z | |
| | | +-----------------------+---+
| | | | Profile Summary | |
| | | +-----------------------+ P
| | +------>| Section A | A
| | +-----------------------+ Y
| +---------->| Section B | L
| +-----------------------+ O
| | ... | A
| +-----------------------+ D
+-------------->| Section Z | |
+-----------------------+---+
注記
プロファイルサマリーセクションは、ペイロードの先頭に配置されます。ヘッダーの直後にあるため、ヘッダーを読み込んだ後、その位置は暗黙的にわかります。
ヘッダー¶
ヘッダー構造体は真実の源であり、構造体のフィールドはヘッダーの内容を説明する必要があります。高レベルでは、`*Offset`フィールドはセクションのバイトオフセットを記録し、リーダーはそれを使用して興味のあるセクションを見つけ、興味のないセクションをスキップします。
注記
インデックス付きのプロファイルの下位互換性を維持するために、既存のフィールドは構造体の定義から削除してはならず、フィールドの順序を変更してはなりません。新しいフィールドは追加する必要があります。
ペイロードセクション¶
(CS) プロファイルサマリー¶
このセクションはプロファイルヘッダーの直後です。シリアル化されたプロファイルサマリーを格納します。コンテキストに依存したIRベースのインストルメンテーションPGOの場合、このセクションには、コンテキストに依存したプロファイルに対応する追加のプロファイルサマリーが格納されます。
関数データ¶
このセクションは、関数とそのプロファイリングデータをオンディスクハッシュテーブルとして格納します。同じ名前の関数のプロファイルデータはグループ化され、1つのハッシュテーブルエントリを共有します(関数は異なる共有ライブラリから来る可能性があります)。それらのプロファイルデータは、キーがFuncHashであり、値が関数のプロファイル情報(InstrProfRecordで表される)であるキーバリューペアのシーケンスとして整理されます。
MemProfプロファイルデータ¶
このセクションは、関数のメモリプロファイリングデータを格納します。MemProfバイナリシリアル化形式RFCで設計を確認してください。
バイナリID¶
このセクションは、生のプロファイルからバイナリID情報を保持するために使用されます。
時間的プロファイルトレース¶
このセクションは、生のプロファイルから時間的プロファイル情報を保持するために使用されます。時間的プロファイリングで設計を確認してください。
仮想テーブル名¶
このセクションは、生のプロファイルの仮想テーブルの名前をインデックス付きのプロファイルに格納するために使用されます。
関数データハッシュテーブルのキーとして格納される関数名とは異なり、仮想テーブル名はインデックス付きのプロファイルのスタンドアロンセクションに格納する必要があります。これにより、`llvm-profdata`はプロファイルされた仮想テーブル情報を人間が読み取れる形式で表示できます。
プロファイルデータの使用方法¶
llvm-profdata
は、インストルメンテーションベースのプロファイルデータを表示および処理するためのコマンドラインツールです。サポートされている使用方法については、llvm-profdataのドキュメントを確認してください。