MarioDebian, mi devlog

Bitácora de un desarrollador newbie.

Tamaño o velocidad???

Estoy depurando algunas cosas de tcos y entre ellas el tamaño de la imagen.

Uno de los elementos «prescindibles» que me he encontrado es el binario seq (man seq), pertenece a coreutils.

Entre otras cosas si se le pasa un número como parámetro hace un bucle entre 1 y ese número, por ejemplo:
$ seq 5
1
2
3
4
5

hace muchas más cosas como poder empezar en algo distinto de 1 o configurar el incremento a otro valor distinto de 1. Tenemos entre manos un programa (en teoría) la mar de sencillo.

¿Donde esta el problema?

Hasta ahora cuando se activa el soporte tcosmonitor en la imagen copiaba /usr/bin/seq al initramfs, pero me he dado cuenta que ocupa la no despreciable cifra de 18 Kb, por lo que le he programado un sustituto en shell script:
#!/bin/sh


MAX=$1
NUM=1

if [ "$MAX" = "" ]; then
echo "Usage:"
echo " seq NUMBER"
exit 1
fi

if [ $MAX -gt 100 ] || [ $MAX -lt 1 ]; then
echo "ERROR: 100 is max number to seq and 1 min"
exit 1
fi

while [ $NUM -le $MAX ]; do
echo $NUM
NUM=$((NUM +1))
done
Es decir paso un número como parámetro y si es mayor de 100 (para evitar bucles demasiado largos) o menor de 1 (para evitar bucles infinitos) hago el bucle e imprimo la salida.

Diferencias de usar este script o el binario:

Tamaños:
  • binario 18Kb
  • script 0.2 Kb
Tiempos de ejecución para un contador de 100:

$ time busybox sh bin/seq 100 > /dev/null

real 0m0.343s
user 0m0.084s
sys 0m0.204s

Tengo que simular el entorno real de alguna manera y como el initramfs usa el shell ash de busybox esa es la forma en la que lo he lanzado...
$ time seq 100 > /dev/null

real 0m0.003s
user 0m0.004s
sys 0m0.000s
Como se puede apreciar la diferencia en tiempo es de 0.3 s, si nos vamos a 1 millón de iteraciones quizás sea de varios segundos.

¿Cuál elegir?

Como el uso que se está pensado hacer del comando seq es para pequeños bucles, me quedo con el script, aligeramos el tamaño del initramfs un poquito (17Kb) y aunque perdemos rendimiento no es algo realmente alarmante...

UPDATE:

Me había quedado con la duda, con 10.000 iteraciones mi script tarda 23.675s segundos (para un millon no he tenido paciencia de esperar), el binario tarda casi nada: 0.079s

UPDATE 2:

Ya que estamos he probado a hacerlo en otro lenguaje interpretado como es python y los resultamos me han sorprendido bastante:


$ time ./seq.py 100 > /dev/null

real 0m0.070s
user 0m0.020s
sys 0m0.004s

$ time ./seq.py 10000 > /dev/null

real 0m0.120s
user 0m0.052s
sys 0m0.016s

El código del programa en python puede ser algo como esto:
#!/usr/bin/python

import sys
max=sys.argv[1]

i=1
while 1:
print i
i=i+1
if i > int(max): break
Nunca hubiera imaginado que python (lenguaje interpretado) fuese tan rápido. La prueba de fuego ha sido comparar seq (C) y seq.py (python) en un bucle de 1.000.000:

C: 1.408s
python: 2.957s

Hacía mucho que no decía «python mola».

Articulos relacionados:

Comentarios

  1. mmoreno80 Y aún puede ser mas rápido!
    05/02/2007 | 01:42

    #!/usr/bin/python
    import sys
    for i in range(1, int(sys.argv[1]) + 1, 1):
    print i

  2. 05/02/2007 | 08:59

    y aún mejor:

    import sys
    if(len(sys.argv) != 2):
    sys.exit();
    a = int(sys.argv[1]) + 1;
    for i in xrange(1,a):
    print i

    de esa forma no calcula cada frame el "cast"

    la forma c00l:
    def p(x): print x;
    map(p,range(1,a));

  3. Gunnar Explicando la diferencia - ¡Python _no_ es un lenguaje interpretado!
    06/02/2007 | 03:31

    Eso explica buena parte de la diferencia que encuentras: Python es un lenguaje completamente compilado. Cuando programas en shell, cada línea es parseada y evaluada al vuelo - por lo que si tienes un ciclo y pasas 1000 veces por un ciclo (digamos, el while) o una condicional (el if), 1000 veces vas a parsearlos y a ejecutarlos. Además, en un script de shell es muy común el llamar tal cual a diversos binarios que puedes tener en cualquier lugar del sistema, lo cual implica además de todo eso un fork() y un exec().
    Python, al igual que Ruby y Perl (entiendo que PHP a veces sí y a veces no, pero no confíes en mi palabra - evito PHP hace mucho tiempo y por muy diversas razones ;-) ) son lenguajes estrictamente compilados - pero tienen compiladores altamente optimizados, de modo que compilar un programa de decenas de miles de líneas usualmente se realiza al inicializar, sin que te des mucha cuenta (claro, tiene su innegable peso). Python tiene un compilador sensiblemente más lento que el de Perl, pero suple esta carencia haciendo una compilación a bytecode (claro, bytecode propio de Python, nada de Java ni cosas por el estilo (a menos que estés usando Jython, pero eso es una perversión mayor a la que estoy dispuesto a tolerar ;-) ) ) siempre que puede - por eso encontrarás archivos .pyc regados: Son archivos Python pre-parseados, hasta cierto punto compilados, de modo que el runtime de Python sólo verifica si no tiene que procesar nuevamente el fuente, y se va directo sobre de este bytecode.

  4. mariodebian Re: gracias
    07/02/2007 | 10:13

    Gracias a todos y en especial a Gunnar por esta clase magistral.

    Hay cierto afán en la comunidad python/perl/ruby en hacer las cosas en la menor cantidad de código posible y cuesta acostumbrarse...

    Bueno lo dicho, tamaño y velocidad no son antónimos en ciertos lenguajes.

Comentarios cerrados