No Matlab, quando é ideal usar o bsxfun?

Minha pergunta: Tenho notado que muitas das boas respostas às perguntas do Matlab sobre SO freqüentemente usam a function bsxfun . Por quê?

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

 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)); 

E, de fato, um teste de velocidade simples demonstra que o segundo método é cerca de 20% mais rápido. Então, por que usar o primeiro método? Eu estou supondo que há algumas circunstâncias em que usar o bsxfun será muito mais rápido que a abordagem “manual”. Eu estaria realmente interessado em ver um exemplo de tal situação e uma explicação do porquê é mais rápido.

Além disso, um elemento final para esta questão, novamente a partir da documentação do Matlab para o bsxfun : “C = bsxfun (fun, A, B) aplica a operação binária de elemento-a-elemento especificada pela function handle handle aos arrays A e B, com expansão singleton habilitada “. O que significa a frase “com expansão de singleton habilitado”?

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

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

bsxfun replicará as matrizes de input ao longo de suas “dimensões singleton”, ou seja, as dimensões ao longo das quais o tamanho da matriz é 1, de modo que elas correspondam ao tamanho da dimensão correspondente da outra matriz. Isso é o que é chamado de “explosão de singleton”. Como um aparte, as dimensões singleton são as que serão descartadas se você chamar squeeze .

É possível que, para problemas muito pequenos, a abordagem repmat seja mais rápida – mas nesse tamanho de array, ambas as operações são tão rápidas que provavelmente não farão qualquer diferença em termos de desempenho geral. Existem duas razões importantes pelas quais o bsxfun é mais rápido: (1) o cálculo acontece no código compilado, o que significa que a replicação atual do array nunca acontece, e (2) o bsxfun é uma das funções do Matlab multithread.

Eu corri uma comparação de velocidade entre repmat e bsxfun com R2012b no meu laptop decentemente rápido.

insira a descrição da imagem aqui

Para mim, o bsxfun é cerca de 3 vezes mais rápido que o repmat . A diferença se torna mais pronunciada se os arrays ficarem maiores

insira a descrição da imagem aqui

O salto no tempo de execução do repmat acontece em torno de um tamanho de matriz de 1Mb, que poderia ter algo a ver com o tamanho do cache do meu processador – o bsxfun não fica tão ruim, porque precisa apenas alocar o array de saída.

Abaixo você encontra o código que eu usei para o tempo:

 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 

No meu caso, eu uso o bsxfun porque evita que eu pense sobre os 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) ou size(A,2)

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

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

4) mean(A) ou mean(A,2)

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

a) mean(A) ou mean(A,2)

Você pode pensar que é preguiçoso ou algo assim, mas quando eu uso o bsxfun , eu tenho menos bugs e bsxfun mais rápido .

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

Pergunta muito interessante! Eu recentemente deparei exatamente com tal situação enquanto respondia a essa pergunta. Considere o seguinte código que calcula os í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 

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

bsxfun permite que você faça exatamente isso: crie uma matriz baseada 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. Isso é expansão singleton . Você também pode pensar nisso como o produto externo do 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. Apenas que o produto externo somente executa multiplicação, e o bsxfun pode aplicar operadores arbitrários. Como uma nota lateral, é muito interessante ver que o bsxfun é tão rápido quanto o produto externo BLAS. E BLAS é geralmente considerado para entregar o desempenho ..

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

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 o bsxfun :

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

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

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

As coisas nem sempre são consistentes com os 3 methods comuns: repmat , expension por indexação e bsxfun . Fica mais interessante quando você aumenta o tamanho do vetor ainda mais. Veja o enredo:

comparação

bsxfun na verdade se torna um pouco mais lento que os outros dois em algum momento, mas o que me surpreendeu é que se você aumentar o tamanho do vetor ainda mais (> 13E6 elementos de saída), o bsxfun repentinamente se torna mais rápido novamente em cerca de 3x. Suas velocidades parecem pular em etapas e a ordem nem sempre é consistente. Meu palpite é que ele pode ser dependente do tamanho do processador / memory também, mas geralmente eu acho que ficaria com o bsxfun sempre que possível.

    Intereting Posts