close Warning: Can't synchronize with repository "(default)" (/var/svn/tolp does not appear to be a Subversion repository.). Look in the Trac log for more information.

Opened 14 years ago

Closed 14 years ago

Last modified 9 years ago

#1101 closed doubt (fixed)

Excessive and strange RAM usage

Reported by: Pedro Gea Owned by: Víctor de Buen Remiro
Priority: highest Milestone: Mantainance
Component: Kernel Version:
Severity: blocker Keywords:
Cc:

Description

Intentando solucionar problemas debidos al excesivo consumo de memoria RAM me encuentro con una circunstancia que no soy capaz de explicarme.

Resulta que al utilizar dos funciones que aparentemente deberían funcionar igual se obtienen comportamientos distintos en el consumo de RAM. Esto también se aprecia en el incremento de NObjects.

Aunque merece la pena insistir que no se trata de un problema de perdida de memoria, sino de un aparente consumo excesivo de memoria.

  1. En primer lugar creamos un conjunto de matrices grandes para aprenciar bien los cambios en RAM.
Set matrices = For(1, 500, Matrix (Real i) {
  Rand(50000, 1, 0, 1)
});

Si comprobamos el incremento de NObject obtenemos 2507:

Real no1 = Copy(NObject);
Set matrices = For(1, 500, Matrix (Real i) {
  Rand(50000, 1, 0, 1)
});
Real no2 = Copy(NObject);
Real no2 - no1;
//-> 2507 

Mientras que si asignamos un nombre a las matrices recién creadas obtenemos 507:

Real no1 = Copy(NObject);
Set matrices = For(1, 500, Matrix (Real i) {
  Matrix a = Rand(50000, 1, 0, 1)
});
Real no2 = Copy(NObject);
Real no2 - no1;
//-> 507 

Sin embargo esto no parece afectar al consumo de RAM siendo en ambos casos de unos 288 MB aproximadamente.

  1. En segundo lugar definimos dos funciones que devuelven la matriz pasada como argumento con el nombre "nombre":
Matrix Use1(Matrix object) {
  Matrix nombre = Copy(object)
};
Matrix Use2(Matrix object) {
  Matrix nombre = object
};

La primera asigna una copia del objeto, mientras que la segunda no hace la copia.
Aunque como son asignaciones por valor se espera que esto sea lo mismo.

Las consecuencias sin embargo no son para nada similares:

Si hacemos uso de Use1 (la que hace copia) tanto el valor de NObject como la RAM aumentan lo esperado. La RAM crece otros 288 MB hasta los 576 MB comparada con el comienzo.

Real no3 = Copy(NObject);

Set matrices2 = EvalSet(matrices, Anything (Matrix m) {
  Matrix data = m;
  Use1(data) 
});

Real no4 = Copy(NObject);
Real no4 - no3;
//-> 505

Mientras que si hacemos uso de Use2 tanto el valor de NObject como la RAM aumentan excesivamente. La RAM crece 576 MB (en lugar de la mitad) llegando hasta los 864 MB comparada con el comienzo:

Real no3 = Copy(NObject);

Set matrices2 = EvalSet(matrices, Anything (Matrix m) {
  Matrix data = m;
  Use2(data) 
});

Real no4 = Copy(NObject);
Real no4 - no3;
//-> 2505

El problema está en el consumo de RAM, y no en que aparezcan más o menos NObjects, aunque sin duda sería interesante comprender lo que está ocurriendo.

Attachments (1)

TestInstances.tol (1.8 KB) - added by Pedro Gea 14 years ago.

Download all attachments as: .zip

Change History (13)

comment:1 Changed 14 years ago by Pedro Gea

Summary: Excessive and strange RAM ussageExcessive and strange RAM usage

comment:2 Changed 14 years ago by Víctor de Buen Remiro

Status: newaccepted

La primera parte es clara: si no les pones nombre se guarda todo el árbol de objetos.
En la expresión Rand(50000, 1, 0, 1) hay 4 argumentos más el resultado son 5, por 500 iteraciones dan 2500 objetos de los que 2000 no ocupan casi nada en comparación con las matrices. Los otros 7 son los de las expresiones creadas entre medio, la del ciclo y los contadores de objetos y demás.

Sobre el segundo punto parece quedarse una copia temporal de más. Luego se borrará por lo que a la larga no importa mucho, salvo que se trate de objetos muy grandes como es el caso. Lo miraré a ver si encuentro algo corregible, aunque ya adelanto que es el tipo de cosas complicadas, que arreglas una y fastidias cuatro.

comment:3 Changed 14 years ago by Víctor de Buen Remiro

Component: VariousKernel
Milestone: Mantainance

comment:4 Changed 14 years ago by Pedro Gea

La primera parte ya la entiendo, aunque no la esperaba. Entiendo que es un comportamiento propio de las funciones compiladas y de las funciones que en general no devuelven como resultado una variable con nombre.

Así el comportamiento de NObject al usar myRand1 o myRand2 en lugar de Rand es distinto: se almacena un árbol de objetos aún mayor en el primer caso, y simplemente la matriz en el segundo.

Matrix myRand1(Real row, Real col, Real min, Real max) {
  Rand(row, col, min, max)
};
Matrix myRand2(Real r, Real c, Real mi, Real ma) {
  Matrix return = Rand(row, col, min, max)
};

No sé si es razonable, pero me veo tentado a devolver siempre los resultados con nombre (return) en todas las funciones y métodos que programe. Ya que no le veo ninguna utilidad al almacenamiento del árbol de objetos y tiendo a relacionarlo (con acierto o no) con lo que ocurre en la segunda parte.

Si al usar Use2 nombro la matriz retornada, el problema desaparece:

Real no3 = Copy(NObject);

Set matrices2 = EvalSet(matrices, Anything (Matrix m) {
  Matrix data = m;
  Matrix return = Use2(data)
});

Real no4 = Copy(NObject);
Real no4 - no3;
//-> 505

¿Es razonable que me plantee revisar todos mis funciones y métodos y en general la manera de programar las salidas de las funciones (Code)?

Mi preocupación ya no es tanto que este problema se solucione o no, sino comprender por qué ocurre y cómo evitarlo para poder asegurar la eficiencia y calidad del código que programamos.

comment:5 Changed 14 years ago by Víctor de Buen Remiro

Yo quiero revisar este tema con un poco de tranquilidad, a ver si veo cómo hacer que siempre se comporte igual.

El motivo detrás de todo esto está en el tratamiento de los tipos abstractos como series y conjuntos temporales infinitos que necesitan todo su árbol para poder evaluarse pues no se pueden resumir en un conjunto finito de datos.

Creo que podría intervenir en el evaluador de funciones cuando el tipo de datos lo permita y reducir el objeto devuelto a su contenido eliminando el objeto temporal con el árbol semántico.

El problema es que eso requiere un nivel de chequeo importante, no sólo hay que pasar los tests sino ver cómo afecta a la velocidad y a la memoria, y comprobar que no hay efectos secundarios inesperados.

Cuando la tenga, ¿podría pasarte una versión binaria de pruebas para que comprobaras también por tu cuenta si mejora el rendimiento en el uso de MMS?

comment:6 Changed 14 years ago by Pedro Gea

Añado un nuevo tipo de extraños comportamientos en la RAM que quizá probablemente estén relacionados con los anteriores.
Adjunto un archivo con el código.

El test está construido siguiendo una línea similar a la que se usó al enunciar este tique: se comprueba el número de NObjects y el uso de RAM al hacer una misma cosa de varias formas distintas.

En el código que se adjunta:

  1. se crean unas matrices grandes (los datos) que nos permiten comprobar claramente los cambios en RAM,
  2. se crean unas instancias (unos nameblocks) con un atributo data de tipo Set conteniendo a una de estas matrices,
  3. se liberan los datos, manteniendo sólo las instancias.

Se usan tres formas (según el método Instance1, Instance2 o Instance3):

  1. En el primero se construye la instancia de un modo que se podría denominar normal, obteniéndose un numero alto de NObjects y una RAM que no se deja liberar.
  2. En el segundo se construye la instancia vacía (sin datos) y luego se añaden. En este caso los NObjects se reducen y la RAM se libera.
  3. Se devuelve una copia de la instancia creada, en lugar de ella misma, reduciéndose aún más el número de NObjects y liberándose la RAM.

La duda, lo habitual, ¿qué está ocurriendo?

P.S. Sin problemas, respecto a lo de usar una versión de pruebas para comprobar mejoras de rendimiento. Escríbeme un mail para lo que sea.

Changed 14 years ago by Pedro Gea

Attachment: TestInstances.tol added

comment:7 Changed 14 years ago by Víctor de Buen Remiro

Creo que esto último se debe a que DeepCopy no es un operador especial como Copy sino una función normal definida con las macros DeclareContensClass y DefExtOpr que no tiene ningún control sobre el objeto devuelto, sino sobre su contenido.

Voy a probar a reecribirla como función especial a ver si se soluciona el problema y ya te paso la versión de pruebas.

comment:8 Changed 14 years ago by Víctor de Buen Remiro

Resolution: fixed
Status: acceptedclosed

(In [3226]) Refs #1106
If and Case will show a warning when the condition is unknown

Fixes #1101
DeepCopy is transformed in an special function

comment:9 Changed 14 years ago by Víctor de Buen Remiro

(In [3227]) Fixes #1106
Select will show an error when the condition is unknown

Refs #1101
DeepCopy is transformed in an special function

comment:10 Changed 14 years ago by Víctor de Buen Remiro

(In [3228]) Fixes #1101
User functions will return a copy of the result for allowed types

comment:11 Changed 14 years ago by Víctor de Buen Remiro

(In [3230]) Refs #1101

comment:12 Changed 9 years ago by Pedro Gea

(In [6816]) Refs #1101
Alguna corrección posterior al tique ha reducido en tol.3.2 el número de NObjects innecesarios un poco más de modo que la comprobación de número de objetos creados no es exacta.
Se corrige el test.

Note: See TracTickets for help on using tickets.