No Matlab, quando é ideal usar o bsxfun?

135

Minha pergunta: notei que muitas respostas boas para as questões do Matlab no SO usam frequentemente a função bsxfun. Por quê?

Motivação: Na documentação do Matlab para bsxfun, é fornecido o seguinte exemplo:

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

Claro que poderíamos fazer a mesma operação usando:

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

De fato, um simples teste de velocidade demonstra que o segundo método é cerca de 20% mais rápido. Então, por que usar o primeiro método? Suponho que há algumas circunstâncias em que o uso bsxfunserá muito mais rápido que a abordagem "manual". Eu estaria realmente interessado em ver um exemplo dessa situação e uma explicação de por que é mais rápido.

Além disso, um elemento final para essa pergunta, novamente da documentação do Matlab para bsxfun: "C = bsxfun (fun, A, B) aplica a operação binária elemento a elemento especificada pela função handle fun nas matrizes A e B, com singleton expansão ativada. " O que significa a frase "com expansão singleton ativada"?

Colin T Bowers
fonte
4
Observe que a velocidade de leitura obtida depende do teste que você executa. Se você executar o código acima após reiniciar o Matlab e simplesmente tic...toccontornar as linhas, a velocidade do código dependerá da necessidade de ler as funções na memória.
Jonas
@ Jonas Sim, eu acabei de aprender sobre isso lendo a timeitfunção no link que você / angainor / Dan fornece.
Colin T Bowers

Respostas:

152

Existem três razões pelas quais eu uso bsxfun( documentação , link do blog )

  1. bsxfuné mais rápido que repmat(veja abaixo)
  2. bsxfun requer menos digitação
  3. Usar bsxfun, como usar accumarray, me faz sentir bem com minha compreensão do Matlab.

bsxfunreplicará as matrizes de entrada ao longo de suas "dimensões singleton", ou seja, as dimensões ao longo das quais o tamanho da matriz é 1, para que correspondam ao tamanho da dimensão correspondente da outra matriz. Isto é o que se chama "expasão singleton". Como um aparte, as dimensões singleton são as que serão eliminadas se você ligar squeeze.

É possível que, para problemas muito pequenos, a repmatabordagem seja mais rápida - mas nesse tamanho de matriz, as duas operações são tão rápidas que provavelmente não farão diferença em termos de desempenho geral. Há duas razões importantes para que bsxfunseja mais rápido: (1) o cálculo ocorre no código compilado, o que significa que a replicação real da matriz nunca acontece e (2) bsxfuné uma das funções do Matlab multithread.

Fiz uma comparação de velocidade entre repmate bsxfuncom o R2012b no meu laptop decentemente rápido.

insira a descrição da imagem aqui

Para mim, bsxfuné cerca de 3 vezes mais rápido que repmat. A diferença se torna mais acentuada se as matrizes ficarem maiores

insira a descrição da imagem aqui

O salto no tempo de execução repmatocorre em torno de um tamanho de matriz de 1Mb, que pode ter algo a ver com o tamanho do cache do meu processador - bsxfunnão fica tão ruim quanto um salto, porque ele só precisa alocar a matriz de saída.

Abaixo, você encontra o código que usei para cronometrar:

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
Jonas
fonte
Obrigado por uma excelente resposta +1. Marquei esta a resposta, pois é a discussão mais abrangente e também (neste momento) recebeu mais votos positivos.
Colin T Bowers
40

No meu caso, uso bsxfunporque me evita pensar nos problemas de coluna ou linha.

Para escrever seu exemplo:

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

Eu tenho que resolver vários problemas:

1) size(A,1)ousize(A,2)

2) ones(sizes(A,1),1)ouones(1,sizes(A,1))

3) ones(size(A, 1), 1) * mean(A)oumean(A)*ones(size(A, 1), 1)

4) mean(A)oumean(A,2)

Quando uso bsxfun, só tenho que resolver o último:

a) mean(A)oumean(A,2)

Você pode pensar que é preguiçoso ou algo assim, mas quando uso bsxfun, tenho menos bugs e programa mais rapidamente .

Além disso, é mais curto, o que melhora a velocidade e a legibilidade da digitação .

Oli
fonte
1
Obrigado pela resposta Oli. +1, pois acho que essa resposta contribuiu com algo além das respostas de angainor e Jonas. Gostei particularmente da maneira como você expôs o número de problemas conceituais que precisam ser resolvidos em uma determinada linha de código.
Colin T Bowers
16

Pergunta muito interessante! Recentemente, deparei-me exatamente com essa situação ao responder a essa pergunta. Considere o seguinte código que calcula índices de uma janela deslizante de tamanho 3 por meio de um vetor 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

Nesse caso, bsxfuné quase duas vezes mais rápido! É útil e rápido, pois evita a alocação explícita de memória para matrizes idx0e idx1, salvando-as na memória e, em seguida, lendo-as novamente apenas para adicioná-las. Como a largura de banda da memória é um ativo valioso e, muitas vezes, o gargalo nas arquiteturas atuais, você deseja usá-la com sabedoria e diminuir os requisitos de memória do seu código para melhorar o desempenho.

bsxfunpermite fazer exatamente isso: crie uma matriz com base na aplicação de um operador arbitrário a todos os pares de elementos de dois vetores, em vez de operar explicitamente em duas matrizes obtidas pela replicação dos vetores. Essa é a expansão singleton . Você também pode pensar nisso como o produto externo da BLAS:

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

Você multiplica dois vetores para obter uma matriz. Só que o produto externo executa apenas a multiplicação e bsxfunpode aplicar operadores arbitrários. Como uma observação lateral, é muito interessante ver que bsxfuné tão rápido quanto o produto externo BLAS. E o BLAS é geralmente considerado como o melhor desempenho.

Editar Graças ao comentário de Dan, aqui está um ótimo artigo de Loren discutindo exatamente isso.

angainor
fonte
7
Este artigo pode ser relevante: blogs.mathworks.com/loren/2008/08/04/…
Dan
@ Dan Obrigado por uma ótima referência.
angainor
Obrigado por uma ótima resposta. +1 por ser o primeiro a indicar claramente a principal vantagem de bsxfunum bom exemplo.
Colin T Bowers
13

A partir do R2016b, o Matlab suporta expansão implícita para uma ampla variedade de operadores, portanto, na maioria dos casos, não é mais necessário usar bsxfun:

Anteriormente, essa funcionalidade estava disponível através da bsxfunfunção Agora é recomendado que você substitua a maioria dos usos bsxfunpor chamadas diretas para as funções e operadores que suportam expansão implícita . Comparada ao uso bsxfun, a expansão implícita oferece velocidade mais rápida , melhor uso da memória e melhor legibilidade do código .

Há uma discussão detalhada de Expansão implícita e seu desempenho no blog de Loren. Para citar Steve Eddins do MathWorks:

No R2016b, a expansão implícita funciona mais rápido ou mais rápido do que bsxfunna maioria dos casos. Os melhores ganhos de desempenho para expansão implícita são com pequenos tamanhos de matriz e matriz. Para tamanhos de matriz grandes, a expansão implícita tende a ser aproximadamente a mesma velocidade que bsxfun.

nirvana-msu
fonte
8

As coisas nem sempre são consistentes com os três métodos comuns: repmatdespesa pela indexação e bsxfun. Fica bem mais interessante quando você aumenta ainda mais o tamanho do vetor. Veja o enredo:

comparação

bsxfunna verdade se torna um pouco mais lento que os outros dois em algum momento, mas o que me surpreendeu é que se você aumentar ainda mais o tamanho do vetor (> 13E6 elementos de saída), o bsxfun de repente se torna mais rápido novamente em cerca de 3x. Suas velocidades parecem saltar em passos e a ordem nem sempre é consistente. Meu palpite é que também pode ser dependente do tamanho do processador / memória, mas geralmente acho que continuaria bsxfunsempre que possível.

Justin Wong
fonte