Matlabでは、いつbsxfunを使用するのが最適ですか?


135

私の質問: SOに関するMatlabの質問に対する多くの良い回答がこの関数を頻繁に使用していることに気付きましたbsxfun。どうして?

動機:のMatlabドキュメントでbsxfunは、次の例が提供されています。

A = magic(5);
A = bsxfun(@minus, A, mean(A))

もちろん、次のコマンドを使用して同じ操作を実行できます。

A = A - (ones(size(A, 1), 1) * mean(A));

そして実際、簡単な速度テストは、2番目の方法が約20%速いことを示しています。では、なぜ最初の方法を使用するのでしょうか。使用bsxfunが「手動」アプローチよりもはるかに高速になる状況がいくつかあると思います。私はそのような状況の例とそれがなぜより速いのかについての説明を見ることに本当に興味があります。

また、この質問の最後の要素の1つである、Matlabのドキュメントのbsxfun「C = bsxfun(fun、A、B)」は、関数ハンドルfunで指定された要素ごとのバイナリ演算を配列AおよびBにシングルトンで適用します。拡張が有効です。」「シングルトン拡張が有効になっている」とはどういう意味ですか?


4
得られる読み取り速度は、実行するテストによって異なることに注意してください。Matlabを再起動して上記のコードを実行し、単純にtic...toc行を囲んだ場合、コードの速度は関数をメモリに読み込む必要があるかどうかに依存します。
ジョナス

@ジョナスはい、私timeitはあなた/ angainor / Danが提供するリンクの機能について読むことでこれについて学びました。
Colin T Bowers、

回答:


152

私が使用する理由は3つありますbsxfunドキュメントブログリンク)。

  1. bsxfunより速いrepmat(下記参照)
  2. bsxfun タイピングが少なくて済みます
  3. を使用することはbsxfun、を使用するのと同じようにaccumarray、Matlabの理解に満足します。

bsxfun入力配列を「単一の次元」、つまり配列のサイズが1である次元に沿って複製するため、他の配列の対応する次元のサイズと一致します。これがいわゆる「シングルトン探査」です。余談ですが、シングルトンディメンションは、を呼び出すとドロップされるディメンションですsqueeze

非常に小さな問題の場合、repmatアプローチはより速くなる可能性がありますが、その配列サイズでは、両方の操作が非常に速いため、全体的なパフォーマンスの点で違いはありません。bsxfunより速い2つの重要な理由があります。(1)計算はコンパイルされたコードで行われます。つまり、配列の実際の複製は決して行われません。(2)bsxfunマルチスレッドのMatlab関数の1つです。

かなり高速なラップトップでR2012b repmatとの速度比較を実行しましたbsxfun

ここに画像の説明を入力してください

私にとって、bsxfunは約3倍高速ですrepmat。配列が大きくなると、違いはより顕著になります

ここに画像の説明を入力してください

実行時のジャンプはrepmat、1Mbの配列サイズで発生します。これは、プロセッサキャッシュのサイズに関係している可能性がbsxfunあります。出力配列を割り当てるだけでよいため、ジャンプが悪くなることはありません。

以下に、タイミングに使用したコードを示します。

n = 300;
k=1; %# k=100 for the second graph
a = ones(10,1);
rr = zeros(n,1);
bb=zeros(n,1);
ntt=100;
tt=zeros(ntt,1);
for i=1:n;
   r = rand(1,i*k);
   for it=1:ntt;
      tic,
      x=bsxfun(@plus,a,r);
      tt(it)=toc;
   end;
   bb(i)=median(tt);
   for it=1:ntt;
      tic,
      y=repmat(a,1,i*k)+repmat(r,10,1);
      tt(it)=toc;
   end;
   rr(i)=median(tt);
end

優れた回答+1をありがとうございます。これが最も包括的なディスカッションであり、(現時点で)最も高い投票を受けたので、私はこれを回答にマークしました。
Colin T Bowers、

40

私の場合、bsxfun列または行の問題について考えるのを回避するために使用します。

あなたの例を書くために:

A = A - (ones(size(A, 1), 1) * mean(A));

私はいくつかの問題を解決しなければなりません:

1)size(A,1)またはsize(A,2)

2)ones(sizes(A,1),1)またはones(1,sizes(A,1))

3)ones(size(A, 1), 1) * mean(A)またはmean(A)*ones(size(A, 1), 1)

4)mean(A)またはmean(A,2)

私が使用するときbsxfun、私は最後のものを解決する必要があります:

a)mean(A)またはmean(A,2)

あなたは、それは怠惰か何かだと思うかもしれませんが、私は使用している場合bsxfun、私が持っているより少ないバグを、私はより速くプログラム

さらに、文字が短くなり、入力速度読みやすさが向上します


1
オリオリありがとうございます。+1は、この回答が、投資家とジョナスの反応に加えて、何かに貢献したと思います。私は、特定のコード行で解決する必要のある多くの概念的な問題のレイアウト方法が特に気に入りました。
Colin T Bowers、

16

非常に興味深い質問です。私は最近、この質問に答えながら、まさにそのような状況に遭遇しました。ベクトルを介してサイズ3のスライディングウィンドウのインデックスを計算する次のコードを考えますa

a = rand(1e7,1);

tic;
idx = bsxfun(@plus, [0:2]', 1:numel(a)-2);
toc

% equivalent code from im2col function in MATLAB
tic;
idx0 = repmat([0:2]', 1, numel(a)-2);
idx1 = repmat(1:numel(a)-2, 3, 1);
idx2 = idx0+idx1;
toc;

isequal(idx, idx2)

Elapsed time is 0.297987 seconds.
Elapsed time is 0.501047 seconds.

ans =

 1

この場合bsxfun、ほぼ2倍高速になります。行列とへのメモリの明示的な割り当てを回避idx0idx1、それらをメモリに保存してから、それらを追加するために再度読み取るだけので、便利で高速です。メモリ帯域幅は貴重な資産であり、多くの場合、今日のアーキテクチャのボトルネックであるため、それを賢く使用し、コードのメモリ要件を減らしてパフォーマンスを向上させる必要があります。

bsxfunベクトルを複製して得られた2つの行列を明示的に操作するのではなく、2つのベクトルの要素のすべてのペアに任意の演算子を適用して行列を作成します。それがシングルトン展開です。BLASのアウター製品と考えることもできます。

v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.

2つのベクトルを乗算して行列を取得します。外積は乗算のみを実行し、bsxfun任意の演算子を適用できるというだけです。bsxfun余談ですが、BLASアウター製品と同じくらい高速であることは非常に興味深いことです。また、BLASは通常パフォーマンスを提供すると考えられてます。

編集 Danのコメントのおかげで、Lorenがそれについて正確に議論しているすばらしい記事があります。


7
この記事では、関連するかもしれない:blogs.mathworks.com/loren/2008/08/04/...
ダン・

@ダン素晴らしいリファレンスをありがとう。
占領者2012年

素晴らしい回答者に感謝します。+1はbsxfun、良い例で主な利点を明確に述べた最初の人物です。
Colin T Bowers、

13

R2016b以降、Matlabはさまざまな演算子の暗黙的展開をサポートしているため、ほとんどの場合、以下を使用する必要はありませんbsxfun

以前は、この機能は関数を介して利用可能でしたbsxfun。のほとんどの使用をbsxfun暗黙の展開をサポートする関数および演算子の直接呼び出しに置き換えることをお勧めします。使用する場合と比較してbsxfun暗黙の拡張申し出速いスピードより良いメモリ使用量、およびコードの可読性改善

あります詳細な議論暗黙の拡大とローレンのブログ上でのパフォーマンスが。MathWorksからSteve Eddins を引用するに

R2016bでは、暗黙の拡張bsxfunはほとんどの場合と同じかそれより速く機能します。暗黙的な拡張でパフォーマンスが最も向上するのは、行列と配列のサイズが小さい場合です。行列サイズが大きい場合、暗黙の展開はとほぼ同じ速度になる傾向がありますbsxfun


8

:物事は常に3つの一般的な方法と一致していないrepmat、インデックスするものによってexpension、とbsxfun。ベクトルサイズをさらに大きくすると、より興味深いものになります。プロットを参照してください:

比較

bsxfun実際にはある時点で他の2つよりも少し遅くなりますが、驚いたのは、ベクトルサイズをさらに大きくすると(> 13E6出力要素)、bsxfunが突然、約3倍速くなったということです。それらの速度は段階的にジャンプするようであり、順序は常に一貫しているわけではありません。私の推測では、プロセッサやメモリのサイズにも依存している可能性がありますが、一般的には、bsxfun可能な限り、私は固執すると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.