arrays matlab performance bsxfun

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 con N×1 array
  • N×N×N×N array con N×1×N array
  • N×N array con 1×N array
  • N×N×N×N array con 1×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 que timeit 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'')