La variable parece cambiar de tamaño en cada iteración del bucle, ¿qué?

Resuelto Shai asked hace 10 años • 4 respuestas

Al escribir el siguiente código Matlab:

for ii=1:n
    x(ii) = foo( ii ); % foo is some function of ii that cannot be vectorized.
end

Recibo la siguiente advertencia de m-lint :

La variable xparece cambiar de tamaño en cada iteración del bucle.

Mi pregunta:

  1. ¿Qué significa esa advertencia?
  2. ¿Por qué es malo cambiar el tamaño de la variable en cada iteración?
  3. ¿Cómo se puede solucionar este problema?

Esta pregunta no es un duplicado de esta , ya que trata de aspectos más generales de la preasignación, más bien de una instancia específica de la misma.

Shai avatar Jan 09 '14 21:01 Shai
Aceptado

Bueno, lo primero es lo primero.

1. ¿Qué significa esta advertencia?

Este código es correcto en términos de sintaxis y se ejecutará correctamente y devolverá el resultado esperado: el ii-ésimo elemento xcontendrá el valor foo( ii ).
Sin embargo, antes de que se ejecute este pequeño fragmento de código, la variable xno está definida. Ahora, cuando comienza el ciclo, x(1)se le asigna el valor foo( 1 ), por lo que Matlab lo crea xcomo una matriz de longitud 1. En la segunda iteración x(2)se asigna el valor foo( 2 ), por lo que Matlab debe cambiar xpara tener una longitud de 2, y así sucesivamente: xcambia su longitud/tamaño en cada iteración.

2. ¿Por qué es malo cambiar el tamaño de la variable en cada iteración?

Considere lo que sucede en segundo plano (en términos de asignación de memoria) cuando xcambia su tamaño en cada iteración: en cada iteración, Matlab necesita encontrar un espacio de memoria libre para alojar el nuevo tamaño de x. Si tiene suerte, habrá suficiente espacio libre inmediatamente después, xpor lo que todo lo que sucederá es cambiar la cantidad de memoria asignada xy escribir el nuevo valor en el lugar correcto.
Sin embargo, si no hay suficiente espacio libre justo después de x, Matlab tiene que encontrar un nuevo lugar para todos los ii-1elementos que ya están en x, asignar este nuevo espacio para x, copiar todos ii-1los valores que ya están en xel nuevo lugar y liberar el antiguo lugar xutilizado. Estas operaciones de asignación sin copia que se realizan en segundo plano pueden llevar mucho tiempo, especialmente cuando xson grandes.

3. ¿Cómo se puede solucionar este problema?

La solución más sencilla es preasignar todo el espacio xnecesario antes del ciclo:

x = zeros(1,n); 
for ii=1:n
    x( ii ) = foo( ii );
end

Al realizar la preasignación, nos aseguramos de que xse asigne toda la memoria que requiere por adelantado, por lo que no se necesita una costosa asignación/copia de memoria cuando se ejecuta el bucle.

Una solución alternativa y genial al problema.

Si eres demasiado vago (como yo) y no quieres preasignar, simplemente puedes:

for ii=n:-1:1
    x( ii ) = foo( ii );
end

De esta manera, la primera vez xque se asigna un valor, se asigna a su n-ésimo elemento (el último) y por lo tanto Matlab inmediatamente asigna espacio para todos nlos elementos de x.
¡Fresco!

Shai avatar Jan 09 '2014 14:01 Shai

Mi respuesta llega un poco tarde, pero hay algunas cosas que mencionaría con respecto al crecimiento de la matriz y la preasignación en MATLAB.

Lo primero que hay que tener en cuenta es que MATLAB ha mejorado mucho el rendimiento del crecimiento automático de matrices en versiones recientes, por lo que el impacto en el rendimiento implícito en la advertencia podría no ser tan malo si se hace bien (ver más abajo). Aún así, la mejor práctica es preasignar su matriz (por ejemplo, con zeros).

La advertencia explicada

A partir de R2014a, la explicación detallada de la advertencia establece lo siguiente:

El tamaño de la variable o matriz indicada parece cambiar con cada iteración del bucle. Normalmente, este mensaje aparece porque una matriz está creciendo por asignación o concatenación. Hacer crecer una matriz mediante asignación o concatenación puede resultar costoso. Para matrices grandes, MATLAB debe asignar un nuevo bloque de memoria y copiar el contenido de la matriz anterior a la nueva matriz a medida que realiza cada asignación.

Los programas que cambian el tamaño de una variable de esta manera pueden dedicar la mayor parte de su tiempo de ejecución a esta actividad ineficiente. ...

A partir de este extracto, debería quedar bastante claro por qué la preasignación es una idea inteligente si a usted le preocupa el rendimiento.

Nota al margen: hay información limitada sobre el algoritmo utilizado para la reasignación durante el crecimiento de la matriz, pero Steve Eddins proporcionó cierta información en la misma publicación del blog, que resumí en esta respuesta anterior .

Optimización automática del crecimiento de la matriz

Si desea utilizar el cambio de tamaño de una matriz dinámica creciendo a lo largo de una dimensión (sin preasignación), hay formas de hacerlo correctamente. Vea esta publicación del blog de MathWorks de Steve Eddins . Lo más importante a tener en cuenta es que debes crecer en la última dimensión para obtener el mejor rendimiento . Esto no es un problema en su caso ya que la matriz es 1D. Entonces, si decide dejarlo funcionar, colóquelo %#ok<SAGROW>en la misma línea que la advertencia, después del código culpable, para silenciar la advertencia.

Yair analiza el cambio de tamaño de la matriz dinámica en otra publicación de su blog . Además, hay formas de asignar una matriz sin inicializar usando algunas acrobacias complicadas de la API MEX, pero eso es todo.

Preasignación

Se recomienda la preasignación. Adquiere el hábito, aprende a amar zeros. Si está decidido a exprimir al máximo el rendimiento de MATLAB, Yair Altman tiene un par de artículos excelentes sobre el tema de la preasignación de memoria:

  • Rendimiento de preasignación
  • Rendimiento de preasignación y subprocesos múltiples
chappjc avatar Jan 23 '2014 22:01 chappjc