En Matlab, ¿cuándo es óptimo usar bsxfun?

135

Mi pregunta: Me he dado cuenta de que muchas buenas respuestas a las preguntas de Matlab sobre SO utilizan con frecuencia la función bsxfun. ¿Por qué?

Motivación: en la documentación de Matlab para bsxfun, se proporciona el siguiente ejemplo:

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

Por supuesto, podríamos hacer la misma operación usando:

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

Y, de hecho, una simple prueba de velocidad demuestra que el segundo método es aproximadamente un 20% más rápido. Entonces, ¿por qué usar el primer método? Supongo que hay algunas circunstancias en las que el uso bsxfunserá mucho más rápido que el enfoque "manual". Me interesaría mucho ver un ejemplo de tal situación y una explicación de por qué es más rápido.

Además, un elemento final para esta pregunta, nuevamente de la documentación de Matlab para bsxfun: "C = bsxfun (fun, A, B) aplica la operación binaria elemento por elemento especificada por la función de diversión de manejo a las matrices A y B, con singleton expansión habilitada ". ¿Qué significa la frase "con la expansión singleton habilitada"?

Colin T Bowers
fuente
44
Tenga en cuenta que la lectura de velocidad que obtiene depende de la prueba que realice. Si ejecuta el código anterior después de reiniciar Matlab y simplemente coloca tic...toclas líneas, la velocidad del código dependerá de tener que leer las funciones en la memoria.
Jonas
@ Jonás Sí, acabo de enterarme de esto leyendo sobre la timeitfunción en el enlace que usted / angainor / Dan proporciona.
Colin T Bowers

Respuestas:

152

Hay tres razones por las que uso bsxfun( documentación , enlace del blog )

  1. bsxfunes más rápido que repmat(ver abajo)
  2. bsxfun requiere menos escribir
  3. Usar bsxfun, como usar accumarray, me hace sentir bien con mi comprensión de Matlab.

bsxfunreplicará las matrices de entrada a lo largo de sus "dimensiones singleton", es decir, las dimensiones a lo largo de las cuales el tamaño de la matriz es 1, de modo que coincidan con el tamaño de la dimensión correspondiente de la otra matriz. Esto es lo que se llama "expansión singleton". Por otro lado, las dimensiones de singleton son las que se eliminarán si llama squeeze.

Es posible que para problemas muy pequeños, el repmatenfoque sea más rápido, pero con ese tamaño de matriz, ambas operaciones son tan rápidas que probablemente no harán ninguna diferencia en términos de rendimiento general. Hay dos razones importantes para que bsxfunsea ​​más rápido: (1) el cálculo se realiza en código compilado, lo que significa que la replicación real de la matriz nunca ocurre, y (2) bsxfunes una de las funciones de Matlab multiproceso.

He realizado una comparación de velocidad entre repmaty bsxfuncon R2012b en mi portátil bastante rápido.

ingrese la descripción de la imagen aquí

Para mí, bsxfunes aproximadamente 3 veces más rápido que repmat. La diferencia se hace más pronunciada si las matrices se hacen más grandes

ingrese la descripción de la imagen aquí

El salto en tiempo de ejecución repmatocurre alrededor de un tamaño de matriz de 1Mb, que podría tener algo que ver con el tamaño de la memoria caché de mi procesador, bsxfunno es un salto tan malo, ya que solo necesita asignar la matriz de salida.

A continuación encontrará el código que usé para el tiempo:

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
fuente
Gracias por una excelente respuesta +1. He marcado esta la respuesta, ya que es la discusión más completa y también (en este momento) ha recibido la mayoría de los votos positivos.
Colin T Bowers
40

En mi caso, lo uso bsxfunporque me evita pensar en los problemas de columna o fila.

Para escribir su ejemplo:

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

Tengo que resolver varios problemas:

1) size(A,1)osize(A,2)

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

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

4) mean(A)omean(A,2)

Cuando lo uso bsxfun, solo tengo que resolver el último:

a) mean(A)omean(A,2)

Puede pensar que es flojo o algo así, pero cuando lo uso bsxfun, tengo menos errores y programo más rápido .

Además, es más corto, lo que mejora la velocidad de escritura y la legibilidad .

Oli
fuente
1
Gracias por la respuesta Oli. +1 ya que creo que esta respuesta aportó algo además de las respuestas de angainor y Jonas. Me gustó especialmente la forma en que estableció la cantidad de problemas conceptuales que deben resolverse en una determinada línea de código.
Colin T Bowers
16

Pregunta muy interesante! Recientemente me topé con tal situación al responder esta pregunta. Considere el siguiente código que calcula los índices de una ventana deslizante de tamaño 3 a través de un vector 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

¡En este caso bsxfunes casi dos veces más rápido! Es útil y rápido porque evita la asignación explícita de memoria. para las matrices idx0y idx1, guardándolas en la memoria, y luego volviéndolas a leer solo para agregarlas. Dado que el ancho de banda de la memoria es un activo valioso y, a menudo, el cuello de botella en las arquitecturas de hoy en día, desea usarlo sabiamente y disminuir los requisitos de memoria de su código para mejorar el rendimiento.

bsxfunle permite hacer exactamente eso: crear una matriz basada en la aplicación de un operador arbitrario a todos los pares de elementos de dos vectores, en lugar de operar explícitamente en dos matrices obtenidas al replicar los vectores. Esa es la expansión singleton . También puede considerarlo como el producto externo de BLAS:

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

Multiplica dos vectores para obtener una matriz. Solo que el producto externo solo realiza la multiplicación y bsxfunpuede aplicar operadores arbitrarios. Como nota al margen, es muy interesante ver quebsxfun es tan rápido como el producto externo BLAS. Y generalmente se considera que BLAS ofrece el rendimiento.

Editar Gracias al comentario de Dan, aquí hay un gran artículo de Loren que habla exactamente de eso.

angainor
fuente
77
Este artículo puede ser relevante: blogs.mathworks.com/loren/2008/08/04/…
Dan
@ Dan Gracias por una gran referencia.
angainor
Gracias por una gran respuesta angainor. +1 por ser el primero en establecer claramente la principal ventaja de bsxfuncon un buen ejemplo.
Colin T Bowers
13

A partir de R2016b, Matlab admite la expansión implícita para una amplia variedad de operadores, por lo que en la mayoría de los casos ya no es necesario usar bsxfun:

Anteriormente, esta funcionalidad estaba disponible a través de la bsxfunfunción. Ahora se recomienda reemplazar la mayoría de los usos bsxfuncon llamadas directas a las funciones y operadores que admiten la expansión implícita . En comparación con el uso bsxfun, la expansión implícita ofrece una velocidad más rápida , un mejor uso de la memoria y una mejor legibilidad del código .

Hay una discusión detallada de Expansión implícita y su desempeño en el blog de Loren. Para citar a Steve Eddins de MathWorks:

En R2016b, la expansión implícita funciona tan rápido o más rápido que bsxfunen la mayoría de los casos. Las mejores ganancias de rendimiento para la expansión implícita son con matrices pequeñas y tamaños de matriz. Para tamaños de matriz grandes, la expansión implícita tiende a ser aproximadamente la misma velocidad que bsxfun.

nirvana-msu
fuente
8

Las cosas no siempre son consistentes con los 3 métodos comunes: la repmatexpulsión por indexación y bsxfun. Se vuelve más interesante cuando aumenta aún más el tamaño del vector. Ver trama:

comparación

bsxfunen realidad se vuelve un poco más lento que los otros dos en algún momento, pero lo que me sorprendió es que si aumenta el tamaño del vector aún más (> 13E6 elementos de salida), bsxfun de repente se vuelve más rápido en aproximadamente 3x. Sus velocidades parecen saltar en pasos y el orden no siempre es consistente. Supongo que también podría depender del tamaño del procesador / memoria, pero en general creo que me quedaría bsxfunsiempre que sea posible.

Justin Wong
fuente