GCCプロファイルに基づく最適化

プロファイルに基づく最適化(以降PGO)は、コンパイラーによってプログラムを最適化する手法であり、プログラム実行の生産性を向上させることを目的としています。 ソースコードのみを分析する従来の最適化手法とは異なり、PGOは最適化されたプログラムのテスト実行の測定結果を使用して最適なコードを生成します。 テスト実行により、プログラムのどの部分がより頻繁に実行され、どの部分がより頻繁に実行されないかが明らかになります。 このアプローチの利点は、最適化方法を選択する際にコンパイラーが仮定を行わず、プログラム実行中に収集された実際のデータに基づいていることです。 テストプログラムの起動は、統計が代表的なものになるように最も典型的なシナリオに従って実行する必要があることを考慮する必要があります。そうしないと、プログラムのパフォーマンスが低下する可能性があります。

PGOには、次のタイプの最適化が含まれる場合があります( ソース ):

GCCコンパイラーを使用するときにPGOを実行する最も簡単な方法について説明します。 GCCでのPGOのサポートは、2つのフラグ-fprofile-generateおよび-fprofile-useによって提供されます。 一般的なコンパイルスキームは次のようになります。
  1. すべての最適化フラグと-fprofile-generateフラグを使用してプログラムをコンパイルします。 このフラグは、コンパイラーとリンカーの両方に設定する必要があります。 たとえば、次のように:
    g ++ -O3 -march =ネイティブ-mtune =ネイティブ-fprofile-generate -Wall -c -fmessage-length = 0 -MMD -MP -MF "src / pgo-1.d" -MT "src / pgo-1.d "-O" src / pgo-1.o "" ../src/pgo-1.cpp "
    g ++ -fprofile-generate -o "pgo-1" ./src/pgo-1.o

  2. コンパイルが成功した後、最も典型的なユースケースでプログラムのテスト実行を実行する必要があります。 すべてが正しく実行されると、テスト実行の結果として、 gcda拡張子を持つ統計ファイルが表示されます。
  3. すべての最適化フラグと-fprofile-useフラグを使用してプログラムをコンパイルします。 このフラグは、コンパイラーとリンカーの両方に設定する必要があります。 たとえば、次のように:
    g ++ -O3 -march =ネイティブ-mtune =ネイティブ-fprofile-use -Wall -c -fmessage-length = 0 -MMD -MP -MF "src / pgo-1.d" -MT "src / pgo-1.d "-O" src / pgo-1.o "" ../src/pgo-1.cpp "
    g ++ -fprofile-use -o "pgo-1" ./src/pgo-1.o

    この場合、gccは手順2で作成した統計ファイルを使用するか、ファイルが見つからなかったことを報告します。

これは簡単なスキームです。 ただし、最適化を考えずに使用する価値がない場合。 常に何かを最適化する前に、特定の最適化フラグの有用性を効果的に評価できるテスト環境を最初に作成する必要があります。

簡単な例でPGOの有効性を検討してください。 学習したプログラムのコード:
#include <iostream>
#include <アルゴリズム>
#include <stdlib.h>

const size_t MB = 1024 * 1024 ;
size_t MOD = 0 ;

unsigned char uniqueNumber {
static unsigned char number = 0 ;
++番号 MODを返し ます
}

int main int argc、 char ** argv {
if argc < 3 {
1を 返し ます。
}

size_t BLOCK_SIZE = atoi argv [ 1 ] * MB ;
MOD = atoi argv [ 2 ] ;

unsigned char * garbage = unsigned char * malloc BLOCK_SIZE ;

std :: generate_n ガベージ、BLOCK_SIZE、uniqueNumber ;
std :: sort garbage、garbage + BLOCK_SIZE ;

無料 ごみ ;

0を 返し ます
}


プログラムは、渡された最初のパラメーターに応じて、数メガバイトの符号なしchar配列を作成します。 次に、渡された2番目のパラメーターに応じて、繰り返し文字のシーケンスでそれを埋めます。 その後、結果の配列をソートします。 例:
./prog 32 3-スキームに従って埋められたサイズ32MBの配列を作成します:{1、2、1、2、...}。 その後、ソートします。

./prog 16 7-16メガバイトのサイズの配列を作成します。{1、2、3、4、5、6、1、2、3、4、5、6、...}のスキームに従って埋められます。 その後、ソートします。

したがって、さまざまなパラメーターを設定することにより、この配列を並べ替えるときに条件付き遷移をトリガーする頻度と、処理されるデータのサイズに影響を与えることができます。 これにより、前述の条件付きブランチ最適化をテストできます。 トリッキーなスクリプトを作成した後、さまざまなパラメーターを使用して1792のテストを実行し、それらを以下のグラフにまとめました。 配列サイズは変化しました:{2、4、8、16、32、128、256}、および除数{1..256}。

生産性向上率(累積)


このグラフでは、関心が互いに重なっています。 y軸に指定された絶対値ではなく、特定の色で塗りつぶされた領域を見る必要があります。 面積が大きいほど、生産性の向上は大きくなります。 グラフは、配列サイズのさまざまな値に対するパフォーマンスの向上を明確に示しています。


生産性向上率(単純)


y軸は、最適化されていないプログラムに対するパフォーマンス向上の割合を示しています。 x軸は使用されている仕切りを示しています。


絶対性能


以下のy軸のグラフは、プログラムの実行時間を秒単位で示し、x軸で使用される除数を示しています。 伝説では、-PGOは最適化なし、+最適化ありPGOを意味します。
















結論


この特定のプログラムでは、PGOベースの最適化が非常に有用であり、5〜25%の安定したパフォーマンスの向上を提供します。

ソースコード、テストスクリプト、グラフ作成スクリプトを含むファイルをダウンロードできます

アーカイブの構造は次のとおりです。
pgo-1 / fprofile-generate--fprofile-generateフラグを使用してビルドスクリプトを作成します
pgo-1 / fprofile-use--fprofile-useフラグを使用してビルドスクリプトを作成する
pgo-1 /リリース-通常バージョンをビルドするためのスクリプトを作成
pgo-1 / src-ソースコード
pgo-1 / graph.pl-チャート生成スクリプト(super_logファイルを読み取ります)
pgo-1 / run.sh-テストサイクルを開始するスクリプト

graph.plは、run.shコマンドの出力に対応する形式のログファイルで機能します。
たとえば、次のように実行できます。
./run.sh> log 2>&1
テールログ| grep -A2 PGO


UPD。 テストプロジェクトは、フラグ-O3 -march = native -mtune = native with PGO、および-O3 -march = native -mtune = native with PGOを使用してビルドされました。 すべてのグラフは、-O3 -march = native -mtune = nativeと比較して成長を示しています。

ミラー用品

Source: https://habr.com/ru/post/J138132/


All Articles