La variable parece cambiar de tamaño en cada iteración del bucle, ¿qué?
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
x
parece cambiar de tamaño en cada iteración del bucle.
Mi pregunta:
- ¿Qué significa esa advertencia?
- ¿Por qué es malo cambiar el tamaño de la variable en cada iteración?
- ¿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.
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 x
contendrá el valor foo( ii )
.
Sin embargo, antes de que se ejecute este pequeño fragmento de código, la variable x
no está definida. Ahora, cuando comienza el ciclo, x(1)
se le asigna el valor foo( 1 )
, por lo que Matlab lo crea x
como una matriz de longitud 1. En la segunda iteración x(2)
se asigna el valor foo( 2 )
, por lo que Matlab debe cambiar x
para tener una longitud de 2, y así sucesivamente: x
cambia 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 x
cambia 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, x
por lo que todo lo que sucederá es cambiar la cantidad de memoria asignada x
y 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-1
elementos que ya están en x
, asignar este nuevo espacio para x
, copiar todos ii-1
los valores que ya están en x
el nuevo lugar y liberar el antiguo lugar x
utilizado. Estas operaciones de asignación sin copia que se realizan en segundo plano pueden llevar mucho tiempo, especialmente cuando x
son grandes.
3. ¿Cómo se puede solucionar este problema?
La solución más sencilla es preasignar todo el espacio x
necesario 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 x
se 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 x
que se asigna un valor, se asigna a su n
-ésimo elemento (el último) y por lo tanto Matlab inmediatamente asigna espacio para todos n
los elementos de x
.
¡Fresco!
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