AMDの無料プロファイラ「CodeXL」を使ってみた

#概要
　Intelコンパイラには、[VTune](http://www.xlsoft.com/jp/products/intel/vtune/?isus)というプロファイラが付属していることはよく知られています。ただ、Intelコンパイラは**アカデミック料金ですら高い**ので、無料のプロファイラが欲しいなーって思っていました。昔はAMDの[CodeAnalyst](http://developer.amd.com/tools-and-sdks/archive/amd-codeanalyst-performance-analyzer/)をよく使っていたのですが、更新が止まっていて、Windows 10だと**挙動がなんかおかしい(フリーズする)**のが残念でした。
　そこでググり直したところ、[AMDがより新しいプロファイラを出していた](http://d.hatena.ne.jp/mjt/20121001/p1)ことを知りました。ということで使用してみたというのが今回の記事です。

#インストール方法
　まず、[CodeXLの公式サイト](http://developer.amd.com/tools-and-sdks/opencl-zone/codexl/)にアクセスします。
![image](https://qiita-image-store.s3.amazonaws.com/0/29802/78825ff2-9f54-e66a-9c2c-1d285ea907ea.png)
　ページの一番下に、このようにWindows用およびLinux用のインストーラが置いてあるのでそのままダウンロードしてインストールすればOKです。

#使い方
1：解析したい実行ファイルを用意します。普段使っているVisual Studioで説明すると、**/Ziオプションを付けてデバッグ情報が出力される状態でビルド**しておけばOKです。32bitバイナリでも64bitバイナリでもプロファイルできるのが嬉しいですね。
今回は、次のソースをテストとして実行してみます。

```sample.cpp
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char *argv[]){
	if(argc < 2) return 1;
	const rsize_t N = std::stoi(argv[1]);
	std::vector<int> data(N);
	for(rsize_t i = 0; i < N; ++i){
		data[i] = N - i;
	}
	for(rsize_t i = 0; i < N - 1; ++i){
		for(rsize_t j = i + 1; j < N; ++j){
			if(data[i] > data[j]){
				int temp = data[i];
				data[i] = data[j];
				data[j] = temp;
			}
		}
	}
	for(rsize_t i = 0; i < N; ++i){
		std::cout << data[i] << std::endl;
	}
}
```

2：次にCodeXLを起動して、最初に表示されるメニューからは「Profile」→「Create a new project for profiling」を選択します。2回目以降は、メニューから「File」→「New project...」を選択します。
![image](https://qiita-image-store.s3.amazonaws.com/0/29802/1be3a0cf-5324-63de-a03a-5eba649f441d.png)

3：するとプロファイルの設定画面が出てきます。「General」から実行ファイルのパスや引数などを設定できるので設定しましょう。
細かい設定は「Profile」→「CPU Profile」から行えます。
![image](https://qiita-image-store.s3.amazonaws.com/0/29802/578a4e4c-00bb-a9ff-ef30-fa7360f62b0c.png)

4：そしてメニューから「Profile」→「Start Profiling」を押してプロファイルを実行します。F5キーを押すか、GUIで見える緑の三角ボタンを押してもオーケーですね。中断したい際は、緑の三角ボタンの右にあるボタンを押せばいいかと。
![cap3.png](https://qiita-image-store.s3.amazonaws.com/0/29802/ff05dcc3-ad46-9689-73c1-1fb5f1ad71ce.png)

5：先の画像からも分かるように、各関数ごとにどれほど時間が掛かったかを表示できる他、ソースコードの各行……どころかアセンブラ単位での経過時間も測れます。メニューから「File」→「Project Settings...」を選ぶとプロファイルの設定を弄れますので、異なるパタメータ間の実行結果比較も可能です。
![cap4.png](https://qiita-image-store.s3.amazonaws.com/0/29802/c9569d16-7b50-39cb-ad71-64401d37f124.png)

6：ただ、この**Sample数はあくまでも累計**ですので、「この行の処理とこの行の処理のどっちが速いか」といったことを調べる際には注意が必要です。上の例で言えば、N=5000の際、10行目の代入は**5000回**しか行われませんが、14行目の比較および15～17行目の交換は**12497500回**も行われます。10行目がSample数0で、14行目がSample数18というのは**約2500倍も違えば当然**ですね？
　逆に考えれば、N=700000ぐらいあれば10行目もSample数1に行きそうですが、実際に試すとむしろ**std::vectorとかのコスト**がプロファイルに出てきてしまいます。

7：実は先ほどの実行結果では、コンパイラの最適化をワザと切って行いました。/O2オプションを付けて同じ引数で実行すると、ご覧のとおりsample数およびアセンブラが大きく異なった結果になります。行数が減ったどころか、15行目のように**最適化でアセンブラが消失した**部分すらあることが分かりますね。main関数における最適化前のサンプル数が65、最適化後で8ですので実に**8倍以上のスピードアップ**と言えます。
![cap5.png](https://qiita-image-store.s3.amazonaws.com/0/29802/0e4ff377-1f0a-5a21-dbd4-d1a331095541.png)
　また、大きな入力(N=50000)で試してみると、14行目のサンプル数と16・17行目のサンプル数がおよそ2：1の割合だと分かります(前述したように両者の実行回数は同じ)。条件分岐が重いのは自明ですが、このように**実行回数さえ分かっていれば行ごとの処理の重さを比較することも可能**です。ちなみに今回のサンプリング間隔は1ミリ秒ですので、サンプル数が383＝383ミリ秒ということ。
![cap6.png](https://qiita-image-store.s3.amazonaws.com/0/29802/2eda11b6-0937-098a-ab05-508cac9e248b.png)

#備考
　本当はこのツール、GPUコードのデバッグも可能です。ただ、その方向で使ったことはないので詳しい説明は出来ませんorz