arrays - En Matlab, ¿cuándo es óptimo usar bsxfun?
(5)
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 debsxfun
con llamadas directas a las funciones y operadores que soportan la expansión implícita . En comparación con el uso debsxfun
, 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 quote 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 quebsxfun
.
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"?
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 .
Hay tres razones por las que uso bsxfun
( documentation , enlace de blog )
-
bsxfun
es más rápido querepmat
(ver a continuación) -
bsxfun
requiere menos tipeo - Usar
bsxfun
, como usaraccumarray
, 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 compilado, 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.
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
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
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 diagrama:
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.
Pregunta muy interesante! Recientemente me encontré con exactamente esa situación al responder this 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 arquitecturas 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.