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

Mi pregunta: Me he dado cuenta de que muchas buenas respuestas a las preguntas de Matlab sobre SO frecuentemente usan 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 usar bsxfun será mucho más rápido que el enfoque “manual”. Estaría realmente interesado en ver un ejemplo de tal situación y una explicación de por qué es más rápido.

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

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

  1. bsxfun es más rápido que repmat (ver a continuación)
  2. bsxfun requiere menos tipeo
  3. Usar bsxfun , como usar accumarray , me hace sentir bien sobre mi comprensión de Matlab.

bsxfun replicará las matrices de entrada a lo largo de sus “dimensiones únicas”, 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”. Como un aparte, las dimensiones singleton son las que se descartarán si llama squeeze .

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

He realizado una comparación de velocidad entre repmat y bsxfun con R2012b en mi laptop decentemente rápida.

enter image description here

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

enter image description here

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

A continuación encontrará el código que utilicé 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 

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

Para escribir tu ejemplo:

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

Tengo que resolver varios problemas:

1) size(A,1) o size(A,2)

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

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

4) mean(A) o mean(A,2)

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

a) mean(A) o mean(A,2)

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

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

Pregunta muy interesante! Recientemente me encontré con exactamente esa 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, bsxfun es casi dos veces más rápido! Es útil y rápido porque evita la asignación explícita de memoria para las matrices idx0 e idx1 , guardándolas en la memoria y luego leyéndolas nuevamente 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 architectures de hoy en día, desea utilizarlo con prudencia y disminuir los requisitos de memoria de su código para mejorar el rendimiento.

bsxfun permite hacer justamente eso: crear una matriz basada en aplicar 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. 

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

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

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

Anteriormente, esta funcionalidad estaba disponible a través de la función bsxfun . Ahora se recomienda que reemplace la mayoría de los usos de bsxfun con llamadas directas a las funciones y operadores que soportan la expansión implícita . En comparación con el uso de 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 bsxfun en la mayoría de los casos. Las mejores ganancias de rendimiento para la expansión implícita son con pequeños tamaños de matrices y matrices. Para tamaños de matriz grandes, la expansión implícita tiende a ser aproximadamente la misma velocidad que bsxfun .

Las cosas no siempre son consistentes con los 3 métodos comunes: repmat , expension mediante la indexación de unos, y bsxfun . Se vuelve más interesante cuando aumenta el tamaño del vector aún más. Ver diagtwig:

comparación

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

    Intereting Posts