コードの他のバージョンを実行することで、アイデアを得ることができます。ループで関数を使用する代わりに、計算を明示的に書き込むことを検討してください
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
コンピュータで計算する時間:
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
今、完全に「ベクトル化された」ソリューションが明らかに最速ですが、すべてのxエントリに対して呼び出される関数の定義は、 大きなオーバーヘッドます。計算を明示的に書き込むだけで、ファクター5の速度が向上しました。これは、MATLABs JITコンパイラがインライン関数をサポートしていないことを示していると思います。そこでのgnoviceの回答によると、実際には匿名関数ではなく通常の関数を記述する方が適切です。それを試してみてください。
次のステップ-内部ループを削除(ベクトル化)します。
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
もう1つの要因5のスピードアップ:これらのステートメントには、MATLABでループを回避する必要があると言っているものがあります...それとも本当にありますか?これを見てください
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
「完全に」ベクトル化されたバージョンに非常に近い。Matlabは行列を列ごとに格納します。可能な場合は常に、「列方向」にベクトル化されるように計算を構造化する必要があります。
Soln3に戻ります。ループの順序は「行単位」です。変えましょう
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
良いが、それでも非常に悪い。シングルループ-良い。二重ループ-悪い。MATLABはループのパフォーマンスを改善するためにある程度の作業を行ったと思いますが、それでもループのオーバーヘッドはあります。内部でより重い作業があったとしても、気付かないでしょう。ただし、この計算はメモリ帯域幅に制限があるため、ループのオーバーヘッドが発生します。あなたもなり一層明らかに存在FUNC1の呼び出しのオーバーヘッドを参照してください。
では、arrayfunはどうなっているのでしょうか。そこにも機能がないので、オーバーヘッドがたくさんあります。しかし、なぜ二重ネストループよりもはるかに悪いのですか?実際、cellfun / arrayfunの使用に関するトピックは、何度も何度も議論されてきました(例:ここ、ここ、ここ、ここ、ここ)。これらの関数は単に遅いので、そのような細かい計算には使用できません。これらを使用して、セルと配列の間の簡潔なコード変換と洗練された変換を行うことができます。しかし、関数はあなたが書いたものより重い必要があります:
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
Soln7がセルになっていることに注意してください。コードのパフォーマンスは非常に良好になり、出力としてセルが必要な場合は、完全にベクトル化されたソリューションを使用した後でマトリックスを変換する必要はありません。
では、なぜarrayfunは単純なループ構造よりも遅いのですか?残念ながら、利用できるソースコードがないため、私たちが確実に言うことは不可能です。推測できるのは、arrayfunがあらゆる種類のさまざまなデータ構造と引数を処理する汎用関数であるため、ループの入れ子として直接表現できる単純なケースでは必ずしもそれほど高速ではないということです。オーバーヘッドはどこから来るのかわかりません。より良い実装によってオーバーヘッドを回避できますか?そうでないかもしれない。しかし残念ながら、私たちができる唯一のことは、パフォーマンスを調査して、うまく機能するケースとうまくいかないケースを特定することです。
更新このテストの実行時間は短いので、信頼できる結果を得るために、テストの周りにループを追加しました。
for i=1:1000
% compute
end
以下にいくつかの時間:
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
arrayfunは依然として悪いのですが、少なくともベクトル化されたソリューションよりも3桁ほど悪いことはありません。一方、列単位の計算を使用する単一のループは、完全にベクトル化されたバージョンと同じくらい高速です...これはすべて単一のCPUで実行されました。2コアに切り替えても、Soln5とSoln7の結果は変わりません-Soln5では、並列化するためにparforを使用する必要があります。高速化について忘れてください... arrayfunは並列実行されないため、Soln7は並列実行されません。一方、オリスのベクトル化バージョン:
Oli 5.508085 seconds.