Fortran で、多数の行列を異なる重みで乗算し、それらを合計して単一の行列を形成する関数を作成しようとしています。このプロセスが私のプログラムのボトルネックであることを確認しました (この重み付けは、プログラムの 1 回の実行に対して、さまざまな重み付けで何度も行われます)。現在、Matlab から Fortran に切り替えて、より高速に実行しようとしています。私は Fortran の初心者なので、すべての助けに感謝します。
Matlab では、このような計算を行うために私が見つけた最速の方法は次のようになります。
function B = weight_matrices()
n = 46;
m = 1800;
A = rand(n,m,m);
w = rand(n,1);
tic;
B = squeeze(sum(bsxfun(@times,w,A),1));
toc;
が割り当てられている行B
は、私のマシン (Matlab R2012b、MacBook Pro 13" retina、2.5 GHz Intel Core i5、8 GB 1600 MHz DDR3) で約 0.9 秒で実行されます。私の問題では、テンソルA
は(初期化後) プログラムの実行全体で同じ (定数) ですが, w は任意の値を取ることができます. また,ここではn
との典型的な値m
が使用されています. つまり, テンソルA
はメモリ内で約 1 GB のサイズを持つことになります.
これを Fortran で書く最も明確な方法は、次のようなものです。
pure function weight_matrices(w,A) result(B)
implicit none
integer, parameter :: n = 46
integer, parameter :: m = 1800
double precision, dimension(num_sizes), intent(in) :: w
double precision, dimension(num_sizes,msize,msize), intent(in) :: A
double precision, dimension(msize,msize) :: B
integer :: i
B = 0
do i = 1,n
B = B + w(i)*A(i,:,:)
end do
end function weight_matrices
この関数は、-O3 を使用して gfortran 4.7.2 でコンパイルすると、約 1.4 秒で実行されます (「call cpu_time(t)」で時間指定された関数呼び出し)。ループを手動でアンラップすると
B = w(1)*A(1,:,:)+w(2)*A(2,:,:)+ ... + w(46)*A(46,:,:)
代わりに、関数の実行に約 0.11 秒かかります。これは素晴らしいことで、Matlab バージョンと比較して約 8 倍のスピードアップが得られることを意味します。ただし、読みやすさとパフォーマンスについてはまだ疑問があります。
まず、この重み付けと行列の合計を実行するさらに高速な方法があるかどうか疑問に思います。BLAS と LAPACK を調べましたが、適合すると思われる関数が見つかりません。また、行列を列挙する次元を最後の次元として配置しようとしましたA
(つまり、要素に対して から(i,j,k)
に切り替え(k,i,j)
ました) が、これによりコードが遅くなりました。
第 2 に、この高速バージョンはあまり柔軟ではなく、実際には非常に見栄えが悪くなります。これは、このような単純な計算に対してテキストが多すぎるためです。私が実行しているテストでは、さまざまな数の重みを使用して、w の長さが変化し、アルゴリズムの残りの部分にどのように影響するかを確認したいと思います。B
とはいえ、毎回の課題の書き直しって結構面倒なんですよね。パフォーマンスを同じ (またはそれ以上) に保ちながら、これをより柔軟にする方法はありますか?
第三に、テンソルA
は、前述のように、プログラムの実行中は一定です。独自のモジュールで「パラメーター」属性を使用してプログラムに定数スカラー値を設定し、それらを必要とする関数/サブルーチンに「use」式でインポートしました。テンソルに対して同等のことを行う最良の方法は何A
ですか? 対応する最適化を実行できるように、このテンソルは初期化後に定数になることをコンパイラに伝えたいと思います。A
通常、サイズは約 1 GB であるため、ソース ファイルに直接入力するのは現実的ではないことに注意してください。
ご意見をお寄せいただきありがとうございます。:)