¿Es A == 0 realmente mejor que ~ A?

Introducción a la configuración del problema

Estaba haciendo algunos puntos de referencia que implican ~A y A==0 para una double array with no NaNs , que convierten a A en una matriz lógica donde todos los zeros se convierten en valores true y el rest se establecen como valores false .

Para la evaluación comparativa, he utilizado tres conjuntos de datos de entrada:

  • Datos de tamaño pequeño a pequeño: 15:5:100
  • Datos de tamaño pequeño a mediano: 50:40:1000
  • Datos de tamaño mediano a grande – 200:400:3800

La entrada se crea con A = round(rand(N)*20) , donde N es el parámetro tomado de la matriz de tamaños. Por lo tanto, N podría variar de 15 to 100 with stepsize of 5 para el primer conjunto y de manera similar para el segundo y tercer conjunto. Tenga en cuenta que estoy definiendo Datasize como N, por lo tanto, la cantidad de elementos sería Datasize ^ 2 o N ^ 2.

Código de Benchmarking

 N_arr = 15:5:100; %// for very small to small sized input array N_arr = 50:40:1000; %// for small to medium sized input array N_arr = 200:400:3800; %// for medium to large sized input array timeall = zeros(2,numel(N_arr)); for k1 = 1:numel(N_arr) A = round(rand(N_arr(k1))*20); f = @() ~A; timeall(1,k1) = timeit(f); clear f f = @() A==0; timeall(2,k1) = timeit(f); clear f end 

Resultados

enter image description here

enter image description here

enter image description here

Finalmente las preguntas

Uno puede ver cómo A==0 tiene un rendimiento mejor que ~A en todos los tamaños de datos. Aquí hay algunas observaciones y preguntas relacionadas junto con ellos:

  1. A==0 tiene un operador relacional y un operando, mientras que ~A tiene solo un operador relacional. Ambos producen arreglos lógicos y ambos aceptan arreglos dobles. De hecho, A==0 también funcionaría con NaNs , mientras que ~A no lo hará. Entonces, ¿por qué sigue siendo ~A al menos no tan bueno como A==0 ya que parece que A==0 está haciendo más trabajo o me falta algo aquí?

  2. Hay una gota peculiar de tiempo transcurrido con A==0 y, por tanto, un mayor rendimiento en N = 320 , es decir, 102400 elementos para A. He observado esto en muchas ejecuciones con ese tamaño en dos sistemas diferentes a los que tengo acceso. Entonces, ¿qué está pasando allí?

Esto no es estrictamente una respuesta sino más bien mi contribución a la discusión

profiler el profiler de profiler para investigar una versión ligeramente modificada de tu código:

 N_arr = 200:400:3800; %// for medium to large sized input array for k1 = 1:numel(N_arr) A = randi(1,N_arr(k1)); [~]=eq(A,0); clear A A = randi(1,N_arr(k1)); [~]=not(A); clear A end 

Usé los siguientes indicadores de perfil (según la serie de publicaciones de UndocumentedMatlab sobre Profiler ):

 profile('-memory','on'); profile('on','-detail','builtin'); 

Y aquí hay un extracto de los resultados del perfilador ( enlace a la imagen más grande ): Salida de Profiler

Parece que la variante == asigna un poquito de memoria adicional que le permite trabajar su magia …

En relación con su pregunta 2 : antes de eliminar el mantenimiento del timeall , traté de trazar los mismos gráficos que hizo en Excel. No observé el comportamiento que mencionaste para N = 320 . Sospecho que esto puede tener algo que ver con los contenedores adicionales (es decir, los identificadores de funciones) que está usando en su código.


Pensé en adjuntar la documentación disponible para las funciones discutidas para una referencia rápida.

La documentación para ~ (\ MATLAB \ R20 ??? \ toolbox \ matlab \ ops \ not.m):

 %~ Logical NOT. % ~A performs a logical NOT of input array A, and returns an array % containing elements set to either logical 1 (TRUE) or logical 0 (FALSE). % An element of the output array is set to 1 if A contains a zero value % element at that same array location. Otherwise, that element is set to % 0. % % B = NOT(A) is called for the syntax '~A' when A is an object. % % ~ can also be used to ignore input arguments in a function definition, % and output arguments in a function call. See "help punct" % Copyright 1984-2005 The MathWorks, Inc. 

La documentación para == (\ MATLAB \ R20 ??? \ toolbox \ matlab \ ops \ eq.m):

 %== Equal. % A == B does element by element comparisons between A and B % and returns a matrix of the same size with elements set to logical 1 % where the relation is true and elements set to logical 0 where it is % not. A and B must have the same dimensions unless one is a % scalar. A scalar can be compared with any size array. % % C = EQ(A,B) is called for the syntax 'A == B' when A or B is an % object. % Copyright 1984-2005 The MathWorks, Inc. 

También no es estrictamente una respuesta, pero quiero agregar a la discusión. Tal vez se reduce a su tiempo de función.

Probé la función de Dev-iL. He creado un perfil y obtuve los mismos resultados: el EQ parece ser más rápido que el NOT y el EQ parece asignar un poco más de memoria que NOT . Parecía lógico que si el operador de EQ está asignando más memoria, entonces, a medida que aumentamos el tamaño de la matriz, la asignación de memoria también boostía. Sospechosamente, ¡no es así!

Continué y eliminé todo lo innecesario y repetí el ciclo para N=1000 iteraciones. El generador de perfiles parecía estar de acuerdo en que EQ es más rápido que NOT . Pero no estaba convencido.

Lo siguiente que hice fue eliminar el extraño aspecto [~] = ~A y [~] = A == 0 para algo más humano como tmp1 = ~A y tmp2 = A == 0 y voilà! Los tiempos de ejecución son casi iguales.

Resultados del perfilador

Así que supongo que estás haciendo algo similar dentro de tu función timeid . Vale la pena notar que la asignación [~] ralentiza ambas funciones, pero NOT parece ser más afectada que EQ .

Ahora la gran pregunta: ¿por qué el operador [~] frena las funciones? No lo sé. Quizás solo Mathworks pueda responder eso. Puede abrir un ticket en la página web de Mathworks.

Respuesta parcial : tienen casi el mismo tiempo de ejecución, incluso para arreglos grandes (la matriz más grande que probé es 10K).

Parte no respondida : por qué [~] asignación ralentiza el código. Por qué NOT es más afectado que EQ

Mi código:

 clear all clear classes array_sizes = [1000:1000:10000]; repetitions = 10000; for i = 1:length(array_sizes) A1 = randi([0, 1], array_sizes(i), 1); for j = 1:repetitions tmp1 = eq(A1, 0); end end for i = 1:length(array_sizes) A2 = randi([0, 1], array_sizes(i), 1); for j = 1:repetitions tmp2 = not(A2); end end