arrays - ¿Cuánto más rápida es la expansión implícita en comparación con bsxfun?
matlab performance (1)
Como commented Steve Eddins , la expansión implícita (introducida en Matlab R2016b) es más rápida que bsxfun
para tamaños de matriz pequeños, y tiene una velocidad similar para matrices grandes:
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 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
.
Además, la dimensión a lo largo de la cual tiene lugar la expansión puede tener una influencia:
Cuando hay una expansión en la primera dimensión, los operadores pueden no ser tan rápidos como
bsxfun
.
(¡Gracias a @Poelie y @rayryeng por dejarme know about esto!)
Dos preguntas surgen naturalmente:
- ¿Cuánto más rápida es la expansión implícita en comparación con
bsxfun
? - ¿Para qué tamaños de arrays o formas es la diferencia significativa?
Para medir la diferencia de velocidad, se han realizado algunas pruebas. Las pruebas consideran dos operaciones diferentes :
- adición
- poder
y cuatro formas diferentes de las matrices a operar:
-
N×N
array conN×1
array -
N×N×N×N
array conN×1×N
array -
N×N
array con1×N
array -
N×N×N×N
array con1×N×N
array
Para cada una de las ocho combinaciones de operaciones y formas de matriz, la misma operación se realiza con expansión implícita y con bsxfun
. Se utilizan varios valores de N
, para cubrir el rango de arreglos pequeños a grandes. timeit
se utiliza para la sincronización confiable.
El código de referencia se da al final de esta respuesta. Se ha ejecutado en Matlab R2016b, Windows 10, con 12 GB de RAM.
Resultados
Los siguientes gráficos muestran los resultados. El eje horizontal es el número de elementos de la matriz de salida, que es una mejor medida del tamaño que N
es.
Las pruebas también se han realizado con operaciones lógicas (en lugar de aritméticas). Los resultados no se muestran aquí por brevedad, pero muestran una tendencia similar.
Conclusiones
Según los gráficos:
- Los resultados confirman que la expansión implícita es más rápida para matrices pequeñas y tiene una velocidad similar a
bsxfun
para matrices grandes. - La expansión a lo largo de la primera o de otras dimensiones no parece tener una gran influencia, al menos en los casos considerados.
- Para matrices pequeñas la diferencia puede ser de diez veces o más. Sin embargo,
timeit
quetimeit
no es preciso para tamaños pequeños porque el código es demasiado rápido (de hecho, emite una advertencia para tamaños tan pequeños). - Las dos velocidades se vuelven iguales cuando el número de elementos de la salida alcanza aproximadamente
1e5
. Este valor puede ser dependiente del sistema.
Dado que la mejora de la velocidad solo es significativa cuando las matrices son pequeñas, que es una situación en la que cualquiera de los dos enfoques es muy rápido, usar expansión implícita o bsxfun
parece ser principalmente una cuestión de gusto, legibilidad o compatibilidad con versiones anteriores.
Código de referencia
clear
% NxN, Nx1, addition / power
N1 = 2.^(4:1:12);
t1_bsxfun_add = NaN(size(N1));
t1_implicit_add = NaN(size(N1));
t1_bsxfun_pow = NaN(size(N1));
t1_implicit_pow = NaN(size(N1));
for k = 1:numel(N1)
N = N1(k);
x = randn(N,N);
y = randn(N,1);
% y = randn(1,N); % use this line or the preceding one
t1_bsxfun_add(k) = timeit(@() bsxfun(@plus, x, y));
t1_implicit_add(k) = timeit(@() x+y);
t1_bsxfun_pow(k) = timeit(@() bsxfun(@power, x, y));
t1_implicit_pow(k) = timeit(@() x.^y);
end
% NxNxNxN, Nx1xN, addition / power
N2 = round(sqrt(N1));
t2_bsxfun_add = NaN(size(N2));
t2_implicit_add = NaN(size(N2));
t2_bsxfun_pow = NaN(size(N2));
t2_implicit_pow = NaN(size(N2));
for k = 1:numel(N1)
N = N2(k);
x = randn(N,N,N,N);
y = randn(N,1,N);
% y = randn(1,N,N); % use this line or the preceding one
t2_bsxfun_add(k) = timeit(@() bsxfun(@plus, x, y));
t2_implicit_add(k) = timeit(@() x+y);
t2_bsxfun_pow(k) = timeit(@() bsxfun(@power, x, y));
t2_implicit_pow(k) = timeit(@() x.^y);
end
% Plots
figure
colors = get(gca,''ColorOrder'');
subplot(121)
title(''N/times{}N, N/times{}1'')
% title(''N/times{}N, 1/times{}N'') % this or the preceding
set(gca,''XScale'', ''log'', ''YScale'', ''log'')
hold on
grid on
loglog(N1.^2, t1_bsxfun_add, ''s-'', ''color'', colors(1,:))
loglog(N1.^2, t1_implicit_add, ''s-'', ''color'', colors(2,:))
loglog(N1.^2, t1_bsxfun_pow, ''^-'', ''color'', colors(1,:))
loglog(N1.^2, t1_implicit_pow, ''^-'', ''color'', colors(2,:))
legend(''Addition, bsxfun'', ''Addition, implicit'', ''Power, bsxfun'', ''Power, implicit'')
subplot(122)
title(''N/times{}N/times{}N{}/times{}N, N/times{}1/times{}N'')
% title(''N/times{}N/times{}N{}/times{}N, 1/times{}N/times{}N'') % this or the preceding
set(gca,''XScale'', ''log'', ''YScale'', ''log'')
hold on
grid on
loglog(N2.^4, t2_bsxfun_add, ''s-'', ''color'', colors(1,:))
loglog(N2.^4, t2_implicit_add, ''s-'', ''color'', colors(2,:))
loglog(N2.^4, t2_bsxfun_pow, ''^-'', ''color'', colors(1,:))
loglog(N2.^4, t2_implicit_pow, ''^-'', ''color'', colors(2,:))
legend(''Addition, bsxfun'', ''Addition, implicit'', ''Power, bsxfun'', ''Power, implicit'')