行列乗算のパフォーマンスを比較するためだけに、Java で 2 つの行列クラスを作成しました。1 つのクラス (Mat1)には、行列のdouble[][] A
行が であるメンバーが格納されます。もう一方のクラス (Mat2) には、 が格納され、は の転置です。i
A[i]
A
T
T
A
正方行列 M があり、 の積が欲しいとしましょうM.mult(M)
。製品を呼び出しますP
。
M が Mat1 インスタンスの場合、使用されるアルゴリズムは単純なものでした。
P[i][j] += M.A[i][k] * M.A[k][j]
for k in range(0, M.A.length)
M が私が使用した Mat2 の場合:
P[i][j] += M.A[i][k] * M.T[j][k]
これは同じアルゴリズムですT[j][k]==A[k][j]
。1000x1000 行列では、2 番目のアルゴリズムは私のマシンで約 1.2 秒かかりますが、最初のアルゴリズムは少なくとも 25 秒かかります。2番目の方が速いと思っていましたが、それほどではありません。問題は、なぜこれほど高速なのかということです。
私の唯一の推測は、データが 1 ワードよりも大きなチャンクでキャッシュに取り込まれるため、2 番目のアルゴリズムが CPU キャッシュをより有効に利用できるということです。すぐ下の行に移動してキャッシュします(配列は行の優先順で格納されるため、メモリ内には約 1000 ワードです)。キャッシュされるデータはありません。
誰かに尋ねたところ、メモリ アクセス パターンがより使いやすいためだと彼は考えていました (つまり、2 番目のバージョンでは TLB のソフト フォールトが少なくなるからです)。私はこれについてまったく考えていませんでしたが、TLB フォールトがどのように減少するかを見ることができます。
それで、それはどれですか?または、パフォーマンスの違いには他の理由がありますか?