2005 年度 修士論文 線形代数ライブラリ 自動チューニングソフトウェア ATLAS の改良 提出日 : 2006 年 2 月 3 日 指導 : 上田 和紀 教授 早稲田大学大学院 理工学研究科 情報・ネットワーク専攻 学籍番号 : 3604U009-1 石崎 淳也 概要 本研究では, 数値計算ライブラリ ATLAS (Automatically Tuned Linear Algebra Software) を改良し, より性能の高い行列積を自動で生成することを目的として いる. 特に, BLAS (Basic Liner Algebra Subprograms) の Level 3 で規定される 行列積用のサブルーチンである GEMM のカーネル探索部分についての改良を行 なった. 数値計算ライブラリ ATLAS とは, AEOS (Automated Empirical Optimization of Software) と呼ばれるパラダイムに基づいて, 自動的にチューニングされた線 形代数ライブラリを生成するソフトウェアである. また ATLAS の生成するライ ブラリは BLAS に準拠している. GEMM は, BLAS の Level 3 で規定される行列積用のサブルーチンである. 中 でも, 倍精度行列積用のサブルーチンである DGEMM は, 世界中のスーパーコン ピュータの性能を計測し, そのランキングを公表するプロジェクト TOP500[2] で 利用される Linpack ベンチマークで使われるため, これまで多くのグループの研 究対象となってきた. また, 単に Linpack ベンチマークで使われているからだけ ではなく, 行列積をチューニングすることで Level 3 BLAS サブルーチンの大部 分をチューニングできると言う特徴もある. ATLAS の生成した行列積は高性能ではあるものの, Xeon や Pentium 4 等の アーキテクチャにおいては ATLAS の生成したコードを手動で改良し, 性能向上 に成功した論文が発表されている [3][4]. これは, 近年のアーキテクチャの特徴で ある「 L2 / L3 キャッシュのレイテンシの隠蔽機能」を, ATLAS が考慮していな いからであると思われる. また, 実際に ATLAS のコードを読んでみると, 探索方 法や探索範囲にも改善の余地が見られた. そこで本研究では, ATLAS が自動でより性能のよい行列積を生成できるよう に, 次の 4 つの改良を行なった. 1) L2 / L3 キャッシュのサイズを測定し, それら の値をブロックサイズの決定に利用した. 2) ハードウェアプリフェッチを考慮し, 探索するアンロール段数を拡張した. 3) 固定された幅で探索されていたブロック サイズを, アンロールの段数に基づいて探索を行なうようにした. 4) 固定された 幅で探索されていたアンロールの段数をブロックサイズや他のアンロール段数に 基づいて探索を行なうように改良した. これらの改良により, Xeon プロセッサにおいて約 14 %, Itanium 2 プロセッサ において約 19 % の性能向上を達成し, その他のプロセッサについても性能の向 上を確認することができた. 目次 第 1 章 はじめに 1.1 研究の背景と目的 . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 論文構成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 第2章 2.1 2.2 2.3 BLAS 概要 . . . . . . . . . . . . . . . . . . サブルーチンの名称 . . . . . . . . . DGEMM (倍精度行列積ルーチン) . . 2.3.1 DGEMM の一般的な実装 . . 2.3.2 DGEMM のインターフェース . . . . . 3 3 3 5 5 7 第3章 3.1 3.2 3.3 3.4 AEOS 概要 . . . . . . . . . . . . . . . . . AEOS の役割 . . . . . . . . . . . . 基本的な AEOS の要求 . . . . . . . 実際にソフトウェアに適用する方法 3.4.1 parameterized adaptation . 3.4.2 sorce code adaptation . . . . . . . . . . 8 8 9 10 11 11 11 . . . . . . . . . 13 13 13 14 15 18 24 25 25 26 第 5 章 ATLAS 改良の提案 5.1 考慮すべきアーキテクチャの特性 . . . . . . . . . . . . . . . . . . 5.1.1 レイテンシ隠蔽機能 . . . . . . . . . . . . . . . . . . . . . 30 30 30 第4章 4.1 4.2 4.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ATLAS 概要 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ATLAS の限界 . . . . . . . . . . . . . . . . . . . . . . . ATLAS による BLAS 3 のサポート . . . . . . . . . . . . 4.3.1 一般的な行列積の構成とパラメータによる最適化 4.3.2 コードジェネレータによる最適化 . . . . . . . . . 4.3.3 すべてを考慮した探索の概要 . . . . . . . . . . . 4.4 ATLAS による探索の詳細 . . . . . . . . . . . . . . . . . 4.4.1 探索の全体像 . . . . . . . . . . . . . . . . . . . . 4.4.2 探索の手順 . . . . . . . . . . . . . . . . . . . . . i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2 5.1.2 ハードウェア固有の命令 . . . . . 5.1.3 FSB 動作周波数 / FSB 帯域 . . . 考慮すべき探索プログラムの設計 . . . . 5.2.1 ブロックサイズの探索 . . . . . . 5.2.2 K に対するアンロール段数の探索 5.2.3 カーネル呼び出し部分の最適化 . 第 6 章 評価と考察 6.1 実験内容と結果 . . . . . . . . . 6.1.1 Celeron プロセッサ . . . 6.1.2 PentiumIII プロセッサ . 6.1.3 Pentium 4 プロセッサ . 6.1.4 Pentium M プロセッサ . 6.1.5 Xeon プロセッサ . . . . 6.1.6 Itanium 2 プロセッサ . . 6.1.7 Athlon MP プロセッサ . 6.1.8 Opteron プロセッサ . . 6.1.9 G4 プロセッサ . . . . . 6.2 結果のまとめ . . . . . . . . . . 6.3 性能に関する考察 . . . . . . . . 6.3.1 それぞれの改良について 6.3.2 手動との性能比較 . . . . 6.4 探索時間に関する考察 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 7 章 関連研究 7.1 手動での DGEMM の改良 . . . . . 7.2 自動チューニング . . . . . . . . . . 7.2.1 AEOS に基づくライブラリ 7.2.2 探索時間の削減と性能向上 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 第 8 章 まとめと今後の課題 8.1 まとめ . . . . . . . . . . . . . . . . . . 8.2 今後の課題 . . . . . . . . . . . . . . . 8.2.1 カーネル部分の最適化 . . . . . 8.2.2 カーネル呼び出し部分の最適化 8.2.3 探索時間の短縮 . . . . . . . . . 謝辞 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 35 35 35 37 37 . . . . . . . . . . . . . . . 39 39 40 42 44 46 48 50 52 54 56 58 58 58 61 62 . . . . 63 63 63 63 63 . . . . . 65 65 65 65 66 66 67 ii 図目次 2.1 2.2 DGEMM カーネル呼び出し処理例 . . . . . . . . . . . . . . . . . DGEMM カーネル例 . . . . . . . . . . . . . . . . . . . . . . . . . 4.1 行列積計算の 1 ステップ . . . . 4.2 A を最内ループにした行列積 . 4.3 B を最内ループにした行列積 . 4.4 行列積探索の概要 . . . . . . . . 4.5 具体的な行列積探索 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 19 20 26 27 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 DGEMM カーネル呼び出し処理例 . . . . . . . . . . . . DGEMM カーネル例 . . . . . . . . . . . . . . . . . . . . 変更後のアンロール段数探索域 (レジスタが 8 本の場合) 手動で生成した最適な DGEMM カーネルの命令列 . . . DGEMM カーネルの j/i ループ内で参照されるデータ . DGEMM カーネルの j / i ループ内で参照されるデータ . システムバス負荷を考慮した DGEMM カーネル . . . . ループ順序とデータアクセス . . . . . . . . . . . . . . . 行列 C のワーク領域へのデータコピーの削除 . . . . . . 行列 B のワーク領域へのコピー回数削減 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 31 32 33 34 35 36 38 38 38 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 Celeron のブロックサイズと性能 (全体) . . Celeron のブロックサイズと性能 (詳細) . . PentiumIII のブロックサイズと性能 (全体) . PentiumIII のブロックサイズと性能 (詳細) . Pentium 4 のブロックサイズと性能 (全体) . Pentium 4 のブロックサイズと性能 (詳細) . Pentium M のブロックサイズと性能 (全体) Pentium M のブロックサイズと性能 (詳細) Xeon のブロックサイズと性能 (全体) . . . . Xeon のブロックサイズと性能 (詳細) . . . . Itanium 2 ブロックサイズと性能 (全体) . . Itanium 2 ブロックサイズと性能 (詳細) . . Athlon MP のブロックサイズと性能 (全体) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 41 43 43 45 45 47 47 49 49 51 51 53 iii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 6 . . . . . . . . . . . . . . . . . . . . . . . . . . 6.14 6.15 6.16 6.17 6.18 6.19 Athlon MP のブロックサイズと性能 (詳細) Opteron ブロックサイズと性能 (全体) . . . Opteron ブロックサイズと性能 (詳細) . . . G4 のブロックサイズと性能 (全体) . . . . . G4 のブロックサイズと性能 (詳細) . . . . . ブロックサイズと性能の再現性 (Xeon-gcc) . iv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 55 55 57 57 60 表目次 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 6.14 6.15 6.16 6.17 6.18 6.19 6.20 6.21 6.22 6.23 6.24 6.25 6.26 Celeron プロセッサの特徴 . . . . . . . . . . . . . . . . . gcc における Celeron プロセッサのベストパラメータ . . PentiumIII プロセッサの特徴 . . . . . . . . . . . . . . . gcc における PentiumIII プロセッサのベストパラメータ Pentium 4 プロセッサの特徴 . . . . . . . . . . . . . . . . gcc における Pentium 4 プロセッサのベストパラメータ icc における Pentium 4 プロセッサのベストパラメータ . Pentium M プロセッサの特徴 . . . . . . . . . . . . . . . gcc における Pentium M プロセッサのベストパラメータ icc における Pentium M プロセッサのベストパラメータ Xeon プロセッサの特徴 . . . . . . . . . . . . . . . . . . gcc における Xeon プロセッサのベストパラメータ . . . icc における Xeon プロセッサのベストパラメータ . . . . プロセッサの特徴 . . . . . . . . . . . . . . . . . . . . . . gcc における Itanium 2 プロセッサのベストパラメータ . icc における Itanium 2 プロセッサのベストパラメータ . Athlon MP プロセッサの特徴 . . . . . . . . . . . . . . . gcc における Athlon MP プロセッサのベストパラメータ icc における Athlon MP プロセッサのベストパラメータ Opteron プロセッサの特徴 . . . . . . . . . . . . . . . . . gcc における Opteron プロセッサのベストパラメータ . . icc における Opteron プロセッサのベストパラメータ . . G4 プロセッサの特徴 . . . . . . . . . . . . . . . . . . . . gcc における G4 プロセッサのベストパラメータ . . . . . 改良の効果 . . . . . . . . . . . . . . . . . . . . . . . . . 改良前後の性能比較 . . . . . . . . . . . . . . . . . . . . v . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 40 42 42 44 44 44 46 46 46 48 48 48 50 50 50 52 52 52 54 54 54 56 56 58 59 第 1 章 はじめに 1.1 研究の背景と目的 数値計算ライブラリ ATLAS (Automatically Tuned Linear Algebra Software) とは, AEOS (Automated Empirical Optimization of Software) と呼ばれるパラ ダイムに基づいて, 自動的にチューニングされた線形代数ライブラリを生成する ソフトウェアである. そして AEOS とは, 急速に発展し続ける様々なアーキテク チャにソフトウェアがついていくためのライブラリ管理方法である. ATLAS の生成するライブラリは性能がよく, BLAS (Basic Liner Algebra Subprograms) に準拠しているため, 大変多くのワークグループで利用されている. 中でも BLAS の Level 3 で規定される倍精度行列積用のサブルーチンである DGEMM は, 世界中のスーパーコンピュータの性能を計測し, そのランキングを 公表するプロジェクト TOP500[2] で使用される Linpack ベンチマークで利用さ れている. また, 単に Linpack ベンチマークで使われているからだけではなく, 行 列積をチューニングすることで Level 3 BLAS サブルーチンの大部分をチューニ ングできると言う特徴もある. このため, 行列積はこれまで多くのグループの研 究対象となって来た. ATLAS の生成する行列積は高性能ではあるものの, Xeon や Pentium 4 等の アーキテクチャにおいては, ATLAS の生成したコードを手動で改良し性能向上 に成功した論文が発表されている [3][4]. これは, 近年のアーキテクチャの特徴で ある「 L2 / L3 キャッシュのレイテンシの隠蔽機能」を, ATLAS が考慮していな いからであると思われる. また, 実際に ATLAS のコードを読んでみると, 探索方 法や探索範囲にも改善の余地が見られた. そこで本研究では, ATLAS が自動でより性能のよい行列積を生成できるよう にすることを目的とする. 1.2 論文構成 本論文の構成は下記の通りである. 第2章 する. ATLAS の生成するライブラリの準拠する BLAS の概要について説明 1 1.2 論文構成 第3章 はじめに ATLAS の基となっているパラダイムである AEOS について説明する. 第 4 章 一般的な行列積 (DGEMM) の構成と, ATLAS の持っている自動チュー ニングの方法について説明する. さらに, ATLAS の行列積の探索部分について詳 細に解説する. 第 5 章 第 4 章で説明した行列積の探索ルーチンに関する問題点と, その改良方 法を提案する. 第 6 章 第 5 章で提案した改良点のいくつかを実装した ATLAS を, 様々なアー キテクチャ上で実行し, その結果を示し考察する. 第7章 本研究と関連する研究を紹介する. 第8章 本研究の成果をまとめ, 今後の課題を述べる. 2 第 2 章 BLAS 2.1 概要 BLAS (Basic Linear Algebra Subprograms)[5][6][7] とは, 行列とベクトルの基 本演算を行うルーチンを集めたライブラリで, Level 1 BLAS (ベクトル - ベクト ル演算), Level 2 BLAS (行列 - ベクトル演算), Level 3 BLAS (行列 - 行列演算) で構成されている. Level 1 BLAS の主な機能としては次のようなものがある. • スカラとベクトルの乗算 (y = αx + y) • ベクトルの内積計算 (β = xT y) • ベクトルのコピー (y = x, y = αx) • ベクトルノルムの計算 Level 2 BLAS の主な機能としては次のようなものがある. • 行列ベクトル積 (y = αAx + βy) • ベクトルの外積 (A = αxy T + A) Level 3 BLAS の主な機能としては次のようなものがある. • 行列積 1 回 (C = αAB + βC) • 行列積 2 回 (C = α(AB T + B T A) + βC) 2.2 サブルーチンの名称 BLAS はすべてのサブルーチンの名称のつけ方にルールを持っている. それぞれのサブルーチンにおいて先頭の文字は次のように決められている. • 単精度浮動小数点実数:S - REAL • 倍精度浮動小数点実数:D - DOUBLE PRECISION 3 2.2 サブルーチンの名称 BLAS • 単精度浮動小数点複素数:C - COMPLEX • 倍精度浮動小数点複素数:Z - COMPLEX * 16 or DOUBLE COMPLEX(if available) 行列を使う演算の場合はこれに続く文字 (列) が行列の型を表す. サポートし ている行列の型は次の通りである. • 一般行列:GE - General matrix • 一般帯行列:GB - General band matrix • 対称行列:SY - Symmetric matrix • 対称帯行列:SB - Symmetric band matrix • 対称圧縮行列:SP - Symmetric matrix stored in packed form • エルミート行列:HE - Hermitian matrix • エルミート帯行列:HB - Hermitian band matrix • エルミート圧縮行列:HP - Hermitian matrix stored in packed form • 三角行列:TR - Triangular matrix • 三角帯行列:TB - Triangular band matrix • 三角圧縮行列:TP - Triangular matrix in packed form さらに演算の種類を指定する文字 (列) がこれに続く. サポートしている演算 の種類は次の通りである. • Level 2 BLAS – 行列 - ベクトル演算:MV - Matrix-vector product – Rank-1 のアップデート:R - Rank-one update – Rank-2 のアップデート:R2 - Rank-two update – 線形方程式のを解く:SV - Solve a system of linear equations • Level 3 BLAS – 行列 - 行列演算:MM - Matrix-matrix product – Rank-k のアップデート:RK - Rank-k update of a symmetric or Hermitian matrix 4 2.3 DGEMM (倍精度行列積ルーチン) BLAS – Rank-2k のアップデート:R2K - Rank-2k update of a symmetric or Hermitian matrix – 右辺の行列に対する線型方程式を解く:SM - Solve a system of linear equations for a matrix of right-hand sides 2.3 DGEMM (倍精度行列積ルーチン) ここでは, Level 3 BLAS で規定されているサブルーチンである DGEMM につ いて解説する. DGEMM は C ← αop(A)op(B) + βC (ここで op(X) = XorX T , C は M × N の行列を表し, op(A) と op(B) はそれぞれ M × K, K × N を表し ている) の計算を行なう関数である. Level 3 BLAS で規定される多数のサブルーチンの多くは, 行列積に帰結できる ことが知られており, この DGEMM を改良することが Level 3 BLAS の大部分の 性能を改善することにつながる. また, この関数は世界中のスーパーコンピュー タの性能を計測し, そのランキングを公表するプロジェクト TOP500[2] で使用さ れる Linpack ベンチマーク [17][18] の実行時間の大部分を占めているため, これ まで多くのグループの研究対象となって来た. 第 4 章で解説する ATLAS はこの DGEMM を自動チューニングする. 2.3.1 DGEMM の一般的な実装 一般的な DGEMM 実装例について述べる. 行列積 C = AB + C の計算方法と しては, コアループでのキャッシュミスや TLB ミスを削減するため, 行列 A, B, C をブロックサイズでサブ行列に分割し, サブ行列単位で行列積を計算するのが一 般的である. サブ行列に対する行列積ルーチンは DGEMM カーネルと呼ばれる. ブロックサイズが N B の場合の DGEMM カーネルの呼び出し処理を図 5.1 に示 す. また, DGEMM カーネルの例を図 5.2 に示す. ATLAS はこの DGEMM カーネルをビルド時に自動チューニングする機能を 持つ. カーネル中の各ループのアンローリング数, ブロックサイズなどがパラメー タ化されており, ATLAS 付属のテストプログラムの演算性能を計測することで, 最適なパラメータを探索する. また, ユーザが用意したカーネルをライブラリに 組み込むこともできる. ATLAS の開発者も各ターゲットに特化したカーネルを 用意しており, ビルド時にこれらのカーネルのうちテストプログラムで最も性能 が高かったものがライブラリに組み込まれる. 5 2.3 DGEMM (倍精度行列積ルーチン) BLAS for (j=0; j<N; j+=NB) { for (i=0; i<M; i+=NB) { copy_gather (C[j][i], wC); for (k=0; k<K; k+=NB) { copy_gather (A[i][k], wA); copy_gather (B[j][k], wB); dgemm_kernel (wA, wB, wC); } copy_scatter (wC, C[j][i]); } } 図 2.1: DGEMM カーネル呼び出し処理例 for (i=0; i<NB; i++) { for (j=0; j<NB; j++) { for (k=0; k<NB; k++) { wC[j][i] += wA[i][k] * wB[j][k]; } } } 図 2.2: DGEMM カーネル例 6 2.3 DGEMM (倍精度行列積ルーチン) 2.3.2 BLAS DGEMM のインターフェース dgemm (&TRANSA, &TRANSB, &M, &N, &K, &ALPHA, A, &LDA, B, &LDB, &BETA, C, &LDC); それぞれの引数は次の通りである. ¶ ³ char TRANSA: 行列 A を転置,共役転置するか否か (”n”or”t”or”c”) char TRANSB: 行列 B を転置,共役転置するか否か (”n”or”t”or”c”) long M: 行列 A, C の行の次元数 long N: 行列 B, C の列の次元数 long K: 行列 A の列, 行列 B の行の次元数 double ALPHA: A ∗ B に付く定数倍の値 (α) double* A: 行列 A の成分が並んだ配列ポインタ (M xK 要素) long LDA: 行列 A の第一次元要素数 double* B: 行列 B の成分が並んだ配列ポインタ (KxN 要素) long LDB: 行列 B の第一次元要素数 double BETA: C に付く定数倍の値 (β) double* C: 行列 C の成分が並んだ配列ポインタ (M xN 要素) long LDC: 行列 C の第一次元要素数 µ ´ 7 第 3 章 AEOS 3.1 概要 線形代数のルーチンは一般に計算の場面で幅広く使われており, 特に科学のモ デリングで利用される. これらの多くのアプリケーションにおいて, より現実味 のある複雑なモデリングを行なうためには線形代数計算のパフォーマンスが重要 となり, 主な制約となってしまっている. そのため, 優れたパフォーマンスのルー チンは常に必要とされ続けている. よりたくさんの計算資源が利用できるように なるにつれ, その限界まで「モデルの複雑さ」や「正確さ」を向上できるはずで ある. それ故, 多くのアプリケーションは際限なく正確さを求め続けるため, 日々 性能を向上し続けるコンピュータが最適化された線形代数ルーチンを利用できる と言うことは重要なことである. 線形代数は高度に最適化できる部分をたくさん持っている. その点を考えると, 高度にチューニングされたコードは何もしていないコードのルーチンより数倍の 性能になる可能性がある. しかしながら, 最適化はプラットホーム固有のもので あるため, あるアーキテクチャに対する最適化は他のアーキテクチャにおいては 性能低下の原因となりうる. このような問題は, 伝統的には「与えられたマシン に対して” 手動” で最適化したルーチンを作る」ことで解決されてきた. しかし, これは骨の折れる作業で, 単純に「線形代数」と「計算の最適化」の両方に精通 した人員と時間を必要とする. ハードウェアは信じられないほどのペースで発展 するので, この伝統的な手法だけでは長い期間は支えきれない. 特に最適化に影 響を与える多くのソフトウェアのレイヤを考慮すると (オペレーティングシステ ム, コンパイラなど), それぞれのレイヤがそれぞれのペースで発展している. それ故, 計算分野において高度に最適化されたルーチンの生産に対する新しパラ ダイムが必要とされた. 伝統的に手動で行なわれていた一連のテクニックを実行 するこのパラダイムを”Automated Empirical Optimization of Software (AEOS)” と呼ぶ. ATLAS (Automatically Tuned Linear Algebra Software) のような AEOS を利用しているパッケージにおいて, そのパッケージは要求されたオペレーショ ン (行列積演算や FFT 演算など) の実行に関する様々な方法を提供する. そして 与えられたアーキテクチャに対して最も良いものを選択するために経験的な手法 を利用する. 従って, あるパッケージが AEOS のパラダイムを十分に満たすよう に書かれていれば, そのパッケージはトレーニングされた専門家が数ヶ月や数年 8 3.2 AEOS の役割 AEOS の間, 伝統的な方法を用いて最適化を施す代わりに, 数時間で自動的に新しいコン ピュータアーキテクチャに適したルーチンを発見する. 3.2 AEOS の役割 歴史的には, 「ピーク性能を出すソフトウェアの作成」に関する調査を行なっ てきた団体は, その目標に対して 2 つの異なる方法で追求して来た. まず, これらの成果でもっとも一般的に成功したものはコンパイラとそれに関連 する技術の調査である. コンパイラのリサーチは与えられた言語やプラットホー ムに対して, 入力として任意のコードを受け取り, 出力として完全に最適化された コードを出力することである. しかし, このアプローチに対して注がれてきた莫 大な労力にも関わらず, その成功は『実行時間 (言い換えると, ユーザーは数日か かるコンパイル時間を許容できないだろう) 』と『コンパイラが保持できる情報 量 (コンパイルされるソフトウェアについての詳細な情報と実行するだろうハー ドウェアについての詳細な情報) 』によって制限されてきた. 次に, 補足的な手段としては, 幅広く様々なアプリケーションで利用され, 最も パフォーマンスに影響を与えるカーネルルーチンを特定することである. そのよ うなカーネルを見つけることができ, その API があるコミュニティーのメンバー によって認められた時, 技術的知識を持ったプログラマーのグループはその API のカーネルライブラリを最適化することに集中できる. このような努力の最初の 例は前述の BLAS である. BLAS による経験が示してきたように, これらのライ ブラリはいくつかのハードウェアベンダ (例えば IBM , Intel) , 独立したソフト ウェアベンダ (例えば Cooke & Associates) , そして研究員によって生産された. このようになると, この API のコード利用するディベロッパはすべてのサポート されたアーキテクチャにまたがって高いパフォーマンスを達成することができる ようになった. しかしコンパイラの最適化によるピーク性能の達成に限界が来ているように, ライブラリを起源とするアプローチも同様に重要な制限を持っている. 例えば, あるオペレーションに対して最適化されたライブラリが作成されるためには, そ のオペレーションがすでに大きな負担になっているコミュニティーのメンバーに よって有効だとみなされなければならないことは明らかだろう. その上, 一度そのオペレーション (API) が有効だとみなされると, 様々なアー キテクチャに対するサポートが主な問題となってくる. 特にハイパフォーマンス を達成するために必要な最適化は全くポータブルではないのである. それは, そ のようなパフォーマンスのチューニングがハードウェアアーキテクチャの根底に ある詳細な情報を利用しているからである. もしハードウェアが変われば, 以前 の最適化は新しいプラットフォームにおいては, 性能低下の原因となるかもしれ ない. 9 3.3 基本的な AEOS の要求 AEOS プロセッサデザインがムーアの法則によって発見されたペースで変わり続ける 間, そのように手間のかかるハードウェア依存のカーネルの最適化は問題となり 続ける. しかしながら, 鍵となるライブラリがハードウェアと同じペースでアッ プデートされない限り, これらのプロセッサのパフォーマンスの向上には, 結局大 いに労力を浪費することになる. ハードウェアの生成のサイクルが小さくなるに つれて, ライブラリの手動でのアップデートはほぼ不可能となるのである. AEOS はこの問題を直接解決することができるだろう. そして, 高いパフォー マンスのライブラリが生成され, 維持される方法に重要なインパクトを与えるた めのポテンシャルを持っている. 3.3 基本的な AEOS の要求 AEOS の理論を利用しているライブラリをサポートするための基本的な要求は 次の 4 つである. 1. パフォーマンスに関わるコードの分離: 既存のライブラリーからパフォーマンスに関わるコードをサブルーチンと して分離して, 適切な API を選ばなければならない. 2. ソフトウェアを様々な環境に適応させる方法の実装: AEOS はパフォーマンスに大きく影響を与える命令の, 異なる実行方法を 繰り返し実行しようとするため, AEOS に基づくライブラリの作者は幅広 い最適化の実装を供給できるものを作成する必要がある. それを実現する 方法としては以下のようなものが考えられる. (詳しくは「ソフトウェア環 境に適応させる方法」で述べる) • パラメータを持たせる (「キャッシュサイズに合わせる」など) • コードジェネレータをつくる (どんなコードも生成できるような) どのような方法を適用したとしても, 与えられたアーキテクチャに関して, コードの柔軟性に関する境界を見積もるための機構が必要となるだろう. 3. Robust(エラーに自動で対処できるような) でそのときの状態に敏感なタイ マー: タイマーは最も良いコードを選ぶ際に使われるので正確に測定できること 重要になってくる. ほとんどのユーザーはシングルアクセスを保障されて いないので, どんなに処理のたくさんあるマシンであっても信用できる測 定が成される程度の自動的な対処が必要となる. 例えば, たいていコールド キャッシュの状態で呼ばれるのであれば, キャッシュをフラッシュすること 10 3.4 実際にソフトウェアに適用する方法 AEOS が必要であるし, もしある程度プリロードされているならそれも考慮される べきである. 4. 適当な探索方法の発見: 最適な実装を自動で探索する探索方法. 自分の手で改良する時の固定値を 提供するような単純なコードアダプテーションでは単純な線形探索で十分 である. しかし, 洗練されたコードジェネレータによって逐次に何千, 何百 のコードについて操作を行うようになってくると, できる限り早く探索木を 取り除くための洗練された探索方法が実装されるべきである. また最適の 場合を発見でき, さらになるべく早く発見できるような探索方法が実装され るべきである. 3.4 実際にソフトウェアに適用する方法 AEOS をソフトウェアに適用するためには 2 つの方法がある. それは次に説明 する parameterized adaptation と sorce code adaptation である. 3.4.1 parameterized adaptation 一般的に幅広く用いられてきた手法でマシンごとに大きく異なる特性のパラ メータ化である. 線形代数において, もっとも重要なパラメーターはブロッキン グアルゴリズムで用いられるブロッキング要素だろう (ブロッキング要素が変更 されると, データキャッシュの利用率が変わってくる). AEOS のアプローチにお いて, そのようなパラメータはコンパイル時の変数であり, 実行時のスローダウン の原因とはならない. 3.4.2 sorce code adaptation すべての重要なアーキテクチャ依存の変数を parameterized adaptation だけで 扱うことはできない (単純な例では命令キャッシュサイズ, 和積演算命令の有無, 浮動小数点とフェッチパイプラインの長さ, など). それはソースコード自体を書 き変えなければならないからである. これを解決するには, もうひとつの方法であ る sorce code adaptation を用いて, 実際に同じオペレーションの異なった実装を 生成する必要がある. sorce code adaptation には, 少なくとも 2 つの方法がある. multiple implementation もっとも単純なアプローチとして, 変化を付けた手動でチューニングした実装 を供給することである, そしてその時の探索の方法はとても単純で, 最適なものが 11 3.4 実際にソフトウェアに適用する方法 AEOS 見つかるまで順番に実装を試していく. 一見すると, このように様々な実装を提 供することは, このアプローチを「手動でチューニングされたライブラリを作成 する」という伝統的な手法より難しくしているように感じるかもしれない. しか しながら, 伝統的な手法は単によく知られたテクニックを利用しているのではな い. 例えば, L1 キャッシュのサイズや性質を知っているだけでは最も良いブロッ キングサイズを選択するのには十分ではない. これは実世界ではインターロッキ ングファクターによるからである. それ故, 手動のチューニングにおいては, 探索 を制限するためによく知られたマシンの特徴を利用することは一般的である. こ れを実装するためには, 手動で行なう代わりに探索と測定のレイヤーを追加する 必要がある. このような sorce code adaptation を multiple implementation と呼 ぶ. これはとても単純なので, この方法は高度にパラメータ化することができる. その意味では, パッケージ全体を知らない複数の作者が有意義に貢献することが できる. 特に, 与えられた複数のアーキテクチャの複数のスペシャリストが, 他の アーキテクチャや, 一段高いレベルのコードを理解する必要なく手動でチューニ ングされたルーチンを提供することができる. (一段高いレベルのコードとはタイ マー部分や探索部分や基本的なカーネルを利用するハイレベルなルーチンなどで ある). code generation code generation においては, コードジェネレータ (他のプログラムを書くプロ グラム) が必要となる. このコードジェネレータはパラメータとして, 作られる べきいくつかの sorce code adaptation を提供する. 単純な例は命令キャッシュの サイズ, 和積演算命令の有無, 浮動小数点やフェッチパイプラインの長さなどであ る. このパラメータによってマシンの特徴を引き出すのに不可欠なソースコード を生成することができる. このコードジェネレータの強みはその究極の柔軟性に ある. それによって本当に素晴らしいチューニングを行なえる人を除いて, 最も よいチューニングを行なうことができる. しかし, コードジェネレータの複雑さ は柔軟性に従って増していく傾向にある, そのためこれらのルーチンはすぐに克 服できないほどの障害となる. 12 第 4 章 ATLAS 4.1 概要 ATLAS (Automatically Tuned Linear Algebra Software) プロジェクトは様々 なアーキテクチャで高いパフォーマンスを提供するために, 経験的テクニックを 適用することに焦点を置いた調査の成果である. また, 第 3 章で少し述べたが, ATLAS は AEOS に基づいて作られた線形代数 ソフトウェアである. AEOS のような概念に基づいて作られたライブラリは他に もあり, 初めに成功したのは FFTW であり, 初めて行列積に AEOS を利用した のは PHiPAC プロジェクトである. ATLAS の特徴はコードジェネレータ (プログラムを書くプログラム) を利用す ることである. これは, あるひとつの操作を様々な方法で実行するためである. そ して, もうひとつの特徴は, 与えられたアーキテクチャに対する最も良い方法を発 見するための, 洗練されたサーチスクリプトや強固なタイマーを持っていること である. ATLAS は, 線形代数の主要なパフォーマンスのカーネルのひとつである BLAS ( Basic Linear Algebra Subprograms) のすべてのレベルと, LAPACK のいくつ かの API をサポートしている. Level 1 BLAS はベクトルとベクトルの操作を定義しており, 浮動小数点ユニッ トの有効利用やループの最適化によって約 0 ∼ 15 % の性能向上が見込める (コ ンパイラが得意な範囲なので ATLAS の効果が出る時は少ない). Level 2 BLAS は行列とベクトルの操作を定義しており, ベクトルのメモリ再利用によって, オー ダーは O(N 2 ) から O(N ) に減り, 約 10 ∼ 300 % の性能向上が見込める (例外 として特殊な行列の場合は操作を減らせることもある) . Level 3 BLAS は行列 と行列の操作を定義しており, 行列のメモリ再利用によって, オーダーは O(N 3 ) から O(N 2 ) に減り, 大変な性能向上が見込める (コンパイラには複雑すぎるため ATLAS の出番である). 4.2 ATLAS の限界 AEOS のライブラリが持つ限界を ATLAS も同様に持っている. 次の 2 つの条 件がそろわなければ ATLAS によるチューニングは全く効果がなくなってしまう. 13 4.3 ATLAS による BLAS 3 のサポート ATLAS 適当な ANSI C コンパイラ ATLAS はすばらしいコンパイラは要求しないが, アグレッシブなコンパイラは 最適化されたコードをさらに最適化しようとするため, 最適化を行わないような フラグが必要となる. また, コンパイラが ISA (Instruction Set Architecture) を 理解している必要がある. メモリ階層の存在 ブロッキングやレジスタ利用がただのオーバーヘッドにならないように, 複数 のレジスタと少なくとも L1 キャッシュがいる. 4.3 ATLAS による BLAS 3 のサポート Level 3 BLAS のルーチンは実数の範囲に 6 個, 虚数の範囲で 9 個あるが, それ らはすべて効率のよい行列積 (ここから先 BLAS 内のルーチン名である「GEMM」 と呼ぶこともある) が与えられれば, 効率的に実装することができる. よって, 主 要なパフォーマンスカーネルは GEMM である. GEMM はその内部でさらに小 さいカーネルによって構成されているので以下の節ではそのことについて説明を する. BLAS は一般的には次のような形をした GEMM のルーチンを提供している. C ← αop(A)op(B) + βC (ここで op(X) = XorX T , C は M × N の行列を表し, op(A) と op(B) はそれ ぞれ M × K, K × N を表している). 一般的には配列 A, B, C はとても大きく, キャッシュには入らないものとする. ブロッキングアルゴリズムを使えば, 行列をブロックに分割することで, ほとんど の部分のデータをキャッシュに乗せながら演算を実行するように配置することが 可能である. この BLAS ルーチンを使えば, 残りの Level 3 BLAS のルーチンを効率的にサ ポートすることができるので, GEMM は Level 3 BLAS の計算の核となる部分で ある. ATLAS はこのカーネルを parameterized adaptation と code generation の 両方からサポートしている. ATLAS の生成する高度にチューニングされたコー ドは, コンパイル時変数と実行時変数を利用してそれぞれのマシンに適応させる. この高度にチューニングされたコードはその計算の核として, ATLAS によって 生成された L1 (Level 1) cache-contained matrix multiply を利用している. 14 4.3 ATLAS による BLAS 3 のサポート 4.3.1 ATLAS 一般的な行列積の構成とパラメータによる最適化 まずこの項では, 生成する必要のない, ただのパラメータ化による最適化の方法 について説明する. 一般的な BLAS の行列積計算は L1 cache-contained matmul (ここから先 L1 matmul と呼ぶ) を用いている. ユーザーが GEMM を呼ぶときは, ATLAS は与えられた問題がコピーによる オーバーヘッドより, コピーによる性能向上が上回るかどうかを決定しなければな らない. もし入力される行列が十分に大きく, 行列をコピーするための O(N 2 ) の オーバーヘッドに耐えられるなら, ATLAS は行列 A, B をブロック単位のフォー マットでコピーする. ATLAS のブロック単位のフォーマットは行列をある連続 した NB という固定したサイズに分割する. NB は L1 キャッシュサイズを最大限 に再利用できるように選ばれたブロックサイズのことである. 一度ブロックサイ ズに分割されると, そのブロックは連続しているため, TLB 問題を削減し, キャッ シュスラッシング (cache thrashing) を最小化し, キャッシュラインの利用を最大 化する. また, ATLAS はアルファの値が 1 でなければ, A と B のより小さいほう にかけることによりコストを最小化する. さらに ATLAS は, ロードやインデク シングの最適化のために, 問題を転置した形でコピーされたブロックを使うこと もできる. デフォルトでは A は転置された形でコピーされ, B はそのままコピー される. これは ATLAS による L1 matmul が次のような形をしていることを示 している. C ← AT B + C と, C ← AT B + βC である. また, すべての次元は NB のサイズである. そして, それらのすべての次元のループは任意にアンロール することができる (もし命令キャッシュがサポートするなら, ATLAS はすべての ループを完全にアンロールすることもできる. そのときには L1 matmul はまっ たくループを持たないことになる.). さらに, コードジェネレーターがそれらの次 元を知っていると, 前もってインデクシングが行えるため, 無駄な整数やポインタ 演算を行う必要が無くなる. 与えられた問題の行列がとても小さい場合には, たとえ演算のコストが O(N 3 ) だとしても, O(N 2 ) というデータコピーのコストがアルゴリズムのコストを支配 してしまう. このような行列に対して, ATLAS はコピーを行わないで演算を実行 する L1 matmul を呼び出す. コピーを行わないで実行する L1 matmul は一般的 にはコピーを行った L1 matmul より効率的ではない. それは, コピーを行わない で演算を行うと, ユーザーによって与えられた次元を計算するために余計にポイ ンタ演算が要求されるからである. コピーの実行や無駄な演算の禁止の有無を選ぶのは AEOS のパラメータの 1 つである. コピーを行なうかどうかの境界を求めるためには, アーキテクチャと 行列の形に大きく依存するので, アーキテクチャ毎に様々な形をに試す. この問 題を扱うために, ATLAS は様々な形・様々なサイズでコピーとノンコピーの速度 を単純に比較する (いくつかのプラットフォームやいくつかの行列の形では見つ からないときもある). この交点を見つける作業はインストール時に行なわれ, こ 15 4.3 ATLAS による BLAS 3 のサポート ATLAS の時決められた値が実行時に用いられる. 一般的な行列積の計算の実行には 2 つのアルゴリズムがある. その 2 つの行列 積のアルゴリズムはループの順番が違っている. すなわち, ひとつは「 M ( 行列 A の行) を最外ループ, N (B の列) を次のループ」であり, もうひとつはその逆 である. 即ち, どちらのアルゴリズムでも, 常に最内ループは A と B の共通の次 元 (即ち K ループ) となる. ループの順番をどちらに定義したとしても, L1 matmul を一時的に Ĉ に出力 するか, 直接 C に出力するかの選択がある. C ではなく Ĉ に出力する利点は次 の 2 つである. 1. アドレスのアライメントを制御できる (キャッシュラインの境界で Ĉ を始 めることを保証できる). 2. データが連続しているので, 次元の選択ミスによる不必要なキャッシュスラッ シングの可能性を削減できる (ライトスルーではないキャッシュを仮定した 場合). 逆に, Ĉ を使った場合の欠点は L1 matmul の演算が完全に終了した後で, C へ の書き込みが追加されることである. K ループ内で L1 matmul が呼ばれる回数 が多ければ Ĉ から C にコピーするコストは最小になる. しかし, そうでない時に は重大なオーバーヘッドとなってしまう. 特に, 重要な行列積のアプリケーション は rank-K のアップデートである. ここでは, 出力となる行列 C への書き込みが アルゴリズムのコストの重要な部分となりうる. rank-K のアップデートに対して は Ĉ への書き込みはその 2 倍のコストが必要となるため, 明らかに無視すること はできない. そのため, 行列積のルーチンは「 K ループ内で呼ばれる L1 matmul の回数が Ĉ を使うのに十分な大きさであるかどうかを判断し, そうでない場合は C を利用する」ということを判断するための探索ルーチンを持っている. どちらの行列が最外ループに選ばれたとしても, 「一時的な出力 Ĉ 用の NB × NB 」「最外の行列の 1 パネル」「最内の行列全体」の 3 つをストアできるかどう かを実験してみる. もしこれに失敗したら, 「より小さいサイズの Ĉ 」と 「 A と B から 1 パネルずつ」がストアできるかを試す. もっとも小さいワークスペース は直接 C に出力する場合で 2KNB であり, そうでない場合は NB2 + 2KNB が必 要となる. これもストアする領域を確保できない場合は, 先ほど説明したコピー しない行列積が代わりに呼ばれることになる. もし最内の行列全体をコピーするスペースがあるなら, いくつかの利点が生ま れる. • それぞれの行列は一度だけコピーされる • もしワークスペース全体が L2 キャッシュに入るなら, そのアルゴリズムは 最内の行列について完全な L2 キャッシュ再利用を行う 16 4.3 ATLAS による BLAS 3 のサポート ATLAS • データのコピーは最外ループによって制限され, 内側のループが無駄なキャッ シュスラッシングを引き起こすのを防ぐ もちろん, たとえアロケーションが成功してもメモリを使いすぎることは不要 なスワップを引き起こすかもしれない. それ故, ユーザーは ATLAS に持たせる 最大のワークスペースのサイズを指定することができる. もしこの最大のワーク スペースのサイズを超えていたら, ATLAS は最内の行列をコピーしない. 最内の行列全体のコピーを行うためのスペースがアロケートされていなければ, 最内の行列は最外の行列のパネルごとにコピーされる (即ち A が最外の行列な ら, ATLAS は B[M/NB ] 会のコピーを行うだろう). さらに, 利用できる L2 キャッ シュは減少する (最内の行列のパネルのコピーは L2 キャッシュ内でそのパネル の 2 倍のサイズを占領する. 最外の行列に対しても同じことが言えるが, 初めて 2 番目のループを通る時にのみ見られる). どちらのループ構造が選ばれたか, または, どのようにデータのアロケーション が行われたかに関わらず, 最内のループは常に K を含んでいる. そのため, 最内 ループで実行される演算はまったく同じであり, 次の図 4.1 に示す通りである. 図 4.1: 行列積計算の 1 ステップ もし GEMM が Ĉ に出力をするなら, ブロック Ci,j の NB ×NB を計算するため に次の動作が実行される (ここで i, j はそれぞれ 0 ≤ i < [M/NB ], 0 ≤ j < [N/NB ] である). 1. A の行パネル i の 0 番目のブロックと B の列パネル j の 0 番目を掛け算 するために C ← AB の形の L1 matmul を呼ぶ. 2. A の行パネル i の k 番目のブロックと B の列パネル j の k 番目を掛け算 するために C ← AB + C の形の L1 matmul を呼ぶ (∀k, 1 ≤ k ≤ [k/NB ]). この L1 matmul で C ← AB + C を実行すると, 結果として A の行パネル と B の列パネルの掛け算の結果が得られる. 17 4.3 ATLAS による BLAS 3 のサポート ATLAS 3. 今 Ĉ は A の行パネルと B の列パネルの演算結果を保持しているので, ATLAS はここで Ci,j ← Ĉi,j + βCi,j を行い, C に書き戻す. もし ATLAS が直接 C に書き込む場合には, 次のような動作が実行される. 1. A の行パネル i の 0 番目のブロックと B の列パネル j の 0 番目を掛け算 するために C ← AB の形の L1 matmul を呼ぶ. このとき, ユーザーの定義 した β の値に基づいて適切な L1 matmul を選ぶ (例えば, もし β == −1 なら C ← AB − C を選択する). 2. A の行パネル i の k 番目のブロックと B の列パネル j の k 番目を掛け算 するために C ← AB + C の形の L1 matmul を呼ぶ (∀k, 1 ≤ k ≤ [k/NB ]). この L1 matmul で C ← AB + C を実行すると, 結果として A の行パネル と B の列パネルの掛け算の結果が得られる. この内側のループが構築されれば, 完全な行列積計算を行うために, ループの順 番の違う 2 つのアルゴリズムがある. 次の図 4.2 と図 4.3 はその 2 つのアルゴリ ズムの擬似コードで, 直接 C に出力する場合である (Ĉ に出力するかどうかは些 細な問題である). 単純化するために, この擬似コードは cleanup-code を含んでい ない ( cleanup-code とは NB で割り切れない部分を計算するコードである). 行 列のコピーも A, B ともに転置しない形で示されている. 4.3.2 コードジェネレータによる最適化 Level 3 BLAS をサポートするために, コードジェネレータは L1 contained matmul のみを生成する. L1 contained matmul によってサポートされる演算は次 の通りである. C ← αop(A)op(B) + βC (ここで op(X) = XorX T , C は M × N の行列を表し, op(A) と op(B) はそれぞれ M × K, K × N を表している). しか し, L1 cache-contained matmul はそのオペランドの次元が L1 キャッシュを最大 限再利用するように選ばれる. そのため, 生成されたコードは その次元を NB の サイズに分割して L1 キャッシュを利用する (cleanup の時を除く). L1 キャッシュの再利用のために設計された行列積では, 入力される行列の 1 つ を完全に L1 キャッシュに入れて, それからもう一つの入力される行列の行か列を ループすることで再利用する. ATLAS では行列 A をキャッシュにいれ, 行列 B の列をループさせている (逆でも問題ない). 2 つ, または 3 つすべての行列が L1 キャッシュに収まっていることが最適だと いう誤解がある. ライトスルーではないと仮定した場合でも, 利点はハイレベル のキャッシュへの書き込みがないだけである (たいていの L1 キャッシュはライト スルーなので, これ以上書き込みコストを抑えることは不可能である). そのため, 書き込みコストを無視して考えると, キャッシュの再利用を最大化するのは, 行列 18 4.3 ATLAS による BLAS 3 のサポート ATLAS work = allocate((M + NB) * K) if (allocated(work)) then PARTIAL_MATRIX = .FALSE. copy A into block major format else PARTIAL_MATRIX = .TRUE. work = allocate(NB * 2 * K) if (.NOT.allocated(work)) call small_case_code return end if NBNB = NB * NB do j = 1, N, NB Bwork = ALPHA * B(:, j:j+NB-1); Bwork in block major format do i = 1, M, NB if (PARTIAL_MATRIX) Awork = A(i:i+NB-1, :); Awork in block major format ON_CHIP_MATMUL(Awork(1:NBNB), Bwork(1:NBNB), BETA, C(i:i+NB-1, j:j+NB-1), ldc) do k = 2, K, NB ON_CHIP_MATMUL(Awork((k-1)*NBNB+1:k*NBNB), Bwork((k-1)*NBNB+1:k*NBNB), 1.0, C(i:i+NB-1, j:j+NB-1), ldc) end do end do end do 図 4.2: A を最内ループにした行列積 19 4.3 ATLAS による BLAS 3 のサポート ATLAS work = allocate((N + NB) * K) if (allocated(work)) then PARTIAL_MATRIX = .FALSE. copy B into block major format else PARTIAL_MATRIX = .TRUE. work = allocate(NB * 2 * K) if (.NOT.allocated(work)) call small_case_code return end if NBNB = NB * NB do j = 1, M, NB Awork = ALPHA * A(i:i+NB-1, :); Awork in block major format do i = 1, N, NB if (PARTIAL_MATRIX) Bwork = B(:, j:j+NB-1); Bwork in block major format ON_CHIP_MATMUL(Awork(1:NBNB), Bwork(1:NBNB), BETA, C(i:i+NB-1, j:j+NB-1), ldc) do k = 2, K, NB ON_CHIP_MATMUL(Awork((k-1)*NBNB+1:k*NBNB), Bwork((k-1)*NBNB+1:k*NBNB), 1.0, C(i:i+NB-1, j:j+NB-1), ldc) end do end do end do 図 4.3: B を最内ループにした行列積 20 4.3 ATLAS による BLAS 3 のサポート ATLAS A のすべてと, B の 2 列と C の 1 つのキャッシュラインがキャッシュに収まって いる時である. B に関して 2 列保持する理由は, キャッシュをオーバーフローした 時に, B の 1 列目が置き換えられ, 行列 A は常にキャッシュに収まった状態であ ることを保証するためである (キャッシュの置き換えアルゴリズムが LRU (least recently used) であると仮定している). キャッシュの再利用がパフォーマンスのほとんどを決めるが, それだけではな い. 以下の節ではデータキャッシュとは関係のない最適化の手法について述べる. 命令キャッシュの再利用 命令はキャッシュされるので, L1 matmul の命令が L1 命令キャッシュに収まる かどうかは重要となってくる. そのため, 例えば 3 つループをすべてアンロール するといったような, 命令の量が多くなりすぎるものは利用しない. 浮動小数点命令の順番 浮動小数点命令の順番を考える時には, latency hiding と, それに関連して loop skewing について考慮する. 最近のアーキテクチャはほとんどがパイプライン化された浮動小数点ユニット を持っている. これは, ある演算の結果は X サイクル後までは利用不可能であるこ とを意味している. X は浮動小数点のパイプラインの段数を表し, 一般的には 3 段 ∼ 8 段である. ATLAS の L1 matmul は C ← αop(A)op(B) +βC の形をしている ので C[X]+ = A[Y ] ∗ B[Z] と書くことができる. もし与えられたアーキテクチャ が multiply/add ユニット (積と和を同時に計算するユニット) を持っていなかった ら, このコードは不必要なストールを起こす可能性がある. register = A[Y ]∗B[Z] の操作が浮動小数点ユニットの問題を引き起こし, X サイクル後にその演算結果が 利用できるまで足し算を実行できなくする. 足し算は掛け算が終わるまでスター トできないので, 浮動小数点のパイプラインは利用できない. この解決策は掛け算と足し算を離すことで依存関係をなくし, 間にはそれらと 関係のない命令を入れる (掛け算は足し算が行われる前にデータがストアされる までの X サイクルが必要なので, 歪んだループが必要となる). この命令の並び替 えは, アウトオブオーダー実行としてハードウェアが行うか, コンパイラによって 行われる. しかし, これではたいていの場合あまり良くないコードが生成される. 最も重要なのは, オウトオブオーダー実行ができないプラットフォームにおいて 大きなパフォーマンスの改善を得ることができる点である. 21 4.3 ATLAS による BLAS 3 のサポート ATLAS ループオーバーヘッドの削減 ループのオーバーヘッドを削減する方法としてループアンローリングがある. 命令の順番を変えずにループのオーバーヘッドを減らしたい場合, A と B の共通 の次元のループ (即ち K のループ) をアンローリングしなければならない. 他の 次元 (M や N のループ) のアンロールを行うと, 命令の順番を変え, 従ってメモ リアクセスパターンを変えることになる. 並列化 多くの最近のアーキテクチャは複数の浮動小数点ユニットを持っている. それ らを完全に並列に動かすためには 2 つの障害がある. • ハードウェア的にすべての浮動小数点ユニットはメモリアクセスを必要と するので, 完全な並列化のためにはメモリフェッチも並列に行われる必要が ある. • コンパイラが配列のタイミングを認識することと, コンパイラがソフトウェ アの支持に従うことが必要となる. この解決策は M と N (M または N ) を正しい回数アンロールし, 正しいレジスタアロケーションを選ぶことで解 決できる. 正確なキャッシュミス回数の発見 レジスタ上に無い命令やデータはメモリからフェッチされなければならない. も しそのデータが L1 キャッシュに無ければ, 実行を遅らせてより遠いメモリ階層か らフェッチしてこなければならない. ブロッキングをせずに同時に引き起こされ るキャッシュミスの回数はアーキテクチャによって様々である. メモリコストを最 小化するために, すべてのメモリがキャッシュの中にあるか使われるまで, キャッ シュミスの最大数は各サイクルで引き起こされるべきである. ATLAS はキャッ シュのヒット率をコントロールする方法として, もっともシンプルな M と N の ループアンローリングを行う. コードジェネレータのパラメータ コードジェネレータはすべてにおいて柔軟にするために, 様々なパラメータ化 が行われている. 実際には, 次のようなオプションがある. • A, B をそれぞれ転置した形でストアするか, そのままストアするか 22 4.3 ATLAS による BLAS 3 のサポート ATLAS • 行列積を計算する上でもっとも最適なレジスタブロッキング. レジスタブ ロッキングのパラメータを変化させると, たくさんの違った行列積が生成で きる. そのレジスタブロッキングのパラメータは, – ar :A の要素に対して使われるレジスタ数 – br :B の要素に対して使われるレジスタ数 である. このレジスタブロッキングは, ar × br 個のレジスタが C の要素に対 して使われることを意味している. 従って, もし浮動小数点ユニットの個数 を調べる間に見つけられたレジスタの総数が Nr ならば ar br + ar + br ≤ Nr を満たす ar , br をすべて探索しなければならない. • ループアンローリング:matmul に含まれる 3 つのループのそれぞれの次 元 (M, N, K) でそれぞれに関する (mu , nu , ku ) のアンローリング要素があ る. ここで M, N のアンローリングはレジスタブロッキングのために制限 されるが, K は任意の深さにアンロールすることが可能である (即ち ar が 選ばれると, mu も同様にセットされるが, ku は独立した変数である). • 浮動小数点命令の選択: – 積和演算があるならば, それに合ったパイプライニング – 積和演算が無いならば, 積と和の関係に合うパイプライニングと loop skewing (レイテンシの数だけ積と和の演算の距離を離す). • すべてのループに対して, 利用可能な生成時の定数や実行時の変数の選択 (M, N, K は M = N = K = NB ). 生成時にわかっているそれぞれの次元 は, 次のような最適化が成されている. – もしアンローリングの段数がループの回数と同じかそれを超えている 場合には, ループは生成されない (完全にアンローリングされていたら ループは必要ない). – ひとつもアンロールされていなければ, if を使わずに正しい cleanupcode が生成される (従ってループ内での分岐を避けることができる). • それぞれの配列に対して, その次元はインデックス計算を省くことに関連し ている生成時の定数 (例えば L1 matmul にコピーされるサイズは NB とわ かっている) となるか, または実行時の変数となるかもしれない. • それぞれの配列に対して, その次元がストライドを決定する (ストライドは 1 となることが一般的だが, 複素数演算をサポートするためにストライドは 2 となりうる). 23 4.3 ATLAS による BLAS 3 のサポート ATLAS • ジェネレータは特別なアルファ (1, -1, それ以外), ベータ (0, 1, -1, それ以 外) に関してコードを生成する時, 不要な演算を削除することができる. さ らに, アルファとベータがともに変数である時には特別な場合があるが, そ れはベータをアルファで割っても安全なときである (これでアルファを掛け ることを省略することができる). • A と B をレジスタにロードするための様々なフェッチパターン – CPU がサポートする outstanding load の数 – 最内ループ (k ループ) の初期のロード数 – 行列 C からの初期のロードの使用の可否 4.3.3 すべてを考慮した探索の概要 ATLAS はコードジェネレータと共に, いくつかの初期情報を受け取ることの できるタイマーを提供する. そしてループアンローリングやレイテンシを隠すた めの様々な戦略を実験し, 最も良いパフォーマンスを示すものを選び出す. タイマーが組み込まれているので, 命令は部分ごとに区切られ, マルチユーザー の環境においても繰り返しの実験を行った結果を導くことができる. また, すべ ての中間結果は保存されているので途中から再開することも可能である. 最初に L1 キャッシュのサイズを測定する. これはある一定の数のメモリ参照の 実行によって行われる. メモリ参照の総量を連続的に減らしていくのである. 連 続して測定している間の最も大きなギャップによって L1 キャッシュの境界を知 ることができる. 探索のスピードのために, 2 のべき乗だけが実験される. これ は, 例えば 48 KB のキャッシュは 32 KB として認識されるだろうことを意味し ている. 次に, ATLAS は与えられたプラットフォームの浮動小数点のユニットについ て考慮するための情報を調査する. まず, muladd ユニットの有無を調べる. その ために, 足し算と掛け算を結合したものと分割したものを使って簡単なコードを 生成する. それぞれに対して様々なパイプラインの段数を示すコードを使って実 験する. レジスタをあふれて性能が落ちるまでレジスタを使う数を増やしていき, 最も良いコードを採用する. このデータで実際に L1 matmul を測定する準備が できる. このような測定で, ATLAS に L1 キャッシュサイズ, 浮動小数点命令, パイプラ インの深さ, おおよそのレジスタの数を渡す. 与えられた L1 キャッシュサイズに よってブロッキングの妥当なサイズを決め, 浮動小数点命令によって探索領域を 半分に減らし, レジスタの最大数によってレジスタブロッキングの適当な値を知 り, M , N のループの深さを決定する. これらによって大幅に探索時間を削減す ることができる. 24 4.4 ATLAS による探索の詳細 ATLAS 実際, K のアンロールは 1 または K がベストな場合が多いため, 初めはその 2 通りだけを調べる. これはインストール時の探索時間を減らす. そして, インス トールプロセスの最後に, ベストコードを見逃さないために, すべての K につい て探索を行う. 理論的には flops / load を最大にするレジスタブロッキングは, ar br +ar +br ≤ Nr を満たす, 正方形に近い形のものである. ATLAS のジェネレータは ar = mu , br = nu のように生成するので, これら M, N のループは初期のブロッキング要 素を発見するために使われる. 最初のブロッキング要素は先に述べたようなルー プアンロールによって発見される. どんなブロッキング要素が「発見された L1 キャッシュのサイズ」に適しているかを調べることが, 最も良い結果を生成する. この初期のブロッキング要素とここまでで決定された命令セット (muladd を 利用するか, それとも別々の命令を利用するか) を用いて, 与えられたレジスタの 数において可能な M と N のループアンロールすべてを調査する. 一度最適なアンロールが発見されたら, ATLAS はもう一度すべてのブロッキ ングサイズについて実験し, 様々なレイテンシと K のループアンローリングにつ いても実験し, 最も良いものを選択する. すべての結果はファイルに出力されているので, 途中の探索において同じ探索 は繰りえされない. これはもし探索が中断されたとしても, 以前に行った部分は 再度行わないことを意味している. 一般的にはそれぞれの精度において 1, 2 時間 かかる. 4.4 4.4.1 ATLAS による探索の詳細 探索の全体像 ATLAS は mmsearch.c の中で図 4.4 のように探索を進めていく. この mmserch.c が ATLAS におけるサーチエンジンである. まず, GetNumReg() や GetMulAdd() によって, 「レジスタ数, 掛け算と足し 算を同時に計算するユニット (muladd と呼ぶ) の有無, だいたいのレイテンシ」 を測定する (L1 キャッシュは, これより前に L1CacheSize.c というファイル内の L1CacheSize() で 測定されている). これらの値は mmsearch() に渡され, それぞ れ探索空間を制限するために用いられる. 次に, mmsearch() 内では「ブロックサ イズ (N B), アンローリングの段数 (M U, N U, KU ), レイテンシ, 各種フェッチの サイクル, muladd の利用の可否」を決定する. mmcase() は, これらの値に基づ いて DGEMM カーネルの生成・実行・測定を行ない, 測定結果は逐一ファイル に出力されていく. その実行結果は次に生成される DGEMM カーネルの性能と の比較に使われ, 最終的に最も性能が良かったものが選択される. 25 4.4 ATLAS による探索の詳細 ATLAS 図 4.4: 行列積探索の概要 4.4.2 探索の手順 この項では, より具体的な ATLAS の探索の順序を示す. また, それぞれの関数 がどのように各種値を取得しているのか, そこで決定された値は何に使われるの かも共に示す. 全体の流れは図 4.5 の通りである. 1. L1 キャッシュサイズの測定: これは L1CacheSize() の中で行なわれる. 1, 2, 4, 8, …256 KB のサイズの 配列を順次アクセスさせて, ある一定の割合 (8 % ) 以上性能が低下したサ イズを L1 キャッシュサイズと L2 キャッシュサイズの境界とみなす (例え それが実際の L1 キャッシュサイズではなくとも). 256 KB までで見つから なかった場合は 32 KB とする. ここで測定された値, ブロックサイズの探 索範囲を決定する時に利用される. 2. muladd の有無・仮のレイテンシ・レジスタ数の測定: まず, GetMulAdd() で muladd ユニットの有無を調べる. これは関数内で は足し算と掛け算を結合したもの (例: c += c * c;) と分割した簡単なコー ド (例: c += c; c = c * c;) を生成し, 単純に性能を比較することで行なわ れる. ここで muladd ユニットが有ると決定されると, ここから先の探索は すべて muladd を利用して測定する. 同時に, この関数で足し算と掛け算の距離を 1 ずつ離したコードを生成・実 行し, 最も良い性能を示したレイテンシを決定する. この値もここから先す べての探索で利用される. また, M に対するアンロール段数 (mu), N に対するアンロール段数 (nu) を変更しレジスタをあふれて性能が落ちるまでレジスタを使う数を増やす ことでレジスタ数を決定する. ここで測定されたレジスタ数は, 次の「仮の mu, nu」のために利用される. 26 4.4 ATLAS による探索の詳細 ATLAS 図 4.5: 具体的な行列積探索 27 4.4 ATLAS による探索の詳細 ATLAS 3. 仮の mu, nu を計算する: この段階では, まだすべてのアンロール段数を測定するのではなく, 次のよ うな式に基づいて M に対するアンロール段数 (mu), N に対するアンロー ル段数 (nu) を計算している. (mu ∗ nu) + mu + nu ≤ レジスタ数 の式を満 たす mu, nu の中でもほぼ mu = nu となるものがいくつか選択され, 生成・ 実行される (この時のブロックサイズは GetSafeNBs() L1 キャッシュを使い きるサイズが選ばれる). mu = nu が選択されるのは, この値が理論的には 演算数あたりのロード命令数が小さく, 性能が良いことが多いからである. これは FindMUNU() で行なわれている. ここで決められる mu, nu は, 仮 のブロックサイズの決定で利用されるだけで, 必ずしも最終的なアンロール 段数ではない. 4. ブロックサイズ (N B) の探索範囲を計算する: findNBs() において, 初めに測定した L1 キャッシュサイズを基に, N B ∗ N B 個の要素が L1 キャッシュに収まる範囲を計算する. ここで N B の探索範 囲の最大値は 80 であり, L1 キャッシュのサイズがとても大きい場合でも N B ≥ 80 の範囲は探索しない. また, 探索の間隔は 4 である (例: 16, 20, 24, …, 80). これ以降, この N B の値についてブロックサイズを探索して いく. 5. 仮の mu, nu で仮のブロックサイズを測定: 「3.」で計算した仮の mu, nu と, 「4.」で計算した N B のリストを用いて 行列積プログラムを生成・実行し, 最も性能の良いブロックサイズを決定す る (ここでは ku については ku = 1, ku = K についてのみ計測している). これは mms case() を繰り返し呼ぶことで行なわれる. mms case() 内では 1 つのブロックサイズについて 3 回測定し, その平均の性能を採用する. こ こで, 3 回それぞれの性能が 20 % 以上違っている場合はもう一度測定する. ここで決定したブロックサイズは最終的な mu, nu を決定するために用い られるだけで, 必ずしも最終的なブロックサイズではない. 6. 仮のブロックサイズで最終的な mu, nu を測定: 「5.」で決定されたブロックサイズで, muladd 無しの場合は (mu ∗ nu) + mu + nu + latency ≤ レジスタ数 を満たす mu, nu について, また muladd 有りの場合は (mu ∗ nu) + mu + nu + 1 ≤ レジスタ数 を満たす mu, nu につ いて行列積プログラムを生成・実行し, 最も性能の良い mu, nu を決定する (ku については 3 での測定で最も性能が良かった時のものが使われる). こ れは searchmu nu() で行なわれ, searchmu nu() 内で mms case() を繰り返 し実行している. ここで決定した mu, nu が最終的に生成される DGEMM カーネルに利用される. 28 4.4 ATLAS による探索の詳細 ATLAS 7. 最終的な mu, nu で最終的なブロックサイズを測定: 「6.」で決定された mu, nu の値を用いて, もう一度 NB のリストのブロッ クサイズをすべて測定するし, 最も性能の良いブロックサイズが選択され る (ku は 3 での測定で最も性能が良かった時のものが使われる). これは mms case() を繰り返し呼ぶことで行なわれる. ここで決定したブロックサ イズが最終的に生成される DGEMM カーネルに利用される. 8. 最終的なレイテンシの測定 ここまで決定された muladd の利用, mu, nu, ku, ブロックサイズでレイテ ンシを 1 ∼ 6 (例外的に Itanium 2 プロセッサのみ 1 ∼ 14 ) の範囲をもう 一度探索し, 最も性能が良かったレイテンシが決定される. ここで決定した 値が最終的に生成される DGEMM カーネルに利用される. 9. フェッチサイクルの測定: 最後に FindFetch() で, ここまでで決定された値を用いて, 3 つのフェッチ サイクルを測定する. まず, CPU がサポートする outstanding load の数 (IF etch) が 2 ∼ (mu + nu) の範囲で測定される. 次に, 最内ループの初期 のロード数 (N F etch) が 1 ∼ (mu + nu − IF etch) の範囲で測定される. 最 後に, 行列 C からの初期のロードの使用の可否 (F F etch) が測定される. 10. GEMM カーネルの決定: 以上で決定された値で最適な GEMM カーネルが決定される. 29 第 5 章 ATLAS 改良の提案 ATLAS の改良に関しては大きく分けて 2 つの方向性があると考えられる. ひ とつは ATLAS の生成するライブラリの性能向上であり, もうひとつは ATLAS の探索時間の削減である. 本研究では, 探索時間についてはあまり考慮せず, 妥当 だと思われる時間内に探索が終了すれば良いことにする. ATLAS の生成する GEMM はかなり高性能ではあるものの, アーキテクチャ の特性の考慮が不足している. この章では ATLAS の考慮すべきアーキテクチャ の特性と ATLAS の探索プログラム自体の問題点について考慮する. 続いて, そ れらの問題点を解決する ATLAS の改良を提案する. 第 2 章で説明したが, 本章でも参照するため, 再度, 一般的な DGEMM の実装 を示しておく. for (j=0; j<N; j+=NB) { for (i=0; i<M; i+=NB) { copy_gather (C[j][i], wC); for (k=0; k<K; k+=NB) { copy_gather (A[i][k], wA); copy_gather (B[j][k], wB); dgemm_kernel (wA, wB, wC); } copy_scatter (wC, C[j][i]); } } 図 5.1: DGEMM カーネル呼び出し処理例 5.1 5.1.1 考慮すべきアーキテクチャの特性 レイテンシ隠蔽機能 近年のプロセッサは, いくつかのキャッシュレイテンシの隠蔽機能を持っている. 30 5.1 考慮すべきアーキテクチャの特性 ATLAS 改良の提案 for (i=0; i<NB; i++) { for (j=0; j<NB; j++) { for (k=0; k<NB; k++) { wC[j][i] += wA[i][k] * wB[j][k]; } } } 図 5.2: DGEMM カーネル例 Xeon や Pentium 4 プロセッサ, Athlon 64 や Opteron プロセッサ, G5 などに おいては, ハードウェアプリフェッチと呼ばれる機能が備わっている. これはハー ドウェア的にレイテンシを隠蔽する機能で, 例えば Xeon プロセッサにおいては, データのアクセスが 4 K ページ当たり 1 つのストリームの場合, そのストリー ムを自動的に追いかけて 256 バイト先読みを行ない, フェッチしたデータを L2 キャッシュに移す. また, Itanium 2 プロセッサにおいては, 投機的に命令を発行 することにより, 命令で発生する遅延を隠蔽するスペキュレーション機能, ロード およびストアされるデータが置かれるキャッシュ階層を制御する機能であるキャッ シュヒントなどが備わっている. しかし, ATLAS はこれらのレイテンシ隠蔽機能については考慮されていない. そこで, 次の 2 点の改良を加えることでレイテンシ隠蔽機能を利用し, 性能の向 上を図る. • アンロール段数の探索領域の拡張 • ブロックサイズの探索領域の拡張 アンロール段数 DGEMM カーネルにおいて, i ループと j ループのどちらか一方, もしくは双 方をアンローリングすることで演算あたりのロード命令を削減することができる ことは第 4 章で述べた通りである. 第 4.4 節で ATLAS の探索プログラムについて詳しく述べたが, ATLAS は M に対するアンロール段数 (mu), N に対するアンロール段数 (nu) の探索範囲を (mu∗nu)+mu+nu+latency ≤ レジスタ数 (または, (mu∗nu)+mu+nu+1 ≤ レ ジスタ数) と定めている. これは理論的には flops / load を最大にするレジスタ ブロッキングはこの式を満たし, mu = nu に近い形になるからである. しかし, これではハードウェアプリフェッチを最大限利用することはできない. そ れは, ハードウェアプリフェッチは 4 K ページ内に 1 つのストリームがある時のみ 31 5.1 考慮すべきアーキテクチャの特性 ATLAS 改良の提案 効果を発揮するため, データの流れが 1 つである必要があるからである. ストリー ムが 1 つでレジスタを最大限に利用できるコードは (mu ∗ nu) + min(mu, nu) + 1 + latency ≤ レジスタ数 の式の範囲にあると考えられる. 図 5.3 はレジスタが 8 本の場合の探索範囲を示している. 斜線部分はこの式の変更によって新しく探索 する範囲である. 図 5.3: 変更後のアンロール段数探索域 (レジスタが 8 本の場合) また, 他の論文 [3] で発表された Xeon における最適な DGEMM カーネルの命 令列の一部を図 5.4 に示す. これは (6, 1) の時の命令列であるが, このようにレ ジスタを利用することができれば性能が向上することはわかっている. ブロックサイズ ATLAS はブロックサイズを L1 キャッシュサイズ, またはブロックサイズ 80 と定めている. しかし, 近年のプロセッサにはキャッシュレイテンシを隠す機能が あるので, L1 キャッシュのサイズだけを探索したり, ブロックサイズを 80 に制限 することはより性能の高いブロックサイズを見逃す可能性がある. しかし, 無駄 に探索領域を広げないように, 最大でも L2 キャッシュ, または L3 キャッシュまで を探索領域とする. ブロックサイズは, 基本的には DGEMM カーネルの j ループ 1 巡で使用する データがキャッシュに収まる範囲で最大のものを選択するのが望ましい. キャッ シュには行列 C と行列 wB は一列分, そして wA の全データが収まる必要があ 32 5.1 考慮すべきアーキテクチャの特性 ・ ・ ・ movapd movapd mulpd addpd movapd mulpd addpd movapd mulpd addpd movapd mulpd addpd movapd mulpd addpd movapd mulpd addpd ・ ・ ・ ATLAS 改良の提案 wB[j][k] wA[i][k] reg6 reg7 wA[i+1][k] reg6 reg7 wA[i+2][k] reg6 reg7 wA[i+3][k] reg6 reg7 wA[i+4][k] reg6 reg7 wA[i+5][k] reg6 reg7 reg6 reg7 reg7 reg0 reg7 reg7 reg1 reg7 reg7 reg2 reg7 reg7 reg3 reg7 reg7 reg4 reg7 reg7 reg5 図 5.4: 手動で生成した最適な DGEMM カーネルの命令列 33 5.1 考慮すべきアーキテクチャの特性 ATLAS 改良の提案 るため, ブロックサイズ N B は次の条件を満たす必要がある. 8Byte ∗ (N B ∗ (N B + 2)) ≤ キャッシュ容量 図 5.5: DGEMM カーネルの j/i ループ内で参照されるデータ 5.1.2 ハードウェア固有の命令 SSE2, Enchanced 3DNow!, AltiVec などの SIMD (Single Instruction / Multiple Data) 命令を用いることにより, 1 命令で複数の浮動小数点演算を行なうことが可 能となる. しかし, ATLAS はこれらの命令については考慮されていない (AEOS において ISP を知っていることはコンパイラの役目というスタンス). Xeon における最適な DGEMM カーネルの命令列の一部を図 5.4 に示したが, ここでは SSE2 命令の中でも packed 命令と呼ばれる 2 つの浮動小数点を同時に 演算する命令が使われている. ATLAS は AEOS の概念に基づいて作られている ため, SIMD 命令への対策は何も行わない. しかし, SIMD 命令を発行しやすい コードの生成を行なうか, コンパイラのオプションを変更することで, SIMD 命令 を有効活用できれば, 性能が向上する可能性がある. また, SIMD 命令を生成しや すいコードを生成するように改良できれば性能向上の余地がある. 34 5.2 考慮すべき探索プログラムの設計 5.1.3 ATLAS 改良の提案 FSB 動作周波数 / FSB 帯域 FSB 動作周波数 / FSB 帯域の性能は向上しているものの, システムバス負荷 が偏って発生することは望ましくない. ATLAS はこのシステムバス負荷につい ては考慮されていない. DGEMM カーネル内で発生するキャッシュミスの中でも, 特に行列 wB データ の参照は性能に対して大きな影響がある. ここで行列 wB データの参照について 考えると, 行列 wB の参照箇所は j ループによって決まり, i ループの 1 巡目の みキャッシュミスが発生する. j ループ 1 巡の間に参照される行列 wB の総デー タサイズは N B ∗ 8Byte であり, ここで偏ったシステムバス負荷をかけることに なる. 図 5.6: DGEMM カーネルの j / i ループ内で参照されるデータ この偏ったシステムバス負荷を i ループ内において少しずつロードすること で均等化する. 例として図 5.7 では (6, 1) の時のシステムバス負荷を考慮した DGEMM カーネルを示す. 5.2 5.2.1 考慮すべき探索プログラムの設計 ブロックサイズの探索 第 4.4 節で ATLAS の探索プログラムについて詳しく述べたが, 最終的なブロッ クサイズの決定で探索する ブロックサイズは M, N に対するアンロールの段数 35 5.2 考慮すべき探索プログラムの設計 ATLAS 改良の提案 for (j=0; j<NB; j++) { for (i=0; i<NB; i+=6) { for (k=0; k<NB; k++) { C[j][i ] += wA[i ][k] C[j][i+1] += wA[i+1][k] C[j][i+2] += wA[i+2][k] C[j][i+3] += wA[i+3][k] C[j][i+4] += wA[i+4][k] C[j][i+5] += wA[i+5][k] } load (wB[j+1][i]); } } * * * * * * wB[j][k]; wB[j][k]; wB[j][k]; wB[j][k]; wB[j][k]; wB[j][k]; 図 5.7: システムバス負荷を考慮した DGEMM カーネル に関係なく 4 おきに探索されている. 一般的には M, N のアンロール段数の倍数を外れた時には端数を処理するとい う無駄な処理が発生するために性能が低下するはずであるので, 必要なところを 探索せず, 無駄な部分を探索していることになる. また, より性能が良いブロック サイズを見逃す可能性も高い. そこで, アンロールの段数にしたがったブロックサイズ探索幅を指定するよう にプログラムを修正する. 具体的には以下のようなブロックサイズ探索幅で探索を行なう. 1. mu = 1, nu = 1 の場合: ブロックサイズ探索幅は 1 とする. 2. mu = 1 ∧ nu != 1 または mu != 1 ∧ nu = 1 の場合: 1 ではないアンロール段数をブロックサイズ探索幅とする. 3. mu = α nu または nu = α mu の場合 (α は自然数): アンロール段数の小さいほうをブロックサイズ探索幅とする. 4. 1. ∼ 3. 以外の場合: mu, nu それぞれの倍数を探索する. 36 5.2 考慮すべき探索プログラムの設計 5.2.2 ATLAS 改良の提案 K に対するアンロール段数の探索 第 4.4 節で述べたが, 最終的に K に対するアンロールの段数を探索する際, M, N に対するアンロールの段数に関係なく 4 おきに調べられている. 一般的には M, N のアンロール段数の倍数を外れた時には端数を処理するとい う無駄な処理が発生するために性能が低下するはずであるので, 必要なところを 探索せず, 無駄な部分を探索していることになる. また, より性能が良いアンロー ル段数を見逃す可能性も高い. そこで, M, N のアンロールの段数にしたがった K のアンロール段数探索幅を 指定するようにプログラムを修正する. 具体的には以下のような K のアンロール段数探索幅で探索を行なう. 1. mu = 1, nu = 1 の場合: ブロックサイズ探索幅は 1 とする. 2. mu = 1 ∧ nu != 1 または mu != 1 ∧ nu = 1 の場合: 1 ではないアンロール段数を K のアンロール段数探索幅とする. 3. mu = α nu または nu = α mu の場合 (α は自然数): アンロール段数の小さいほうを K のアンロール段数探索幅とする. 4. 1. ∼ 3. 以外の場合: mu, nu それぞれの倍数を探索する. 5.2.3 カーネル呼び出し部分の最適化 ATLAS はカーネル部分の最適化は行なっているが, カーネル呼び出し部分に ついては固定の関数を利用している. カーネル呼び出し部分にも改良の余地があると考えられる. • ループ順序: DGEMM カーネルのループの順序決定後に, 呼び出し部分のループ順序も 考慮する. 行列 A のデータはサイズが大きいためできる限りキャッシュ内 にデータを残したい. そこで, カーネルのループ順序が JIK と決まった場 合は, 行列 A のデータは j には依存しないため, 呼び出し部分のループは k → i → j とする (図 5.8). • 行列データのワーク領域へのコピー: カーネル呼び出し部分で k ループを最外ループとすると, 行列 C のデータ は i ループ, j ループが一巡するまで入れ替えが起こらない. そのため, 行 列 C のデータはワーク領域にはコピーしない (図 5.9). 37 5.2 考慮すべき探索プログラムの設計 ATLAS 改良の提案 • 行列 B のワーク領域へのコピー回数削減: j ループ方向で行列を分割するのをやめることで, 行列 B データのワーク領 域 wB へのコピー回数を削減し, ループのオーバーヘッドを削除する. (図 5.10). 図 5.8: ループ順序とデータアクセス 図 5.9: 行列 C のワーク領域へのデータコピーの削除 図 5.10: 行列 B のワーク領域へのコピー回数削減 38 第 6 章 評価と考察 実際に ATLAS に改良を加え, 性能評価を行なう. また, それぞれの結果に対し て考察も加える. 6.1 実験内容と結果 第 5 章で提案した改良案のうち, 次の 4 つの改良を ATLAS に施して最適な DGEMM の探索を行った. • L2 / L3 キャッシュのサイズを測定し, それらの値をブロックサイズの決定 に利用した. • ハードウェアプリフェッチを考慮し, 探索する m, n のアンロール段数を拡 張した. • 固定された幅で探索されていたブロックサイズを, m, n のアンロールの段 数に基づいて探索を行なうようにした. • 固定された幅で探索されていた k のアンロールの段数をブロックサイズや 他のアンロール段数に基づいて探索を行なうように改良した. 以下の項では, 実験を行ったプロセッサごとに, 実験環境, 改良前・改良後それ ぞれで発見された最速の DGEMM のパラメータと性能, ブロックサイズと性能 の関係を示したグラフを示す. それぞれのプロセッサについて Intel Compiler と Gnu Compiler で性能を測定したが, ここには 2 つのうち性能が良かったものに ついてのデータを示す. 39 6.1 実験内容と結果 6.1.1 評価と考察 Celeron プロセッサ 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 900 MHz 8本 8 KB 128 KB 無し 無し 表 6.1: Celeron プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT NB MU NU KU MFLOPS 改良前 0 5 40 2 1 40 622.26 0 5 44 2 1 44 622.35 改良後 表 6.2: gcc における Celeron プロセッサのベストパラメータ 40 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.1: Celeron のブロックサイズと性能 (全体) ! "# 図 6.2: Celeron のブロックサイズと性能 (詳細) 41 6.1 実験内容と結果 評価と考察 PentiumIII プロセッサ 6.1.2 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 1.2 GHz 8本 16 KB 512 KB 無し 無し 表 6.3: PentiumIII プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT 改良前 0 3 0 3 改良後 NB MU NU KU MFLOPS 44 4 1 44 917.11 172 4 1 172 948.93 表 6.4: gcc における PentiumIII プロセッサのベストパラメータ 42 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.3: PentiumIII のブロックサイズと性能 (全体) !" 図 6.4: PentiumIII のブロックサイズと性能 (詳細) 43 6.1 実験内容と結果 評価と考察 Pentium 4 プロセッサ 6.1.3 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 2.4 GHz 8本 8 KB 512 KB 無し 有り 表 6.5: Pentium 4 プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT NB MU NU KU MFLOPS 改良前 0 1 28 4 1 28 1997.08 0 1 92 4 1 92 2140.73 改良後 表 6.6: gcc における Pentium 4 プロセッサのベストパラメータ MULADD LAT 改良前 0 3 0 1 改良後 NB MU NU KU MFLOPS 28 4 1 28 1932.89 116 4 1 116 1988.05 表 6.7: icc における Pentium 4 プロセッサのベストパラメータ 44 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.5: Pentium 4 のブロックサイズと性能 (全体) 図 6.6: Pentium 4 のブロックサイズと性能 (詳細) 45 6.1 実験内容と結果 評価と考察 Pentium M プロセッサ 6.1.4 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 1.5 GHz 8本 64 KB 1 MB 無し 有り 表 6.8: Pentium M プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT NB MU NU KU MFLOPS 改良前 0 3 56 4 1 56 1351.85 0 3 60 4 1 60 1374.26 改良後 表 6.9: gcc における Pentium M プロセッサのベストパラメータ MULADD LAT NB MU NU KU MFLOPS 改良前 0 3 60 3 1 20 1335.77 0 3 60 3 1 60 1364.73 改良後 表 6.10: icc における Pentium M プロセッサのベストパラメータ 46 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.7: Pentium M のブロックサイズと性能 (全体) 図 6.8: Pentium M のブロックサイズと性能 (詳細) 47 6.1 実験内容と結果 6.1.5 評価と考察 Xeon プロセッサ 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 2.4 GHz 8本 8 KB 512 KB 無し 有り 表 6.11: Xeon プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT 改良前 0 1 0 1 改良後 NB MU NU KU MFLOPS 24 3 1 24 1881.67 150 3 1 150 2082.10 表 6.12: gcc における Xeon プロセッサのベストパラメータ MULADD LAT 改良前 0 4 0 1 改良後 NB MU NU KU MFLOPS 28 3 1 28 1822.11 108 3 1 108 2083.62 表 6.13: icc における Xeon プロセッサのベストパラメータ 48 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.9: Xeon のブロックサイズと性能 (全体) 図 6.10: Xeon のブロックサイズと性能 (詳細) 49 6.1 実験内容と結果 評価と考察 Itanium 2 プロセッサ 6.1.6 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 1.4 GHz 128 本 128 KB (浮動小数点には利用されない) 256 KB 3 MB (ユニファイド・キャッシュ) 無し 表 6.14: プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT 改良前 1 4 1 2 改良後 NB MU NU KU MFLOPS 80 10 10 4 3522.06 240 10 10 4 3736.99 表 6.15: gcc における Itanium 2 プロセッサのベストパラメータ MULADD LAT 改良前 1 5 1 6 改良後 NB MU NU KU MFLOPS 80 5 4 1 4101.77 152 4 7 1 4899.95 表 6.16: icc における Itanium 2 プロセッサのベストパラメータ 50 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.11: Itanium 2 ブロックサイズと性能 (全体) 図 6.12: Itanium 2 ブロックサイズと性能 (詳細) 51 6.1 実験内容と結果 評価と考察 Athlon MP プロセッサ 6.1.7 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 2 GHz (2400+) 8本 128 KB 256 KB 無し 無し 表 6.17: Athlon MP プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT NB MU NU KU MFLOPS 改良前 0 3 68 2 3 68 2198.70 0 3 90 3 2 90 2278.84 改良後 表 6.18: gcc における Athlon MP プロセッサのベストパラメータ MULADD LAT NB MU NU KU MFLOPS 改良前 0 1 76 4 1 76 1850.56 0 1 84 4 1 84 1897.44 改良後 表 6.19: icc における Athlon MP プロセッサのベストパラメータ 52 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.13: Athlon MP のブロックサイズと性能 (全体) ! "# 図 6.14: Athlon MP のブロックサイズと性能 (詳細) 53 6.1 実験内容と結果 評価と考察 Opteron プロセッサ 6.1.8 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 2 GHz 16 本 64 KB (ユニファイドキャッシュ) 1 MB (ユニファイドキャッシュ) 無し 有り 表 6.20: Opteron プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT NB MU NU KU MFLOPS 改良前 1 3 76 1 3 76 2043.13 改良後 1 2 84 3 1 20 2025.34 表 6.21: gcc における Opteron プロセッサのベストパラメータ MULADD LAT 改良前 1 4 改良後 1 3 NB MU NU KU MFLOPS 76 2 2 76 2072.43 219 3 3 219 2172.99 表 6.22: icc における Opteron プロセッサのベストパラメータ 54 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 図 6.15: Opteron ブロックサイズと性能 (全体) ! 図 6.16: Opteron ブロックサイズと性能 (詳細) 55 6.1 実験内容と結果 6.1.9 評価と考察 G4 プロセッサ 実験環境 CPU クロック レジスタ数 L1 キャッシュサイズ L2 キャッシュサイズ L3 キャッシュサイズ HW プリフェッチ 32 本 32 KB 256 KB 2 MB 有り 表 6.23: G4 プロセッサの特徴 最高の性能を示した DGEMM カーネル MULADD LAT 改良前 1 4 1 4 改良後 NB MU NU KU MFLOPS 56 5 4 1 1234.94 124 5 4 15 1280.70 表 6.24: gcc における G4 プロセッサのベストパラメータ 56 6.1 実験内容と結果 評価と考察 ブロックサイズと性能の関係 ! 図 6.17: G4 のブロックサイズと性能 (全体) 図 6.18: G4 のブロックサイズと性能 (詳細) 57 6.2 結果のまとめ 6.2 評価と考察 結果のまとめ 表 6.25, 表 6.26 に実験の結果のまとめを示す. 表 6.25 において, 「○」は改良の効果が見られたことを示し, 「△」は改良の 効果が見られなかったことを示す. 「△」というのは, 改良が失敗したわけでは なく, 改良前でも発見できる範囲がベストなパラメータとして選択されたことを 示す. また, 表 6.26 はそれぞれのアーキテクチャにおいて, 改良前後の性能と性能向 上率が示されている. Celeron-gcc Pentium III-gcc Pentium 4-gcc Pentium 4-icc Pentium M-gcc Pentium M-icc Xeon-gcc Xeon-icc Itanium 2-gcc Itanium 2-icc AthlonMP-gcc AthlonMP-icc Opteron-gcc Opteron-icc G4-gcc mu,nu 探索域拡大 NB 探索域拡大 NB 探索間隔改良 ku 探索間隔改良 △ △ △ △ △ ○ △ △ △ ○ △ △ △ ○ △ △ △ △ △ △ △ △ △ △ △ ○ ○ ○ △ ○ △ △ △ ○ △ △ △ ○ △ △ △ ○ ○ ○ △ ○ △ △ △ △ △ △ △ ○ ○ ○ △ ○ △ △ 表 6.25: 改良の効果 6.3 6.3.1 性能に関する考察 それぞれの改良について ブロックサイズの探索域拡大 それぞれのアーキテクチャの「ブロックサイズと性能」のグラフからもわかるよ うに, ほとんどのアーキテクチャにおいて, ブロックサイズの探索領域は L1 キャッ シュサイズまでにするのではなく, L2 / L3 キャッシュのサイズまで探索を行なえ ば, より性能の高いブロックサイズを発見できていることがわかる. 58 6.3 性能に関する考察 評価と考察 Celeron-gcc Pentium III-gcc Pentium 4-gcc Pentium 4-icc Pentium M-gcc Pentium M-icc Xeon-gcc Xeon-icc Itanium 2-gcc Itanium 2-icc AthlonMP-gcc AthlonMP-icc Opteron-gcc Opteron-icc G4-gcc 改良前 622.26 917.11 1997.08 1932.89 1351.85 1335.77 1881.67 1822.11 3522.06 4101.77 2198.7 1850.56 2043.13 2072.43 1234.94 改良後 622.35 948.93 2140.73 1988.05 1374.26 1364.73 2082.1 2083.62 3736.99 4899.95 2278.84 1897.44 2044.13 2172.99 1280.7 性能向上率 100% 103% 107% 103% 102% 102% 111% 114% 106% 119% 104% 103% 100% 105% 104% 表 6.26: 改良前後の性能比較 全体に共通して言えることは, L1 キャッシュをぎりぎり使いきれるブロックサ イズまでは徐々に性能を向上させ, L1 キャッシュをはずした直後に性能が落ちて いる. 当たり前ではあるが, L1 キャッシュを越えた直後はほとんどすべてのロー ドが L1 キャッシュをはずし, 毎回 L2 キャッシュからのロードが行なわれている からであると思われる. ここから先はアーキテクチャによって性能の上下は異な るが, 大きく分けて 2 つに分けられる. 1 つは L2 キャッシュサイズまで徐々に 性能をあげるもの, もうひとつはそのまま徐々に性能が低下するもの, である. こ こで, ブロックサイズを L2 / L3 まで広げることで性能の向上が見られたもの は PentiumIII, Pentium 4, Xeon, Itanium 2, AthlonMP, Opteron, G4 である (表 6.25). 全体的に, ブロックサイズのグラフは性能の上下が激しい. 一見, 再現性がない ように思えるかもしれないが, ブロックサイズのグラフを見てもわかる通り, 3 回 の性能測定を行ない, ほぼ重なっているので, 再現性があることがわかる. 念のた め Xeon プロセッサに関しては全部で 15 回の実験を行なったが, ある程度の性 能の差はあるものの, 性能が高いブロック数は常に性能が高く, 性能が低いブロッ ク数は常に性能が低いと言う結果がでた (図 6.19). そこにはキャッシュのライン アドレスやアソシアティビティ, TLB ミスなどアーキテクチャ的な性能低下の要 因が複雑に重なり合って性能の低下を招いていることが見て取れる. 何度も測定 して性能が高かったブロックサイズは, これらアーキテクチャ的な要因による性 59 6.3 性能に関する考察 評価と考察 能低下がない部分なのではないかと推測している. 図 6.19: ブロックサイズと性能の再現性 (Xeon-gcc) mu, nu のアンロール段数探索域拡大 この改良で効果があったアーキテクチャは存在しなかった. しかし, 少なくと も Xeon プロセッサに関しては, レジスタをうまく使うことができれば探索範囲 を広げた部分により性能が高い部分があることは [3] によりすでに証明されてい る. そこで, 実際に ATLAS に (mu, nu) = (6, 1) のものを生成させ, それを gnu compiler, Intel compiler によってアセンブリに変換させてみたところ, やはり手 動で生成するようなレジスタの使い方ができていないことがわかった. 今後コンパイラの性能が向上し, 手動で生成するようなレジスタの使い方がで きるようになれば, この改良が役に立つはずである. ブロックサイズ探索間隔の変更 この改良はいくつかのプロセッサでは効果が確認できた. しかし, ほとんどの アーキテクチャはアンロール段数が 2 や 4 が選択されることが多く, 改良前の 4 の倍数だけを調べるというもので事足りてしまっていた. この改良も, コンパイ ラの性能が向上し, mu, nu のアンロール段数がもう少し違った値が選ばれるよう になって来ると効果が発揮されると予想される. ku のアンロール段数探索間隔の変更 この改良もいくつかのアーキテクチャでは効果が確認できたが, ATLAS の論 文 [1] に書かれている通り, k ループのアンロール段数は 1 またはブロックサイズ 60 6.3 性能に関する考察 評価と考察 と同じ段数になることが多いため, あまり効果がなかった. 逆に ku がそれらの値 ではない中途半端な値が選択されたものに関しては追って調査をすれば, より良 い k のアンロール段数の探索方法見つかるかもしれない. 6.3.2 手動との性能比較 SIMD 命令のあるアーキテクチャについて 自分でそれぞれのマシンの最適な DGEMM を見つけることはできなかったため, まずは発表されている論文??を参 考に Xeon プロセッサにおける最適な DGEMM を生成し, ATLAS のタイマーで 測定してみたところ, 約 4100 Mflops の性能がでることがわかった. それに比べ て ATLAS が Xeon プロセッサに対して最適だとした DGEMM の性能は 2083 Mflops である. 性能が約 2 倍違っている. この原因はまず第一にコンパイラが SIMD (Single Instruction / Multiple Data) 命令を適切に利用できないことにあ る. この命令は 2 つの浮動小数点の演算を 1 つの命令で実行するため, 適切に利 用されれば単純に 2 倍の性能が得られるのである. ATLAS の生成するコードを Intel compiler で最大限 SIMD 命令を生成するようなオプションを付けても望ん だ SIMD 命令は利用されていなかった. 単純に比較することはできないものの, もしも今回 ATLAS が生成したプログ ラムがそのまま高度に SIMD 命令を利用したら, 自分が論文を参考に手動で作っ た DGEMM を超える性能を出せているのかもしれない. また, レジスタの使い方にも限界があることは以前に述べた通りである. SIMD 命令のないアーキテクチャについて ここでは 32 Bit のプロセッサとし て PentiumIII を, 64Bit のプロセッサとして Itanium 2 を選んで性能を比較して みる. まず PentiumIII だが, 手動で生成したものの中では ATLAS を開発した R. Clint Whaley 氏の提供している DGEMM がブロックサイズ 40, (mu, nu, ku) = (2, 1, 40) という値で 951.43 Mflops の性能を出した. 一方, 今回改良を加えた ATLAS は 948.93 Mflops という性能を出せている. ほぼ同じ性能まで近づけて いることがわかる. 次に Itanium 2 だが, これも手動で生成したものの中では ATLAS を開発した R. Clint Whaley 氏の提供している DGEMM がブロックサイズ 120, (mu, nu, ku) = (8, 8, 2) という値で 5031.14 Mflops の性能を出している. それと比較して, 今 回改良を加えた ATLAS は 4899.95 Mflops の性能を出した. R. Clint Whaley 氏 の提供するパラメータの値は改良後の ATLAS は調べているはずなので, 単純に パラメータが問題ではなく, 違う要因があると考えられるが, 今回はその原因究明 までは至らなかった. これら 2 つのプロセッサからもわかるように, SIMD 命令のないアーキテクチャ については, かなり手動で生成した DGEMM の性能に近づけていることがわか 61 6.4 探索時間に関する考察 評価と考察 る. SIMD 命令のような特殊な命令を使わなければ, コンパイラの性能はあまり 要求されないこともわかった. 6.4 探索時間に関する考察 ATLAS の探索の基本は「コード生成, コンパイル, 性能測定」の 3 つであるた め, これらの総量が探索時間となる. コードの生成にはほとんど時間はかからな いため, 一回の計測には「コンパイル時間」と「3 回の性能測定」が主な探索時 間となる. ブロックサイズの探索域拡張が探索時間に与える影響は, 単純に探索領域を拡 大した分だけ探索時間が増えることになる. さらに, ブロックサイズの探索は 1 つの精度について 2 回行なわれるため, その倍の時間増えることになる. また, mu, nu のアンロール段数探索域拡張は, 探索範囲を表す式の変更にとも なって探索範囲は約 2 倍になるため, 最適なアンロール段数の探索にかかる時間 も倍に増えることになる. ブロックサイズ探索間隔の変更と ku のアンロール段数探索間隔の変更の 2 つ の改良に関しては選ばれるアンロール段数によって変わってくるが, 固定間隔で 探索していた時と大差がない場合が多い. 以上の考察により, ブロックサイズと mu, nu の探索領域の拡張が探索時間に 影響を与えていることがわかるが, ほとんどのアーキテクチャにおいて, 一回の 計測の時間は 3 秒程度なので, 全体でも 1 時間も探索時間は伸びていない. しか し, Itanium 2 のように命令の同時実行が行なえるようなプロセッサにおいては, k のアンロール段数が増えれば増えるほど, 最内ループ内での命令の順序 (同時実 行の仕方) の組み合わせが増えるため, コンパイラはそのどれが最適なのかを試 すためにコンパイルに大変な時間を要するようになって来る. 例えば, Itanium 2 においてブロックサイズ 300, ku = 300 などをコンパイルしようとすると 10 分 以上コンパイルに時間がかかる. このようなアーキテクチャにおいては ku の扱 いを考えなければならないだろう. 62 第 7 章 関連研究 7.1 手動での DGEMM の改良 それぞれのアーキテクチャに合わせた DGEMM を生成するために, 手動で改 良を行なう研究は今まで数多く行なわれている. Xeon プロセッサにおいては, キャッシュ容量, TLB エントリ数, システムバス負 荷の均等化を考慮して DGEMM カーネルを最適化し, それに合わせて DGEMM カーネル呼び出し部分を変更する [3] ことで ATLAS を超える性能を引き出すこ とに成功している. また, Pentium 4 プロセッサにおいては DGEMM カーネルの命令スケジュー リングの改良, プリフェッチ命令の導入, DGEMM カーネルの呼び出し部分の最 適化を行なう [4] ことで ATLAS の性能を超えることに成功している. 7.2 7.2.1 自動チューニング AEOS に基づくライブラリ AEOS のような概念に基づいて設計されたライブラリには BLAS のサブルーチ ンである DGEMM に焦点を当てた PHiPAC[10], FFT に焦点を当てた FFTW[11][12][13] や SPIRAL[14] などが存在する. これらのアプリケーションはそれぞれコードジェ ネレータや独自のタイマーを持ち, 最適化されたライブラリを生成する. FFTW, SPIRAL に関しては, DFT を解くための様々なアルゴリズムの組み合わせとスケ ジューリングによって最適な FFT ライブラリの生成を行なっている. 7.2.2 探索時間の削減と性能向上 AEOS のように広い探索空間を持たないようにして探索時間を削減しながら, 性能向上を目指す研究も行なわれている. SANS system[15] では Nelder-Mead simplex method と呼ばれる最適化の探索 方法を ATLAS に適用することで探索時間を削減し, 性能の向上に成功している. 63 7.2 自動チューニング 関連研究 また, キャッシュのアソシアティビティ・ラインサイズ・入れ替えポリシー・レ ジスタ数などから妥当なパラメータを推測し, 大幅に探索空間を狭める [16] こと に成功している論文も発表されている. 64 第 8 章 まとめと今後の課題 本章では, 本研究の成果をまとめ, 今後の課題を述べる. 8.1 まとめ ATLAS の改良により, 様々なアーキテクチャのマシンで, 自動的により性能の よい DGEMM を生成することができるようになった. 次々と新しいアーキテク チャが開発されていく中, 既存の ATLAS より性能の高いライブラリの自動生成 を実現した本改良の有用性は高い. また, 今回効果の見られなかったアンロール段数の探索範囲の拡大も, 今後コン パイラの性能が向上し, 高度にレジスタを利用できるようになれば効果を発揮す ると考えられる. 8.2 8.2.1 今後の課題 カーネル部分の最適化 システムバス負荷の均等化 ATLAS は自動でプリフェッチ命令やプリロードを挿入しない. そこで ATLAS がそれらの命令を挿入できるように改良することで性能が向上する可能性がある. DGEMM カーネルテストプログラム ATLAS は独自のカーネルテストプログラムを持っているが, 対象とするソフト ウェアが決まっている上で DGEMM カーネルを生成する場合, 対象とするソフ トウェアと同じ配列アクセスパターンでカーネル測定を行なったほうがよい. そ のため, BLAS がよく利用されるプログラム用のテストプログラムを用意するこ とが有効であると考えられる. 65 8.2 今後の課題 まとめと今後の課題 SIMD 命令を生成しやすいコード生成 本研究過程で, ATLAS の生成するコードをコンパイルする場合, 現在のコンパ イラでは, どのようなオプションを用いても質の高い SIMD 命令を生成すること ができないことがわかっている. 質の高い SIMD 命令を生成するようなコードの 書き方が判明し, その書き方が他のアーキテクチャにマイナスの影響を与えない ことが証明できれば, SIMD 命令を有効活用しながら自動生成ができると考えら れる. キャッシュヒントを生成しやすいコード生成 レイテンシを隠す機能として Itanium 2 にはキャッシュヒントと呼ばれる機能 が備わっている. SIMD 命令と同様に現在のコンパイラではこれらを効率よく生 成できないことが判っている [19]. これらをうまく生成するようなコードの書き 方が判明し, その書き方が他のアーキテクチャにマイナスの影響を与えないこと が証明できれば, キャッシュヒントを有効活用しながら自動生成ができると考え られる. もしくは, キャッシュヒントを効率よく自動的に付加することを可能にし たコンパイラである CHSU[19] を利用することも有効であると考えられる. 8.2.2 カーネル呼び出し部分の最適化 ATLAS はカーネル呼び出し部分についての最適化は行なっていない. そこで, カーネル呼び出し部分に最適化を加えることができれば性能向上の可能性がある. 最適化の方針としては, 「カーネルに合った呼び出し部分の生成」や「呼び出し の順序の最適化」等が考えられる. 8.2.3 探索時間の短縮 キャッシュのアソシアティビティ・ラインサイズ・入れ替えポリシー・レジス タ数などから妥当なパラメータを推測し, 大幅に探索空間を狭めることで, 性能 を落とさずに探索時間のみを削減することができる可能性がある. コンパイルに 時間がかかる Itanium 2 や G5 といったプロセッサには大変有効であると考えら れる. 66 謝辞 本研究を進めるにあたり様々な方の指導・助言をいただきました. まず, ご指 導を賜わった上田 和紀教授に深く感謝致します. また, 並列班の先輩である稲垣 良一氏, ならびに共同研究者の大橋 智明氏に深く感謝致します. 本研究の出発点であり土台ともなった数値計算ライブラリ ATLAS を開発した R. Clinton Whaley 氏に感謝致します. また, 本研究のアイデアの元となった論文“ Xeon プロセッサ向け Linpack ベ ンチマーク最適化手法とその評価 ”を発表した成瀬 彰氏に感謝いたします. 最後に, 入学から現在に至るまで叱咤激励を私にかけてくれた家族に感謝いた します. 67 参考文献 [1] R. Clinton Whaley, Antoine Petitet, Jack Dongarra: Automated Empirical Optimizations of Software and the ATLAS project, Parallel Computing 27(12), pp. 3–35 (2001) [2] Super Computer TOP500. http://www.top500.org [3] 成瀬彰, 住元真司, 久門耕一: Xeon プロセッサ向け Linpack ベンチマーク最 適化手法とその評価, 先進的計算機基盤システムシンポジウム SACSIS2004, pp. 287–294. [4] 西村祥治, 荒木拓也, 蒲池恒彦: Pentium III, Pentium 4 における LINPACK ベンチマークチューニング手法, 先進的計算機基盤システムシンポジウム SACSIS2003, pp. 129–136. [5] BLAS ( Basic Linear http://www.netlib.org/blas/ Algebra Subprograms ). [6] J. J. Dongarra, J. Du Croz, S. Hammarling, and R. J. Hanson: An extended set of FORTRAN Basic Linear Algebra Subprograms, ACM Trans. Math. Soft., 14 (1988), pp. 1–17. [7] J. J. Dongarra, J. Du Croz, I. S. Duff, and S. Hammarling: A set of Level 3 Basic Linear Algebra Subprograms, ACM Trans. Math. Soft., 16 (1990), pp. 1–17. [8] R. Clinton Whaley: User contribution to ATLAS, January 21, 2003. [9] R. Clinton Whaley: Automatically Tuned Linear Algebra Software (ATLAS) http://math-atlas.sourceforge.net/ [10] J. Bilmes, K. Asanovic, J. Demmel, D. Lam, and C. Chin: Optimizing Matrix Multiply using PHiPAC: a Portable, High-Performance, ANSI C Codeing Methodology. Technical Report UT CS-96-326, LAPACK Working Note No.111, University of Tennessee, 1996. 68 8.2 今後の課題 まとめと今後の課題 [11] M. Frigo and S. G. Johnson: The Fastest Fourier Transform in the West, Technical Report MIT-LCS-TR-728, Massachusetts Institute of Technology, 1993. [12] Matteo Frigo: FFTW: An Adaptive Software Architecture for the FFT, In Proceedings of the ICASSP Conference, volume 3, pp. 1381-1384, 1998 [13] Matteo Frigo: A Fast Fourier Transform Compiler, In Proceedings of the 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI ’99), Atlanta, Georgia, May 1999. [14] Markus Puschel, Bryan Singer, Jianxin Xiong, Jose Moura, Jeremy Johnson, David Padua, Manuela Veloso, and Robert W. Johnson: SPIRAL: A Generator for Platform-Adapted Libraries of Signal Processing Algorithms, Journal of High Performance Computing and Applications, special issue on Automatic Performance Tuning, 18(1), 2004, pp. 21-45. [15] Bosilca, G, Chen, Z, Dongarra, J, Eijkhout, V, Fagg, GE, Fuentes, E, Langou, J, Luszczek, P, Pjesivac-Grbovic, J, Seymour, K, You, H, and Vadhiyar, SS: Self Adapting Numerical Software SANS Effort, IBM Journal of Research and Deve, 2005. [16] Kamen Yotov, Xiaoming Li, Gang Ren, Michael Cibulskis, Gerald DeJong, Maria Garzaran, David Padua, Keshav Pingali, Paul Stodghill, Peng Wu: A comparison of empirical and model-driven optimization, Proceedings of the ACM SIGPLAN 2003 conference on Programming language design and implementation, June 09 - 11, 2003, pp. 63–76. [17] A. Petitet, R. C. Whaley, J. Dongarra, A. Cleary: HPL - A Portable Implementation of the High-Performance Linpack Benchmark for DistributedMemory Computers, January 20, 2004. [18] 笹生健, 松岡 聡: HPL のパラメータチューニングの解析, ハイパフォーマン スコンピューティング, No.91-22, 2002. [19] 稲垣良一: Itanium2 プロセッサにおけるキャッシュヒント自動付加手法早稲 田大学大学院理工学研究科情報科学専攻修士論文, 2005. 69
© Copyright 2024 Paperzz