2013-09-24 3 views
8

数値的にルジャンドル多項式をいくつかの高いn次まで使用するコードを書きました。たとえば、ベクトルの力を効率的に利用する方法

.... 
case 8 
p = (6435*x.^8-12012*x.^6+6930*x.^4-1260*x.^2+35)/128; return 
case 9 
... 

ベクトルが長ければ、これは遅くなる可能性があります。 x.^4x.*x.*x.*xの間にパフォーマンスの違いがあることがわかりました。これを使用してコードを改善することができると考えました。私はtimeitを使用してのためには検出されませんでした:

x=linspace(0,10,1e6); 
f1= @() power(x,4) 
f2= @() x.4; 
f3= @() x.^2.^2 
f4= @() x.*x.*x.*x 

f4速く要因残りの部分よりも2です。しかし、x.^6に行くと、(x.*x.*x).^2x.*x.*x.*x.*x.*xの間にはほとんど違いはありません(他のすべてのオプションは遅いです)。

ベクターのパワーを取り出す最も効率的な方法は何でしょうか。 パフォーマンスに大きな違いがある理由を説明できますか?

答えて

8

これはまさにあなたの質問への答えではありませんが、それはあなたの問題を解決することがあります。

x2 = x.*x; % or x.^2 or power(x,2), whichever is most efficient 
p = ((((6435*x2-12012)*x2+6930)*x2-1260)*x2+35)/128 

あなただけ一度電源​​を行い、そして唯一の指数2でこのトリックは、すべてに適用することができ、この方法ルジャンドル多項式(奇数次多項式ではx2xに置き換えられます)。

1

はここにいくつかの考えです:

power(x,4)x.^4は(単にドキュメントを読んで)同じです。

x.*x.*x.*xはおそらくx.^2.^2


x.^2.^2のようなものに最適化されて、おそらくのように評価されます。各要素(速い)の二乗を取り、(速い再び)再びその広場を取ります。

x.^4はおそらく次のように直接評価されます:各要素の4乗をとります(遅い)。

2つの高速動作が1つの低速動作よりも時間がかからないことは奇妙なことではありません。最適化が電源4のケースでは実行されないのはあまりにも悪いことですが、おそらくそれが常に機能するとは限りませんし、コストもかかりません(入力チェック、メモリ?)。


タイミングについて:実際には2倍以上の違いがあります。あなたは今、関数内でそれらを呼び出すと、関数オーバーヘッドが相対的な差を小さくすること、それぞれの場合に追加され

y=x;tic,power(x,4);toc 
y=x;tic,x.^4;toc 
y=x;tic,x.^2.^2;toc 
y=x;tic,x.*x.*x.*x;toc 

が得られます:

Elapsed time is 0.034826 seconds. 
Elapsed time is 0.029186 seconds. 
Elapsed time is 0.003891 seconds. 
Elapsed time is 0.003840 seconds. 

をだから、それがほとんどです要因10の違い。ただし、秒単位の時間差はまだ小さいので、ほとんどの実用的なアプリケーションでは、単純な構文のために進んでいます。

+1

おそらくX 'で行われる最適化。* X * X * X 'の動作不思議なことに。私は 'x。*。x。*。。*。x"の数を2から8まで変化させて試しました。時間は多少直線的に増加しています。私はバンプを期待していただろう。たとえば、 "8"の場合(=> 'x。^ 2.^2^2':3つの電源操作)は、「7」よりも時間がかかります(=>より多くの電源操作) –

+2

@LuisMendoわからない確認する方法はありますが、1ステップ(ネストされた最適化なし)しかないと想像することができます。 7では、 'x。^ 2 * x。^ 2 * x。^ 2。* x'のように' x。^ 2 * x。^ 2 * x。^ 2より遅くならない。* x。^ 2'を8にすると、8を実行した方がこのように7を実行するよりも速い場合、Mathworksはおそらくこのような最適化を電源関数に含めることになりました。 –

+0

はい、それは説明かもしれません:入れ子なし –

1

Mathworksのパワー機能では、特殊な四角形が使用されているようです(残念ながら、見ることのできないすべてのクローズドソースです)。 R2013bのテストでは、.^power、およびrealpowが同じアルゴリズムを使用しているように見えます。正方形の場合、私は彼らがx.*xであることを特別扱いしていると信じています。

1.0x (4.4ms): @()x.^2 
1.0x (4.4ms): @()power(x,2) 
1.0x (4.5ms): @()x.*x 
1.0x (4.5ms): @()realpow(x,2) 
6.1x (27.1ms): @()exp(2*log(x)) 

キューブの場合、ストーリーが異なります。彼らはもはや特別なケースはありません。今回再び、.^power、およびrealpowすべてが似ていますが、はるかに遅い:だから

1.0x (8.1ms): @()x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x 
2.2x (17.4ms): @()x.^2.^2.^2.^2 
3.5x (27.9ms): @()exp(16*log(x)) 
7.9x (63.8ms): @()power(x,16) 
7.9x (63.9ms): @()realpow(x,16) 
8.3x (66.9ms): @()x.^16 

1.0x (4.5ms): @()x.*x.*x 
1.0x (4.6ms): @()x.*x.^2 
5.9x (26.9ms): @()exp(3*log(x)) 
13.8x (62.3ms): @()power(x,3) 
14.0x (63.2ms): @()x.^3 
14.1x (63.7ms): @()realpow(x,3) 

はどのようにこれらのアルゴリズムは、規模見るための16乗までジャンプレッツ.^powerおよびrealpowは、特別なケースでない限り、指数に関して一定の時間内にすべて実行されます(-1も特別なケースで表示されます)。 exp(n*log(x))のトリックを使用すると、指数に関する一定の時間も短縮されます。唯一の結果は、繰り返しの二乗がなぜ乗算よりも遅いのかを私は理解していません。

予想通り、xのサイズを100倍にすると、すべてのアルゴリズムで同様に時間が増加します。

だから、物語の道徳的な?スカラー整数指数を使用する場合、常に乗算を自分で行います。 powerと友人(指数は浮動小数点、ベクトルなど)にはたくさんのスマートがあります。唯一の例外は、Mathworksが最適化を行った場所です。 2013bでは、x^2x^(-1)と思われます。うまくいけば、時間が経つにつれて彼らはさらに追加されます。しかし一般に、べき乗は難しく、乗算は簡単です。パフォーマンスに影響を受けやすいコードでは、常にx.*x.*x.*xと入力して間違っているとは思われません。 (もちろん、あなたのケースでは、Luis`のアドバイスに従うと、各期間内の中間結果の利用を作る!)

function powerTest(x) 

f{1} = @() x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x.*x; 
f{2} = @() x.^2.^2.^2.^2; 
f{3} = @() exp(16.*log(x)); 
f{4} = @() x.^16; 
f{5} = @() power(x,16); 
f{6} = @() realpow(x,16); 

for i = 1:length(f) 
    t(i) = timeit(f{i}); 
end 

[t,idxs] = sort(t); 
fcns = f(idxs); 

for i = 1:length(fcns) 
    fprintf('%.1fx (%.1fms):\t%s\n',t(i)/t(1),t(i)*1e3,func2str(fcns{i})); 
end 
関連する問題