sábado, 7 de marzo de 2009

Limiting memory usage of a python script

I've been running recently some python and java experiments with important memory requirements on a machine shared with other people, some of them with the same needs on that machine.
There were no restrictions on resources usage by a user or a process, so controlled memory usage was the user's responsibility.

Java has a parameter to limit the amount of memory the virtual machine can use. If it exceeds that value, the machine exits and the running program fails. It is specified with java -Xmx value.

I couldn't find something similar for python so I procrastinated a bit writing a bash script to control the processes.
I made a cycle that monitored the values from /proc/meminfo and killed the running process if some limit was reached.
The problem with that solution was that my experiments were not just one program, but many, controlled by a bash script, so this solution would just kill the bash script and not the process doing the important memory usage. So I added some extra code borrowed from the web to kill also the subprocesses. That made the code longer and more complex, leading to this:

control_run.sh:
PROGRAM=$*

#Also kills process children, and their children, and so on
KILL_CHILDREN=1

#Minimum free memory available (in Kb.)
MEM_FREE_LIMIT=300000

#Minimum free swap (in Kb.)
SWAP_FREE_LIMIT=500000

#Run the program in background and get its PID
$PROGRAM &
PID=$!

#While the program is alive
ps $PID > /dev/null
while [ $? -eq "0" ]; do


MEM_FREE=$(grep MemFree: /proc/meminfo | egrep [0-9]+ -o)
#echo "Free Memory: ${MEM_FREE}"

SWAP_FREE=$(grep SwapFree: /proc/meminfo | egrep [0-9]+ -o)
#echo "Free Swap: ${SWAP_FREE}"

if [ "${MEM_FREE}" -le ${MEM_FREE_LIMIT} ] || [ "${SWAP_FREE}" -le ${SWAP_FREE_LIMIT} ]; then

if [ "${MEM_FREE}" -le ${MEM_FREE_LIMIT} ]; then
echo "free memory limit reached, exiting...";
else
echo "free swap limit reached, exiting...";
fi

if [ "${KILL_CHILDREN}" -eq 0 ]; then
echo "killing process with id $PID"
kill $PID
else
#code based on http://www.unix.com/unix-dummies-questions-answers/5245-script-kill-all-child-process-given-pid.html
KILL_PIDS=$PID

CHILDREN=`ps -ef| awk '$3 == '$PID' { print $2 }'`
while [ "$CHILDREN" != "" ]; do
KILL_PIDS="$KILL_PIDS $CHILDREN"
OLD_CHILDREN=$CHILDREN
CHILDREN=''
for i in $OLD_CHILDREN; do
CHILDREN="$CHILDREN `ps -ef| awk '$3 == '$i' { print $2 }'`"
done
done

echo "killing process with id $PID and its children"

for i in $KILL_PIDS
do
echo killing $i
kill $i
done

fi
exit 1;
fi

sleep 1

ps $PID > /dev/null
done

echo "program finished"


I tested it with a small python script that just consumes memory:

memory_consumer.py:
import time
a = range(100000)
while True:
a += range(100000)
print "printing something"
time.sleep(1)


and some bash scripts to execute three instances concurrently:


memory_consumer_main.sh:
#!/bin/sh
./memory_consumer_child.sh


memory_consumer_child.sh:
#!/bin/sh
python memory_consumer.py &
python memory_consumer.py &
python memory_consumer.py



we would run:
$./control_run.sh ./memory_consumer_main.sh
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
free memory limit reached, exiting...
killing process with id 9348 and its children
killing 9348
killing 9349
killing 9350
killing 9351
killing 9353



Another possibility I found was setting the memory limit with ulimit.
We can set it from the command line and enable it for the bash session, or incluide the statements in the first lines of a bash script.

That will limit the allowed memory for any process

for example:
$ ulimit -v 40000
$ ulimit -H -v 40000


There, we limited the available memory for a process to 40Mb.
Then,
$ python memory_consumer.py
printing something
printing something
printing something
printing something
Traceback (most recent call last):
File "memory_consumer.py", line 4, in
a += range(100000)
MemoryError


While simple, the problem with this approach is that it will only kill the process exceeding the memory usage. If we are running just one isolated process, that's fine. But in my case, the batch would continue in an erroneous state and produce wrong results.
Any comment or suggestion, including "hey stupid, python has the option -blabla to do exactly that" will be appreciated.

miércoles, 26 de noviembre de 2008

Experimentando con Qtdesigner y PyQt

El fin de semana estuve probando hacer algún programita con interfaz gráfica usando python y QT. Para eso usé QTDesigner para editar la UI, y PyQT4 para usar la librería QT desde python.

Seguí este tutorial: http://www.cs.usfca.edu/~afedosov/qttut/ que aunque está viejo (es para PyQT3) la idea es la misma y se lo puede adaptar con algunos ejemplos para PyQT4 que hay dando vueltas.

El resultado fue este programita para setear algunos parámetros de mencoder y utilizarlo para capturar TV.

http://code.google.com/p/mtvcgui/

miércoles, 29 de octubre de 2008

Overclockeando la MSX

Todavía conservo mi primer computadora, una MSX Spectravideo SVI-728 que le compré a mi primo cuando se compró su XT.
Tenía solamente un intérprete de BASIC, con el que escribí mis primeras líneas de código (de ahí mis pésimas prácticas :P), y una lectora de cassettes, que usaba para cargar alguno de los cientos de juegos que tenía.

Cada tanto me agarra la nostalgia y me pongo a jugar juegos de esa epoca en emuladores en mi PC actual. Revisando una página encontré una utilidad para pasar a WAV los ROMs usados en los emuladores (los cassettes si se escuchan en un equipo de audio reproducen un sonido similar al de un modem dial up). Grabé un CD de audio con los mejores juegos generando el WAV al doble de bitrate del que se usaba en los cassettes y armé un cable para conectar un discman a la entrada de datos del teclado/motherboard/cpu/etc. A veces falla dando error de input/output, pero ajustando el volumen apropiadamente ahora se puede cargar un juego en la mitad de tiempo :D

Notar el Microsoft en la pantalla. Gracias Bill! Ahora odio tu Internet Explorer pero esta maquinola me trajo muchas horas de diversión! Todo bien con Stallman, pero por más que el Emacs tenga un tetris y tenga que hacer más o menos las mismas combinaciones de teclas para escribir un caracter que para saltar y patear en el Yie Ar Kung Fu, no se compara :P






martes, 28 de octubre de 2008

Mis primeros 0 bytes aportados al core de Plone

Como parte de las actividades locas de los viernes en menttes, esta vez, como hace ya un par de semanas, estuvimos tratando de resolver tickets para el "Plone Tune-Up". En este evento se tratan de solucionar problemas reportados al bug tracker de Plone.

Estuve trabajando en un problema javascriptoso que para variar, solo ocurría en Internet Explorer.
A un documento de Plone, se le puede activar una tabla de contenidos desde "Editar" -> "Configuración" y activar la TdC. Esto genera con javascript la tabla en base a los headlines del documento.
El problema reportado era que en IE7, los títulos que contenían una @ se transformaban dentro de la tabla en el texto del link hacia el headline. En realidad también ocurre con títulos que comienzan con urls, y en IE6 (lo pude reproducir en linux usando IEs4linux)

El javascript que generaba la tabla usaba JQuery, en particular las líneas que metían el texto y el enlace eran puramente JQuery. Parecía ser un bug en esta librería, pero este thread en su lista me demostró que en realidad era todo culpa de IE.
La tremenda solución consistió en cambiar el orden de dos líneas del código.

Screenshot con el problema:




Screenshot con el javascript modificado:



El ticket ya fue cerrado, "commiteado" y "mergeado":D

La verbosidad de este patch va perfecto con mi personalidad, pero igual espero poder aportar para el próximo tuneup algún parche un poco más interesante :)

lunes, 18 de agosto de 2008

Receta milenaria para preparar lupines.

El simpático nombre que elegí para este blog hace que mucha gente inocente se encuentre con código Python cuando en realidad está buscando cómo hacer los lupines como los hacía la abuela.
Para orientar a estos internautas, les traigo su pedido al pie de la letra.

A continuación, mi abuela Titina les explica como preparar lupines (para acompañar con cerveza, como este blog recomienda)



Paso 1: Poner los porotos lupines en una olla con agua y abundante sal gruesa (un pocillito de café, como muestra la foto), y dejarlos remojar por una noche.


(la censura era solo para probar el efecto. Muy profesional, jeje)


Paso 2: Poner a hervir y dejar a fuego lento por 2 horas.



Paso 3: Esperar a que enfríe, retirar el agua removiéndolos para que se se limpien y agregar nuevamente agua con sal. Esto ya puede hacerse en el envase que se quiera utilizar para almacenarlos en la heladera.


Paso 4: Poner el envase en la heladera, y 2 veces al día renovar el agua y la sal, durante 3 días.


Luego ya se los puede consumir. Se los puede ir probando para ver si están en el punto justo, y no están amargos.
Si no se los consume, se deben guardar en la heladera y cambiar el agua con sal una vez al día.


Si tienen dudas o quieren comentar cómo les fue, escriban un comentario. Titina lee y agradece todos los mensajes.

sábado, 16 de agosto de 2008

tuvo o tubo

El desfotologueador fue un buen intento por tratar de salvar a nuestro castellano, pero no está dando resultado. Los servicios de mensajería instantánea nos exigen comunicarnos mediante texto a la misma velocidad a la que lo haríamos hablando y esto lleva a que no pensemos cómo se escribe lo que queremos decir. En estos casos muchas palabras se suelen escribir "como suena", la 'h' no importa, 'b', o 'v', da lo mismo, etc.

Para intentar cambiar esto, les voy a pasar una regla nemotécnica que me enseñó mi vieja de chiquito y nunca falló.
Me dijo:
Hijo, cuando dudes entre escribir tuvo o tubo, piensa:
Las tuberías, cañerías, etc. son por lo general conductos largos, que sirven para transportar algo desde un lugar a otro, ubicados en sitios geográficamente distantes.
Piensa por ejemplo en las distancias que debe recorrer tu caca para salir del núcleo urbano donde fue concebida.
Ergo, cuando se trate de un tubo como objeto, referido a una cañería por ejemplo, es "b" larga, larga como la cañería, idiota.
En el resto de los casos, es con "v" corta. Y así como se conjuga tuvo, del verbo tener, también es como se escriben los verbos que se conjungan de la misma manera, como obtener, estar, etc. Es obtuvo, y no obtubo, estuvo y no estubo.
También se lo ve mal escrito en conjugaciones donde ni siquiera puede confundirse con el tubo de la cañería. Como estube, en lugar de estuve. Esto puede deberse a la existencia de Youtube, pero en ese caso de nuevo, piensa que está hablando de un tubo. El tubo de rayos catódicos que tiene el tele. O sea que quiere decir "Tu tubo" (de rayos catódicos) o "Tu tele".

Pensarán que mi vieja era visionaria, pero se refería al service de TV y cassetteras llamado Youtube que estaba a la vuelta de mi casa y que ahora está iniciando un juicio millonario contra el popular sitio de internet.

En resumen:
'b', para "tubo" ('b' larga, caño largo)
para el resto, 'v'

Espero que os haya iluminado.
Gracias, vuelvas prontos.

miércoles, 6 de agosto de 2008

Referencias a caracteres en XML de vuelta a unicode

Los caracteres que no son son representables en algún encoding pueden representarse con su codificación en XML. Si c es un caracter, la expresión Python para generar esta representación es:
"&#%d;" % ord(c)

Cuando codificamos un texto, podemos especificar qué hacer con los caracteres que no se pueden representar en el encoding usado. Con el argumento 'ignore', simplemente no se incluyen, con 'replace' son reemplazados por un '?' y usando 'xmlcharrefreplace' obtenemos la mencionada codificación. Por ejemplo:
codificado = texto.encode(encoding, "xmlcharrefreplace")

Para recuperar la cadena original, no encontré nada haciendo una simple búsqueda, así que lo solucioné de la siguiente manera, usando una expresión regular y asumiendo que la cadena puede ser representada en el encoding por defecto (en mi caso UTF-8).

La expresión regular simplemente matchea con la forma de la representación XML, el objeto match es pasado una función que toma la parte que corresponde al número, la convierte a entero y devuelve el resultado de aplicarle unichr a ese valor. La función unichr devuelve el caracter unicode correspondiente a un número.
def unicodechar(match):
return(unichr(int(match.group(1))))

def replace_xmlrefs(string):
return re.sub("&#(\d+);",unicodechar,string)


Nada complejo, y se lo puede ver en funcionamiento:

>>> encoded = "財団 基金会"
>>> import re
>>> replace_xmlrefs(encoded)
u'\u8ca1\u56e3 \u57fa\u91d1\u4f1a'
>>> print replace_xmlrefs(encoded)
財団 基金会

La última línea, donde se imprimen los caracteres representados puede fallar si la consola no soporta esos caracteres, o no estamos usando un encoding que cubra todo unicode. Lo recomendable es usar UTF8.
Se puede iniciar el intérprete python especificando que use otro locale (vemos los disponibles con locale -a).
Por ejemplo:
LC_ALL=es_AR.utf8 python


Obviamente siempre hay gente que hace mejor las cosas, y acá encontré una forma de hacer esto que considera también los casos en que la referencia tiene un número hexadecimal.

Update 15-08-2008:
Este snippet también reemplaza las entidades HTML.