1

私のプログラムでは、合計を計算する必要があります。

和.

Cと の新しい値を使用して、この合計を 2500 回計算しますz

引数zはベクトルの場合があります。次のように、単純な for ループとベクトル化されたバージョン コードを記述しました。

K = 200;
n_z = 40000;
C = ones(K,1); % an example, in real life the arey some coefficients (at next call will be new)
k = 0:K-1;
z = linspace(0, 2*pi, n_z); % at next call will be new

tic;
    my_sum_for = zeros(1, K);
    for i=1:n_z
       my_sum_for(i) = C' * tan(k' * z(i));
    end
toc; % Elapsed time is 1.820485 seconds.

tic;
     my_sum = C' * tan(k' * z);
toc; % Elapsed time is 0.160924 seconds.

ベクトル化されたバージョンは高速ですが、十分ではありません。ベクトル化されたバージョンを改善することは可能ですか?

Dominique Jacquel の答えの後、このベクトル化されたバージョンがあり、高速です。

    K = 200;
    n_z = 40000;
    C = ones(K,1)';  % an example, in real life they are some coefficients (at next call will be new)
    k = (0:K-1)';
    z = linspace(0, 2*pi, n_z);  % at next call will be new

    tic;
        my_sum_for = zeros(1, K);
        for i=1:n_z
           my_sum_for(i) = C * tan(k * z(i));
        end
    toc; % Elapsed time is 1.521587 seconds.

    tic;
         my_sum = C * tan(k * z);
    toc; % Elapsed time is 0.125468 seconds.

ベクトル化されたバージョン (bsxfun、arrayfun など) をさらに改善することは可能ですか? 250 秒という時間は、私にとってはまだ遅いです (すべての計算の 75% です)。

4

3 に答える 3

6

ここでは、ハードウェアの制限にかなり近いと思います。Matlabでの行列の乗算は、パフォーマンスに関しては打ち負かすのが難しいことが証明されているBLASライブラリを使用して行われます。

AFAIK、タンジェント関数には、その値を計算するための実際の専用ハードウェアがあります。また、Matlabは、大きな行列の三角関数を複数のコアに自動的に分散するため、基本的に改善する点はほとんどありません。

また、間違っている場合は訂正してください。ただし、データオーバーヘッドとメモリの問題が発生する可能性があることを考えると、GPUではこの計算が実際には遅くなると思います。

これらを比較すると、次のようになります。

tic;
for ii = 1:10
    my_sum = C * tan(k * z);
end
toc 

tic;
for ii = 1:10
    my_sum_notan = C * k * z;
end
toc

すべての痛みはタンジェント関数に起因することがわかるので、これに焦点を当てるのが最善でした。ここで読むことができるように、トリガー機能の高速化は、基本的に、ある程度の精度を犠牲にした場合にのみ可能です。

全体として、次の質問を自問する必要があります。

  1. 完全な倍精度をあきらめても構わないと思いますか、それとも、たとえば6桁は「十分に近い」ですか?

  2. 後で接線が計算されるように、問題を再定式化できませんか?または前に?またはとにかく、非常に少量の要素で?前述の問題では、これは明らかに不可能ですが、完全なコードはわかりません。問題に適用できるいくつかの優れたtrig-identityがある可能性があります。

  3. 上記のすべてを考えると、これをさらに最適化するために必要な労力の量は、より長い実行時間を本当に上回りますか?250秒は、機能が低下しているが高速なtrig関数を実装するカスタムのほとんど移植性のないMEX関数を作成する場合と比べてそれほど悪くはありません。

于 2012-09-06T20:13:57.300 に答える
3

これは、私のコンピューターでわずかに高速に動作するバージョンです。

k = repmat((0:K-1)', 1, n_z);
z = repmat(linspace(0, 2*pi, n_z), K, 1);
C = ones(1, K);
tic
my_sum = C*tan(k.*z);
toc

基本的に、k と z の外積の代わりに、行列を直接操作します。

最初のバージョン

Elapsed time is 0.652923 seconds.
Elapsed time is 0.240300 seconds.

Dominique Jacquelの回答後

Elapsed time is 0.376218 seconds.
Elapsed time is 0.214047 seconds.

私のバージョン

Elapsed time is 0.168535 seconds.

repmats のコストを追加する必要があるかもしれませんが、それは一度だけ行うことができます。残りのコードはわかりません。

私はロディ・オルデンハウスに完全に同意します。作業の大部分は正接関数にあります。もっと言えます。k.*z の計算は非常に効率的で、あまり改善することはできません。メモリ帯域幅を計算すると、私のコンピューターでは約 10GB/s になります。私が得ることができるピークは約16GB /秒なので、それに近い. そこには多くの可能性はありません。C*Tも同様。これは単純な BLAS2 行列とベクトルの乗算であり、メモリ制限があります。システム サイズについては、MATLAB オーバーヘッドが大きすぎないことを示しています。

編集:Rodyが述べたように、MATLABの新しいバージョンはすでにtan()を並列化しています。ここでもそれほど多くはありません。

tan() を改善することだけを期待できます - おそらくそれを並行して実行することによって。結局のところ、これは簡単に並列化できるタスクです... これだけを MEX ファイルにエクスポートすることを検討してください。これは OpenMP を使用します。非常に単純な作業であり、予備のコアがいくつかあれば大幅に高速化されます。

于 2012-09-06T22:05:10.483 に答える
3

ループ内の時間を節約するために、できる限り多くの行列演算を事前に実行します (この場合は転置)。

K = 200;
n_z = 40000;
C = ones(K,1)';
k = (0:K-1)';
z = linspace(0, 2*pi, n_z);

tic;
    my_sum_for = zeros(1, K);
    for i=1:n_z
       my_sum_for(i) = C * tan(k * z(i));
    end
toc

tic;
     my_sum = C * tan(k * z);
toc;

以前の私の実行時間

Elapsed time is 1.266158 seconds.
Elapsed time is 0.531173 seconds.

以降

Elapsed time is 0.496803 seconds.
Elapsed time is 0.185396 seconds.
于 2012-09-06T11:22:58.520 に答える