...

Common Lisp言語処理系によるBroadwellの 任意精度整数演算命令の

by user

on
Category: Documents
11

views

Report

Comments

Transcript

Common Lisp言語処理系によるBroadwellの 任意精度整数演算命令の
Bulletin of Aichi Univ. of Education, 65(Natural Sciences),pp. 25 - 28, March, 2016
Common Lisp 言語処理系による Broadwell の
任意精度整数演算命令の評価
安本 太一
情報教育講座
Evaluation of Broadwell Instructions Supporting Large Interger
Arithmetic on a Common Lisp System
Taichi YASUMOTO
Department of Information Sciences, Aichi University of Education, Kariya 448-8542, Japan
ための命令として,mulx,adcx,adox が用意されて
1 はじめに
いる.
インテル社の第 5 世代 Core プロセッサ Broadwell マ
イクロアーキテクチャに実装されている任意精度の整
数演算のための命令
[1, 2]
の評価を,Common Lisp[3]
mulx は,64 ビット(あるいは 32 ビット)の符号な
し整数どうし src1,src2 の掛け算を行う機械語命令
言語処理系である KCL(Kyoto Common Lisp)[4, 5]
mul の拡張版である.アセンブラ表記は次の通りであ
り,64 ビットの場合は rdx を,32 ビットの場合は edx
の bignum(無限長整数)の掛け算を用いて行ったの
を src2 として使う.
で,その結果を報告する.なお,本論文では,任意精
mulx dest_hi, dest_lo, src1
度整数は一般的な用語,bignum は Common Lisp の無
限長整数を示すものとして扱う.
その動作は
任意精度の整数演算をとりあげるとき,基本データ
dest_hi:dest_lo = src1 r/edx
型である整数の配列
(例えば 32 ビットや 64 ビットの整
数の配列)で,基本データ型の整数よりも長い任意精
のように,ソースオペランド src1 と src2 の積を計算
度の整数を表現することが多い[1, 2]
.基本データ型
である整数の演算を何度も適用して,任意精度の整数
し,その結果を,2 つのレジスタを連結したディスティ
ネーションオペランドである dest_hi:dest_lo に格
の演算を行うのである.一方,KCL の bignum は,配
納する.フラグレジスタは変更されない.実質 4 オペ
列でなく,リスト形式で,C 言語の基本データ型の整
ランドの命令なので,ソースオペランドの値を破壊し
数を連結した表現で実現されている.
配列形式の任意精度整数において,インテル社が提
ないことが可能である,
adcx と adox は,adc(キャリー付き加算命令)の拡
案して実装した任意精度の整数演算のための命令が速
張版であり,アセンブラ表記は次の通りである.
度向上に寄与するのは,掛け算に必要な命令数が減っ
adcx dest/src1, src2
ていることがインテル社のドキュメントおいて示され
adox dest/src1, src2
ているので,明らかである[1]
.
そこで,今回は,これらの命令を,実際の Lisp 言
語 処 理 系 に お いて,リスト形式で表現され て い る
これらの2つの命令は,src1と src2のキャリー付き加
算を行い,その結果を dest に格納する.adcx と adox
bignum における掛け算で用いた場合は,どれくらい
の違いは,CF フラグと OF フラグの扱いである.adcx
の速度向上が得られるのか調べてみた.
は,CF フラグを用いてキャリーインしてキャリーア
ウトし,OF フラグを変更しない.adox は,OF フラグ
2 Broadwell における任意精度整数演算のため
の命令
を用いてキャリーインしてキャリーアウトし,CF フ
ラグを変更しない.adcx と adox を使い分けることに
よって,2 つのキャリーフラグのチェーン(流れ)を
2.1 mulx, adcx, adox 命令
作ることができる.
Broadwell には,任意精度整数演算をサポートする
例 え ば,mulx,adcx,adox を 使 う と,64 ビ ッ
― 25 ―
安本 太一
トの整数を 8 個連結して表現した 512 ビットの整数
A7 A6 . . . A1 A0,B7 B6 . . . B1 B0 の積を,効率よく計算でき
3 KCL の bignum
る.筆算のように,A7 A6 . . . A1 A0 × Bi を繰り返して行
KCL の bignum は,図 1 に示す bignum セル(無限
うとき,A7 A6 . . . A1 A0 × Bi–1 の結果と加算していくこ
長整数セル)の連結によって実現されている.この
とになるが,Aj × Bi は 2 語になるので,
“フラグを変
図で示すのは 64 ビット版の KCL における表現であり
更しない掛け算命令”と“キャリーの流れを 2 つ実現
[7, 8],本論文では 64 ビット版の KCL に 64 ビット版の
mulx と adcx を適用することのみを扱う.
できる加算命令”があると,命令数が少なくて済むか
らである[1]
.
固定長の任意精度整数のように 64 ビットの整数領
なお,今後は,32 ビットについては言及せず,64
域を連続してメモリ領域に配置した表現ではなく,連
ビットにのみに言及する.
続した領域が確保できなくても,メモリの許す限り無
限長の整数を表現できるよう,このような構造になっ
2.2 高級言語からの利用
ている.計算の過程で,変数から参照されなくなった
mulx,adcx,adox 命令は,次に示すように,C 言語
bignum が使用していた bignum セルの領域は,ごみ集
(C++ 言語を含む)から次のような関数呼び出しの形
めで回収され,再利用可能なメモリ領域として利用さ
で利用可能であると,ICC(Intel C++ Compiler)を発
れる.
売しているインテル社のドキュメントに記述されてい
る[1]
.gcc の場合は,_umul128 ではなく _mulx_u64
である.関数呼び出しの形式ではあるが,gcc-4.9 コン
パイラにおいて,call 命令を伴わなずに,アセンブラ
命令(機械語命令)に展開されることを確認している.
mulx に対応.返り値が lo_dest /
unsigned _ _int64
/
umul128(unsigned _ _int64 src1, unsigned _ _int64 src2, unsigned _ _int64
hi_dest);
図 1:64 ビット環境下の bignum セルの構造
adcx と adox に対応,返り値が c_out /
unsigned char
/
bignum を構成する整数を表現するためのフィール
_addcarryx_u64(unsigned char c_in, unsigned _ _int64 src1, unsigned _ _int64 src2, unsigned _ _int64
ドとして 64 ビット確保しているが,実際に整数を表現
しているのは下位 63 ビットである.上位 1 ビットは一
時的な利用場所で,例えば,下位 63 ビットに整数を加
sum_out);
算した直後のキャリー検出に使用し,キャリー検出に
キャリーのための C 言語の変数(c_in, c_out に対
応する変数)
の名前を使い分けて,_addcarryx_u64 を
対応した処理を行った後はすぐ 0 にされる.
使えば,コンパイラの方で,adcx と adox を使い分け
bignum の掛け算を実現するための内部ルーチンと
して,extended_mul(long int d, long int q, long
るコード生成をすると,インテル社のドキュメントに
int r, long int   hp, long int   lp) を使用してい
は記述されている[6]
.ICC の場合は,そうなるのか
る.d × q + r を計算し,その結果を,hp と lp が指す
もしれないが,gcc-4.9 ではそのようにはならず,C 言
場所に格納する.d, q, r は,64 ビットの非負整数であ
語の変数を用いてキャリーフラブを陽に設定するコー
る.lp が指す場所には,結果の下位 63 ビットを格納
ドが生成されていた.
し,最上位ビットは 0 である.hp が指す場所には,結
AVX の SIMD 命令に対応する C 言語の関数が用意
果の残りのビットを格納し,最上位ビットは 0 である.
されるといったことはあったが,上記のように逐次実
extended_mul は,当初は実行効率を重視してイン
行の機械語命令に対応する C 言語の関数が用意される
ラインアセンブラで記述されていたが,KCL の移植
ことは珍しい.高級言語から特定の機械語命令を明示
の容易性を重視して C 言語バージョンが用意されてい
的に指定できるので,キャスト演算子を駆使するなど
る.KCL の配布ソースには,アセンブラバージョンは
プログラムの書き方を工夫して特定の機械語命令が生
成されるようコンパイラに期待するいう,不安定でや
32 ビットプロセッサ向けのみが含まれているので,今
回,64 ビット版の mulx と adcx の使用を試みることは
りにくい方法をとる必要がない.
意義がある.大雑把にいうと 64 ビット版の C 言語の
バージョンは,図 2 に示すように,64 ビットの掛け算
― 26 ―
Common Lisp 言語処理系による Broadwell の任意精度整数演算命令の評価
を,被乗数 d と乗数 q を 32 ビットに分けて計算するよ
うになっていた.オーバーフローを防ぐためである.
本図において,r の加算,積の結果の lp や hp への 63
ビットずつの配置は省略されている.
図 3:任意精度整数演算のための命令を利用した C 言語版
extended_mul
図 2:オリジナルの C 言語版 extended_mul の概要
4 任意精度整数演算をサポートする命令 mulx,
adcx の利用
先述の _mulx_u64と _addcarryx_u64を用いて書き
換えた,C 言語バージョンの extended_mulを図 3 に示
す.gcc-4.9ののヘッダにあわせて,unsigned_int64で
はなく,unsigned long longを使用しているが,64
なものである.再帰的にしなかったのは,fact の引数
が大きい場合,スタックが足りなくなり,実行が途中
で中断してしまうからである.関数 fact は,実行する
前にコンパイルしたので,インタプリタ実行ではない.
extended_mul コンパイル時の最適化オプション
(-O)なしで構築した KCL で(fact 10000)を実行し
た場合の実行時間を,表 1 に示す.10 回計測し平均値
ビット環境下では,unsigned long long は long int
をとったものである(以下同様).この実行中 565 回の
とともに,64 ビットである.
ごみ集めが行われ,そのための時間も実行時間に含ま
積 d × q は 4 つに分けることなく _mulx_u64 で一
気に行う.和 +r は _addcarryx_u64 を用いて行う.
れている.最適化オプションなしなので,gcc が C 言
語の関数 extended_mul を翻訳して生成したアセンブ
キャリーは,C 言語の変数 carry で明示的に受け渡し
ラコードは,(ロード・ストア命令 movq を主とする不
する.その結果,extended_mul の(ブロックの内側
の)行数は,オリジナルの C 言語版と比べて 40% 程度
必要な命令を大量に含む)冗長なものである.mulx,
adcx 使用のバージョンは,オリジナルと比べて,約
に減少した.
9% の実行時間短縮が得られている.
一方,extended_mul コンパイル時の最適化オプ
ション(-O)ありで構築した KCL で(fact 10000)
5 評価実験
を実行した場合の実行時間を,表 2 に示す.この実行
mulx, adcx の使用の有無によって,階乗のプログラ
ムの実行時間がどのように変化するか調べた.また,
extended_mul を(定義している KCL のソースファイ
ル earith.c を)gcc でコンパイルするときの最適化オプ
ション(-O)の有無によって,階乗のプログラムの実
表 1:(fact 10000) の実行時間(-O 最適化なし)
extended_mul のバージョン
実行時間
比
オリジナル
1.943
1.0
mulx, adcx 使用
1.765
行時間がどのように変わるかも調べた.
実行環境は,アップル社製 MacBook Pro
(Retina, 13inch, Early 2015)で,搭載している CPU は Intel Core
i7-5557U 3.1GHz で あ る.OS は OS X 10.10.5,gcc の
バージョンは4.9である.実験に用いた階乗のプログラ
ムは(fact 10000)を計算するもので,階乗を計算す
る関数 fact の定義は do を用いた繰り返し的な典型的
― 27 ―
0.908
(単位は秒)
表 2:(fact 10000) の実行時間(-O 最適化あり)
extended_mul のバージョン
実行時間
比
オリジナル
1.744
1.0
mulx, adcx 使用
(手修正なし)
1.733
0.994
mulx, adcx 使用
(手修正あり)
1.728
0.991
(単位は秒)
安本 太一
中にも,565 回のごみ集めが行われ,そのための時間
も実行時間に含まれている.最適化オプションありな
ので,gcc が C 言語の関数 extended_mul を翻訳して
7 まとめ
Broadwell の任意精度整数演算命令を,Lisp 言語処
生成したアセンブラコードは,先述のような不必要な
理系の bignum の掛け算の中で用いることで,評価し
命令を含まず,簡潔なものである.
“手修正”という
た.
のは,carry という変数を使って C 言語レベルでキャ
mulx 命令の意義は,64ビットの整数の積を確実に高
リーの受け渡しを行っていたものを,機械語レベルで
行うよう,gcc が生成した extended_mul のアセンブ
速に行うことと,フラグを変更しないことであること
ラコードを,手作業で修正したもので,機械語レベル
で命令数が減る(図 4 参照)
.コンパイラの最適化オプ
分割して 4 つの積を行う方法をとるとき,これらの積
が同時実行できる場合は,mulx 命令に実行効率が近づ
ションありの場合は,オリジナルと比べて,手修正な
くが,生成される機械語命令の列によっては,常に同
しの場合は約 0.5% の実行時間短縮が,手修正ありの場
時実行できるとは限らないからである.
合は約0.9% の実行時間短縮が得られている.コンパイ
adcx が C 言語から使えるようなったことは,C 言
ラの最適化オプションなしの場合に比べて,実行時間
語によるコード記述が簡潔になる利点があるが,C 言
短縮(速度向上)の効果は,ごくわずかである.
語の変数を介するオーバーヘッドがあらわれる.コン
が,明確にわかった.被乗数と乗数を 32 ビットずつに
パイラによる最適化においては,キャリーフラグの
チェーンをみつけ,C 言語の変数へのアクセスを排除
して,adcx(や adox)の特徴を生かしたコード生成
が行われることが望まれる.
参考文献
[1]Intel Corporation: New Instructions Supporting Large Integer Arithmetic on Intel Architecture Processors, Document
number: 327831-001(2012)
.
[2]Intel Corporation: Large Integer Squaring on Intel Architecture Processors, Document number: 328569-001(2013)
.
[3]Steele, G. L. Jr.: Common Lisp the language, Digital Press
(1984)
.
[4]Yuasa, T. and Hagiya, M.: Kyoto Common Lisp Report,
図 4:gcc -O が出力したアセンブラのコードの手修正
­Teikoku Insatsu Publishing(1985)
.
[5]Yuasa, T.: Design and Implementation of Kyoto Common
Lisp, Journal of Information Processing, Vol. 13, No. 3, pp.
284-295(1990)
.
6 考察
[6]Intel Corporation: User and Reference Guide for the Intel
C++ Compiler 15.0(2015)
.
gcc の最適化オプションを指定しない場合,
(fact
10000)の実行時間が約 9% 短くなったことは,mulx や
[7]安本太一:Common Lisp 言語処理系の 64 ビット化,愛知教
adcx の使用の効果によるものだと推測される.実行時
[8]安本太一:Common Lisp 言語処理系による 64 ビット環境の
間には 565 回も行われたごみ集めなど extended_mul
育大学研究報告 自然科学編,五十三輯,pp. 27-32(2004).
評価,愛知教育大学研究報告 自然科学編,五十五輯,pp.
9-13(2006).
以外のものが含まれていることを考慮すれば,よく健
闘していると判断される.
gcc の最適化オプションを指定した場合は,実行時
間の短縮がわずかであったのは,図 2 で示したオリジ
ナルの 4 つの掛け算の全て(あるいはいくつかが)が同
時に実行されたことが推測される.Broadwell は ALU
を複数持っているからである.最適化オプションを指
定しないときは,冗長な命令が多かったため,掛け算
の同時実行が行われなかったと推測される.
図 4 に示す手修正により得られたコードは,命令数
が減ったことが,実行時間のわずかな短縮に繋がって
いる.C 言語の変数によるキャリーの伝達のコストを
みることができた.
― 28 ―
(2015 年 9 月 24 日受理)
Fly UP