ノルム計算

1.実験環境

マシン名 : serra
CPU : Pentium-M
コンパイラ : gcc
コンパイラオプション : -O2

2.実験結果

 gnuplotでグラフを作成した。
 このマシンのキャッシュサイズ等の理論値は調べてもわからなかったので(cpuinfo、dmesg等)、
 実験の結果から推測される値を以降の実験で用いることにする。

 2.1.ベクトル長nと性能の関係

ベクトル長nと性能の関係
プログラム:norm.c  データ:cache.dat
このグラフを見ると、ベクトル長が長くなるにつれて性能が上がっていることがわかる。
約32Kでピークになった後に一度性能が低下し、1M付近でまた低下している。
性能が低下したこの2つの場所を拡大して詳しく見てみることにする。
一次キャッシュ
プログラム:norm2.c  データ:cache-1.dat
32Kを過ぎると一気に80Mflops程度性能が低下した。これは一次キャッシュのあふれによる
ものだと考えられる。そのあとは多少上下していたが大きな変化はなかった。
二次キャッシュ
プログラム:norm2.c  データ:cache-2.dat
1Mを過ぎると再び性能が低下し始め、7M程度まで徐々に低下し続ける。
これは2次キャッシュのあふれによるものだと思われる。
 2.2.ストライドと性能の関係
上の実験より1次キャッシュは32K、2次キャッシュは1Mであると予測できる。
そのそれぞれについてストライドを変更して性能との関係を見る。
一次キャッシュのラインサイズ
プログラム:norm-stride.c  データ:stride-cache1.dat
n=128K/mとして、配列全体が2次キャッシュに入るようにした。
ストライドが8までは性能が低下し、以降安定したが、12からまた上昇し始めた。(原因不明)
ストライドが8の時に安定したので1次キャッシュのラインは8ワード=64バイトと予測できる。
二次キャッシュのラインサイズ
プログラム:norm-stride.c  データ:stride-cache2.dat
n=16M/mとして、配列全体が2次キャッシュに入るようにした。
ストライドが8までは性能が低下し、以降安定した。
ストライドが8の時に安定したので2次キャッシュのラインは8ワード=64バイトと予測できる。
キャッシュのラインサイズ
プログラム:norm-stride.c  データ:stride-cache1.datstride-cache2.dat
二つのグラフをひとつにまとめてみると、一次キャッシュのグラフのm=8の値と
二次キャッシュのグラフのm=1の値がほぼ等しいことがわかる。
 2.3.ラインアドレス

ラインアドレス
プログラム:norm-line.c  データ:stride-line.dat
データ数n=64(<1次キャッシュのライン数=32K/64=512)で固定し、
ストライドを広げてみると、所々で性能が低下していることがわかる。
ラインアドレス(ストライドが8の倍数)
プログラム:norm-line.c  データ:stride-line8.dat
m=8の倍数の時、128、256の時に大きく性能を落としていることがわかる。これは
(ストライド:m=128)×(データ数:n=64)×(double型:8byte)=64K(=一次キャッシュの倍)より、
ちょうどラインを毎回読み直さないといけなくなり、そのために性能を落としているのだと考えられる。
このことを確かめるため、mが64の倍数の場合を調べる。
ラインアドレス(ストライドが64の倍数)
プログラム:norm-line.c  データ:stride-line64.dat
m=64の倍数の時、128の倍数の時に性能を落としていることがわかる。
さらに、1024の時にやや性能を落とし、2048の時に大きく性能を落としていることがわかる。
詳しく調べるため、mが512の倍数の時を見てみる。
ラインアドレス(ストライドが512の倍数)
プログラム:norm-line.c  データ:stride-line512.dat
m=512の倍数の時、2048の倍数の時に大きく性能を落としていることがわかる。
mが2048の倍数の時に性能を落としているのは、読み直しが全て同じラインに当たっているためだと思われる。
なぜ1次キャッシュサイズ=4096(=32Kbyte)ではないのかは、アソシアティビティによると思われる。
(アソシアティビティについては2.5で述べる)
さらに、最初のグラフを見てみると、mが128Kの時に大きく性能を落としていることが読み取れる。
これは二次キャッシュのライン衝突によるものだと思われる。
 2.4.TLB(Translation Look-aside Buffer)とページ

TLBのサイズを測定するため、ストライドの数を固定し、データ数を変化させる。
TLB(データ数が32)
プログラム:norm-tlb.c  データ:tlb32.dat
データ数n=32のとき、m=256で一次キャッシュ衝突。
m=4096で急に性能が低下し、キャッシュサイズの倍数を外しても性能が低いまま。
TLB(データ数が64)
プログラム:norm-line.c  データ:stride-line.dat
データ数n=64のとき、m=128で一次キャッシュ衝突。
m=2048で急に性能が低下し、キャッシュサイズの倍数を外しても性能が低いまま。
TLB(データ数が128)
プログラム:norm-tlb.c  データ:tlb128.dat
データ数n=128のとき、m=64で一次キャッシュ衝突。
m=1024で急に性能が低下し、キャッシュサイズの倍数を外しても性能が低いまま。
TLB(データ数が256)
プログラム:norm-tlb.c  データ:tlb256.dat
データ数n=256のとき、m=32で一次キャッシュ衝突。
m=512で急に性能が低下し、キャッシュサイズの倍数を外しても性能が低いまま。
以上のことより、(ストライド:m)×(データ数:n)=131072(=1Mbyte)のときに
性能を落としていることがわかる。よって1MBでTLBを使い果たすと予測される。
TLB(データ数が512)
プログラム:norm-tlb.c  データ:tlb512.dat
データ数n=512のとき、m=16で一次キャッシュ衝突。m=256で性能が低下し、さらに512で
性能が低下している。これはm=256のときはTLBあふれが2回で1ページ、512のときは毎回
新しいページが必要になるためだとすると、ページサイズは512(=4Kbyte)だと考えられる。
するとエントリ数は256ということになるが、この数は多すぎる気がする。
TLB(データ数が1024)
プログラム:norm-tlb.c  データ:tlb1024.dat
データ数n=1024のとき、m=8で一次キャッシュ衝突。m=128、256、512で性能が低下している。
これはm=256のときはTLBあふれが4回で1ページ、256のときはTLBあふれが2回で1ページ、
512のときは毎回新しいページが必要になるためだと考えられる。
 2.5.アソシアティビティ

m:ストライドをキャッシュサイズ(=4096)にし、n:データ数を1024とし、l飛びに次々とアクセスする。
もしl飛びのデータが異なるラインにあたるときはひとつのラインにJ個のデータが載ることになり、
l飛びのデータが同じラインに当たるときはひとつのラインに2J個のデータが載ることになる。
アソシアティビティ(Jが1)
プログラム:norm-cmp.c  データ:cmp1.dat
J=1のとき、性能は落ちないと思ったが、l=4096のときに大きく性能を落とした。
2way set associativeではない?
アソシアティビティ(Jが2)
プログラム:norm-cmp.c  データ:cmp2.dat
J=2のとき、l=2048のときに性能を落とし、l=4096のときに性能を上げている。
アソシアティビティ(Jが3)
プログラム:norm-cmp.c  データ:cmp3.dat
J=3のときもl=2048のときに性能を落とし、l=4096のときに性能を上げている。
アソシアティビティ(3つのグラフをまとめたもの)
プログラム:norm-cmp.c  データ:cmp1.datcmp2.datcmp3.dat
3つのグラフをまとめてみると、l=4096のときに何か起こっているようである。
この値は一次キャッシュの大きさであるのでそのことに関係しているのかもしれない。
2way set associativeでないと、2.3のラインアドレスの際の説明ができないので、
この実験はよくわからない結果になってしまった。
 2.6.パイプライン

データ数:n=1024とし、アンロールを行い性能の変化を調べる。
ループアンロール
プログラム:norm-pipe.c  データ:unroll-1.dat
ループアンローリングを3回したときが一番性能が上がった。
結合変換
プログラム:norm-pipe.c  データ:unroll-2.dat
結合変換を5回したときが一番性能が上がった。
比較
プログラム:norm-pipe.c  データ:unroll-1.datunroll-2.dat
比較すると、ループアンローリングを3回行ったとき以外は結合変換のほうが性能が上がった。
アンロールが3回のときは結合変換をしないほうがよい結果になった。

戻る