lunes, junio 13, 2011

Un Clúster con SLURM, uso y configuración

He tenido la oportunidad de poner en marcha un clúster (pequeñito) y aprovecho que he hecho una presentación para sus usuarios para colgarla aquí por si resulta de ayuda a alguien o por si alguien la encuentra interesante.

viernes, mayo 13, 2011

Lo que todo programador de C debería saber sobre el "comportamiento indefinido"

Este ha sido uno de los artículos damnificados por la caída de blogger.com y por eso no lo he puesto aquí antes, pero me gustaría reseñar este What Every C Programmer Should Know About Undefined Behavior 1/3 del blog de LLVM. No solo hace una estupenda introducción al qué y al por qué del comportamiento indefinido en C sino que apunta a una serie de artículos que merece la pena mencionar también: A Guide to Undefined Behavior in C and C++, Part 1, Part 2 y Part 3.

Del primero de esa serie, para que quede claro de qué va el asunto :)
Of course it is physically possible to pick up a basketball and run with it. It is also possible you will get away with it during a game. However, it is against the rules; good players won’t do it and bad players won’t get away with it for long.
Como se puede leer es uno de esos temas que conviene saber (y muy bien) si se pretende hacer código portable. Así que, para leer con detenimiento.

Soy un atento seguidor del blog de LLVM, pero esta vez lo he visto en el twitter de TooManySecrets

viernes, abril 29, 2011

¿Testeo o especificaciones?

¿Cómo se determina el comportamiento de un sistema? ¿Testeo o leyes, estándares, especificaciones...? Como siempre, es mi postura, las dos. El ejemplo surge de una incialización incompleta de un array de caracteres ¿Cómo se debería comportar? Mi teoría en principio, más o menos errónea, era que debía ocurrir lo mismo que con una copia de cadenas en C. Se copia hasta el cero terminador y ahí acaba el proceso. El resto de elementos del array indefinidos, sin inicializar. Pero estaba la duda... ¿Cómo se comporta y/o se debe comportar un sistema?

Para responder a las dos preguntas ni los test son suficiente ni lo que diga el estándar lo es. Los dos juntos pueden calmar más :)

En el punto 8.5.2.3 del estándar C++0x se habla de lo que ocurre si se proporcionan menos caracteres a la inicialización:

If there are fewer initializers than there are array elements, each element not explicitly initialized shall be
zero-initialized


Sin embargo este punto está ausente en el C++98. Los test aparentemente dicen que se suelen rellenar de ceros ¿Por qué? y ¿Podemos confiar en que siempre sea así?

Asumiendo (que quizás sea mucho asumir) que la incialización de cadenas es análoga a la incialalización en forma de lista (con llaves) esto puede tener una expliación. Según se puede leer en el punto 8.5.1.7 del C++98:
If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized (8.5)
[Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form
int(), that is, 0. ]



En C++0x pensaba que quizás cambia algo pero el punto 8.5.1.7 para decir básicamente lo mismo:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list (8.5.4)

Las diferencias vienen de los mejoradas listas de inicialización de C++0x.

Leyendo todo esto yo tengo claro que no me fiaría de que el resto de los caracteres estuviesen a 0, aunque funcione así probablemente en casi todas las implementaciones... O sea, que no conviene fiarse de lo que hay después del cero terminador. Además ¿Para qué? Es tentar a la suerte...

Lecciones a aprender, al menos tal como yo lo veo:
  • Intentar no buscar los casos difíciles, en los que hay que consultar la ley. En ellos es probable que no todas las implementaciones la hayan leído correctamente
  • Conocer los estándares y especificaciones que usamos o al menos saber buscar en ellas en caso de duda
  • Testear. cuantas más pruebas se hagan al código más fácil es encontrar los "casos patológicos"

jueves, diciembre 02, 2010

Lo mínimo imprescindible para controlar código fuente con subversion

He realizado una presentación para ayudar a un pequeño cursito sobre subversion que voy a dar en breve. Así que publico aquí las transparencias que quizás pueden ser de utilidad para alguien. Está fuertemente inspirado en el manual Version Control with Subversion y concretamente en el apartado del ciclo básico de trabajo con svn con algunos añadidos de gráficos, buenas prácticas, programas auxiliares y algunos enlaces básicos. Por cierto, gracias a Juanjo que le ha echado un vistazo y ha detectado alguna carencia.


El título de la presentación es "Sistemas de control de versiones. Introducción a subversion" aunque me gustaría más haberlo titulado como este post, "Lo mínimo imprescindible para controlar código fuente con subversion" o como posibles títulos alternativos, dado el público al que va dirigido "Subversion para programadores ocasionales" o "Control de código fuente para científicos". A ver si entre todos podemos hacer que los científicos programen mejor. En beneficio de todos :)

jueves, noviembre 11, 2010

Recuperar ficheros de texto de sistema de ficheros corrupto

Hace poco he tenido que vivir una de esas historias de terror que uno teme encontrar. Pues llegó. Un sistema de ficheros irrecuperable (RAID lineal con un par de discos que no responden) e información valiosa de la que no se hacía copia de seguridad. El hecho es que la información valiosa son ficheros de texto, código fuente que se hizo de modo improvisado y fuera de repositorios ni backups y que luego se echa de menos. La solución, está claro, sería hacer backup de lo valioso, pero eso ya se empieza a ver claro por los interesados :)

Bueno, al grano ¿Cómo recuperar esos archivos de código fuente? Aquí voy a dar una solución "sencilla", pero que tiene varias precondiciones:
- Que la controladora del disco responda y que se pueda leer del dispositivo
- Que el texto a encontrar tenga menos del tamaño de un bloque del sistema de ficheros. De otro modo también vale, pero tendremos que buscar trozos y ensamblarlos.
- Que dispongamos de un sistema de ficheros donde quepa el backup. (Se podría hacer este proceso por partes o "al vuelo", jugando con el skip de dd, pero se va de la recetilla que he aplicado)

Entonces lo primero de todo (aplicable a cualquier situación donde un disco empiece a hacer cosas raras) es volcar el contenido del disco

#dd if=/dev/sd[ndev] of=/[outfile] bs=[sizeblock] conv=sync,noerror

Donde ndev es el dispositivo en mal estado. En mi caso es en buen estado pero que formaba parte de un todo en mal estado. De todos modos la opción sync, noerror trata de lidiar con errores en disco y rellenar con ceros las partes no leídas. Para este método rellenar con ceros no sería necesario porque no vamos a tratar de reparar el sistema sino hacer un método más basto, suficiente para nuestros propósitos y sin modificar el contenido del disco. Primero usamos la "magia" de strings. En principio es un comando para encontrar información relevante en código objeto, pero va muy bien para encontrar texto en binario (que es su labor básicamente). El comando que he usado es:

$ strings -n20 -a -t d outfile > outfile.str

Dónde la magia está en que consideramos como "cadena mínima". En este caso, con el n se le indica que una cadena consta de al menos veinte caracteres imprimibles. Si alguien va a usar este método puede jugar con él, sabiendo el tamaño de linea a buscar.
Posteriormente se pueden hacer greps o buscar con less sobre outfile.str con la buena noticia de que strings nos ha puesto en la primera columna (gracias al parámetro -t d) en que posición está la cadena. Ahora podemos recuperar el fichero con el mágico dd:

$ dd if=sdb1 bs=1 skip=[pos-offset] count=filesize 2>/dev/null > file.c

Evidentemente tenemos que jugar con offset (la posición en la que se encontraba la cadena encontrada en el fichero) y filesize (el tamaño del fichero). Manual y un poco a pedales, pero si los datos que hemos perdido lo merecen...

Debo notar que es una técnica no destructiva y que es complementaria a la posibilidad de reparar el sistema de ficheros. Espero que le pueda servir a alguien y también se admiten sugerencias de administradores avezados acerca de cómo reparar RAIDs lineales por soft y/o recuperar información binaria de ellos.

miércoles, octubre 27, 2010

Electronic Arts publica EASTL como software libre

Ya lo he publicado en barrapunto, pero por a alguien más le interesa y como me ha costado un poquillo hacer la entrada con sus enlaces pues la aprovecho para aquí :)

El tema es que un pobrecito hablador ha envíado un enlace con la noticia de que Electronic Arts (EA) ha publicado EASTL como software libre. La EASTL es la versión de EA de la STL, la biblioteca estándar de plantillas de C++. Fue desarrollada por EA motivados por las exigencias extremas del uso de la memoria y CPU en el ámbito de los juegos. Ha sido publicada en la web de software libre de EA en formato zip. Paul Hodge ha creado posteriormente un repositorio en github con el mismo código. En la sección de desarrollo de juegos de reddit han publicado algunos enlaces y comentarios que me han parecido interesantes y los pego a continuación.

Se puede leer allí la propia noticia, las experiencias de un desarrollador tratando de integrarla en un pequeño proyecto existente y también se apunta a una comparativa de rendimiento con otras bibliotecas como la STL estándar o como RDE STL hecha por el autor de esta última.

Actualización y bola extra: Me he encontrado con este artículo que también puede interesar a los lectores de este blog y de la noticia anterior: Start Pre-allocating And Stop Worrying. Los que vistan el blog físicamente (si es que existen) ya saben que estas cosas las suelo poner en mis compartidos de google.

lunes, junio 28, 2010

Using the magic behind TFormula

I wanted to evaluate conditions at runtime and I'm a happy user of ROOT. So I was thinking to use the "magic" behind TFormula. But TFormula has some limitations: the name of the variables are "x, y z, t" and I need to use arbitrary names...

Doing some research I found RooFormulaVar, a class that is part of RooFit and it does the job :) I think that RooFormulaVar lacks proper documentation, so I'm going to describe briefly how to use it to evaluate mathematical and logic expressions at runtime. Using it is easy, if you know how :)

A simple example:


RooRealVar a("a","a",3.);
RooRealVar b("b","b",7.);
RooFormulaVar plus(“aplusb”,”a+b”,RooArgSet(a,b));
//10
cout << plus.getVal() << endl;


Evaluating bitwise operations and passing a set of arguments in a more dynamic way:

TClonesArray tca("RooRealVar",2)
RooRealVar *var1 = new (tca[1]) RooRealVar("var1","var2",0);
RooRealVar *var2 = new (tca[0]) RooRealVar("var2","var2",0);

RooFormulaVar opor("var1orvar2","var1&var2",RooArgSet(tca));

var1->setVal(2.);
var2->setVal(7.);
//2
cout << opor.getVal() << endl;

var1->setVal(5.);
var2->setVal(1.);
//1
cout << opor.getVal() << endl;

var1->setVal(8.);
var2->setVal(1.);
//0
cout << opor.getVal() << endl;

Finally, evaluating logical expressions:

TClonesArray tca("RooRealVar",2)
RooRealVar *var1 = new (tca[1]) RooRealVar("var1","var2",0);
RooRealVar *var2 = new (tca[0]) RooRealVar("var2","var2",0);

RooFormulaVar cond("var1littvar2big","var1<1 || var2>10",RooArgSet(tca));


var1->setVal(0.);
var2->setVal(17.);

//true
cout << cond.getVal() << endl;

var1->setVal(3.);
var2->setVal(11.);

//true
cout << cond.getVal() << endl;

var1->setVal(8.);
var2->setVal(1.);
//false
cout << cond.getVal() << endl;

I hope that this blog entry will be useful and someone will save time and code. Cheers and happy coding! :)

Cómo evaluar expresiones lógico-matemáticas en tiempo de ejecución (y usar la magia de TFormula)

C++ es un lenguaje poco dinámico. En otros lenguajes se dispone del propio lenguaje en tiempo de ejecución y, usando Eval o análogos, se puede evaluar cualquier expresión válida del lenguaje. Esto que puede ser una ventaja y puede ahorrar líneas de código puede ser también peligroso si se usa con poca cabeza. Como casi todo :)

Bueno, de todos modos lo que yo necesitaba no era eval como tal, sino evaluación de condiciones en tiempo de ejecución: poder decirle al programa qué condición debe evaluar en función de una serie de variables que dependen también de una configuración. Todo demasiado dinámico para que resultase sencillo de programar en C++. Había por lo menos tres alternativas, aparte de una cuarta que es la que he elegido :)

  • Programar una especie de ejecutor de reglas. Para el que tiene un martillo todo son clavos y si sólo te centras en el código la solución pasa por ahí. Mi intuición me dice que hiciese lo que hiciese me iba a quedar corto o introduciría bugs gordos potenciales. Mejor aprovechar algo existente...
  • Dejar la parte dinámica para un lenguaje dinámico. Hacer librerías que puedan ser llamadas desde dicho lenguaje. La descarté por varias razones, entre ellas el esfuerzo "librerizar" el código y la posible pérdida de rendimiento: la condición ha de evaluarse en le bucle principal y el rendimiento es un requisito clave.
  • Embeber un lenguaje dinámico dentro de programa. Introduciría dependencias nuevas. Sonaba seductor :), pero seguramente demasiado arriesgado

Prácticamente todas las soluciones son matar moscas a cañonazos y la que he elegido creo que es la más elegante de todas: usar una librería que ya estaba usando, cuya funcionalidad no conocía del todo (y que no está especialmente documentada...)

Así que esta entrada servirá además para mostrar una funcionalidad interesante de ROOT que podría ser usada por separado. Más o menos. El hecho es que ROOT, una librería de gestión y análisis de datos (en grandes cantidades) ya posee un evaluador de formulas que se llama TFormula. Es capaz de evaluar tanto expresiones matemáticas como lógicas o a nivel de bit. Cubre sobradamente mis exigencias. Al informarme más sobre su uso, vi que tenía inconvenientes: el nombre de las variables es fijo (x, y, z ,t) y no creía conveniente configurar las formulas basadas en esos nombres de variables o reemplazar a mano el nombre de las variables por las "por defecto". Entonces, buscando un poquito más encontré que en un sublibrería de ROOT, RooFit, existía una no muy documentada RooFormulaVar, que hace básicamente lo que yo quería, evaluar condiciones (y de paso formulas matemáticas) en base a una cadena de definición. El uso es relativamente sencillo una vez que se descubre cómo, porque no hay una documentación directa de cómo hacerlo. Un uso fácil sería:

RooRealVar a("a","a",3.);
RooRealVar b("b","b",7.);
RooFormulaVar plus(“aplusb”,”a+b”,RooArgSet(a,b));
//10
cout << plus.getVal() << endl;


Si se quiere usar con un número variable de argumentos, se puede usar otro constructor y da más juego, es un poquito más dinámico. Este ejemplo además usa operaciones a nivel de bit:

TClonesArray tca("RooRealVar",2)
RooRealVar *var1 = new (tca[1]) RooRealVar("var1","var2",0);
RooRealVar *var2 = new (tca[0]) RooRealVar("var2","var2",0);

RooFormulaVar opor("var1orvar2","var1&var2",RooArgSet(tca));

var1->setVal(2.);
var2->setVal(7.);
//2
cout << opor.getVal() << endl;

var1->setVal(5.);
var2->setVal(1.);
//1
cout << opor.getVal() << endl;

var1->setVal(8.);
var2->setVal(1.);
//0
cout << opor.getVal() << endl;

Por último y más importante para mí, evaluar condiciones lógicas, por ejemplo:

TClonesArray tca("RooRealVar",2)
RooRealVar *var1 = new (tca[1]) RooRealVar("var1","var2",0);
RooRealVar *var2 = new (tca[0]) RooRealVar("var2","var2",0);

RooFormulaVar cond("var1littvar2big","var1<1 || var2>10",RooArgSet(tca));


var1->setVal(0.);
var2->setVal(17.);

//true
cout << cond.getVal() << endl;

var1->setVal(3.);
var2->setVal(11.);

//true
cout << cond.getVal() << endl;

var1->setVal(8.);
var2->setVal(1.);
//false
cout << cond.getVal() << endl;

Espero que sirva a alguien y que esa persona se pueda ahorrar tiempo y código :)

martes, mayo 18, 2010

Diseño del software de adquisición de datos para ANAIS

Creo que puede ser de interés para la audiencia potencial de este blog las transparencias que he preparado para presentar mi trabajo de estos últimos tres meses. Se trata del diseño del software de adquisición de datos para el experimento ANAIS:
No tiene que ver con la temática usual del blog pero quizás pueda ser interesante contextualizar el experimento. Se trata de replicar el experimento DAMA que dio un resultado positivo en la búsqueda de candidatos a la materia oscura, pero cuyos resultados son problemáticos en su interpretación, dado que detectores distintos que usan otro tipo de detección no han sido capaces de medir lo mismo. De ahí que el experimento deba repetirse independientemente, para corroborar que los resultados son similares. Para más información sobre la materia oscura se puede leer la wikipedia y para profundizar más en lo que se hace en el el laboratorio de Canfranc: "Dos décadas de búsqueda de materia oscura en el Laboratorio Subterráneo de Canfranc" de María Luisa Sarsa.

jueves, abril 15, 2010

Publicado GCC 4.5

GCC 4.5 acaba de ser publicado. En la lista de cambios se pueden leer las novedades de esta versión. De ellas las más visibles para los usuarios pueden ser las mejoras en el mostrado de errores como indicar la columna por defecto, evitar mostrar más errores en caso de falta de un fichero de inclusión o no mostrar los parámetros por defecto de las plantillas, con la ganancia de legibilidad que eso supondrá. Además viene con mejoras en las optimizaciones, en los lenguajes y plataformas soportadas y con un gran avance en el soporte experimental de C++0x además de la inclusión en la rama principal del sistema de plugins.

Al hilo del sistema de plugins ha surgido una acalorada discusión a raíz de una propuesta de incluir en un futuro por defecto el plugin DragonEgg en la distribución de GCC. DragonEgg en un plugin que reeemplaza las optimizaciones y la generación de código de GCC por las de LLVM.

La misma entrada y previsiblemente más comentarios en Publicado GCC 4.5 en barrapunto. Desde que estoy de editor por allí, por aquí se mueve menos la cosa. Espero que sepan perdonarme, pero no me olvido de este otro lugar :)