lunes, marzo 31, 2008

Más ligero que un bloqueo

Seamos sinceros: si usted necesita un bloqueo, no va a conseguir un recurso menos costoso que el propio bloqueo. Ahora bien, es muy diferente si lo que usted necesita no es exactamente un bloqueo, sino parte de la funcionalidad del bloqueo. En tal caso, hay alternativas.
Hace poco me encontré en esta situación, añadiendo concurrencia a XSight RT. En las últimas versiones (no publicadas todavía) dos hilos pueden ocuparse de una misma escena, generando bandas disjuntas sobre un mismo objeto PixelMap (los mapas de bits internos del API de XSight RT). De este modo, los dos hilos pueden trabajar sin molestarse mutuamente. El conflicto se produce cuando hay que ejecutar un evento para avisar al cliente del API de que hay una banda nueva que se puede mostrar incrementalmente en la ventana de salida. En el código original, sin soporte de hilos, ésta era una tarea del sampler. En el nuevo código, por inercia, este código se duplicó en cada hilo: cada uno intenta notificar al cliente, cuando se ha acumulado trabajo o ha pasado suficiente tiempo.
¿Primera solución? Proteger ese código mediante un bloqueo, por supuesto:
if (SeDanLasCondiciones())
lock
{
Avisar();
}
Esto es matar gorriones a cañonazos. Un bloqueo, implementado aquí mediante la instrucción lock, que el compilador traduce en llamadas a la clase Monitor, se encarga en realidad de dos tareas que se pueden separar:
  1. Comprobar que el código protegido no pueda ejecutarse simultáneamente por más de un hilo.
  2. Si un hilo solicita el bloqueo mientras está concedido a otro hilo, el hilo se pone en modo de espera.
Pasar al "modo de espera" puede ser costoso, de acuerdo a la técnica con la que se haya implementado el bloqueo. En el peor de los casos, si se suspende el hilo mediante llamadas al API de Windows, esto provoca un pequeño terremoto en el planificador de tareas: tenga presente que se produce una transición al modo kernel del sistema operativo.
Para su tranquilidad, es muy poco probable que Monitor, y por consiguiente la instrucción lock, implemente la espera de esta manera. Lo más probable es que intente esperar un instante antes de tomar medidas drásticas y tomarse el somnífero.
No obstante, en mi ejemplo, ni siquiera hace falta esperar. ¿Que un hilo intenta notificar y encuentra que alguien se le ha adelantado? ¡Estupendo! ¡Ya no necesita notificar sobre nada! La única parte que necesitamos del bloqueo es la funcionalidad de exclusión mutua... y para esta tarea sencilla, existe un mejor mecanismo. Observe:
private int access;


public bool ReportProgress()
{
int elapsed = Environment.TickCount;
if (elapsed - lastReport > MIN_INTERVAL)
{
if (0 == Interlocked.Exchange(ref access, 1))
{
lastReport = elapsed;
listener.Progress(progressInfo);
Interlocked.Exchange(ref access, 0);
}
return !progressInfo.CancellationPending;
}
else
return true
;
}
Este código está ubicado en la clase Scene, y los dos hilos comparten una instancia de la misma. Por lo tanto, ambos hilos están usando el mismo campo access, declarado dentro de Scene. Este campo se utiliza en la zona resaltada del método, a través de métodos estáticos de la clase Interlocked.
Primero se ejecuta el método Exchange. Este método intercambia el valor almacenado en la variable con el valor que pasamos como parámetro, y nos devuelve el antiguo valor de la variable. Además, nos ofrece la garantía de que todo este meneo lo hará de forma atómica: no se producirán intromisiones durante su ejecución. Nosotros asumiremos que el valor "normal" del campo access es cero. ¿Qué pasa si intentamos intercambiar el cero por un uno, y descubrimos que ya tiene un uno? Simplemente, que alguien se nos ha adelantado. En tal caso, renunciamos a notificar, pues ya alguien se ha ocupado del asunto. De lo contrario, si Exchange devuelve cero, tendremos la seguridad de que nadie más ejecutará la notificación... al menos, mientras no reasignemos un cero en access. Esto es precisamente lo que hacemos al terminar con la notificación, dejando la vía libre para la próxima notificación.

Etiquetas: , , ,

martes, marzo 04, 2008

Superesferas

Superesferas (XSight RT)
El dado de la imagen está modelado mediante una "superesfera" que se puede describir mediante la ecuación x^4 + y^4 + z^4 = 1. Al tratarse de una ecuación de cuarto grado, se puede resolver algebraicamente, algo que XSight RT hace muy eficientemente. El resultado es la figura del dado: un objeto a mitad de camino entre una esfera (de las de toda la vida) y un cubo. Si en vez de elevar a la cuarta potencia, usásemos exponentes mayores, la figura iría aproximándose más al cubo... pero la ecuación dejaría de tener una solución algebraica, y tendría que resolverse mediante el método de Newton.
Las texturas del tapete y del propio dado están modeladas, naturalmente, mediante el clásico y eficiente ruido de Perlin.

Etiquetas: , ,

sábado, marzo 01, 2008

Grietas

Crackle
Observe el cambio en el "suelo", respecto a la imagen del post anterior: he añadido un patrón que en inglés se conoce como crackle. Lo interesante es que está basado en los diagramas de Voronoi, que se definen como el conjunto de líneas equidistantes respecto a un conjunto de puntos aleatorios en el plano. Si a un robot lo situas en una habitación con objetos calientes, para evitar acercarse demasiado a ellos, tendría que calcular el diagrama de Voronoi asociado, para moverse a lo largo de las líneas del mismo.
En mi imagen, se ha utilizado el crackle para el "pigmento" del plano, pero podría utilizarse también para trucar los vectores normales, y simular que el plano está ligeramente arrugado. Esta técnica, aplicada a una superficie reflectante, puede usarse para simular la superficie del mar.
Para una versión del I King
J.L. Borges

El porvenir es tan irrevocable
Como el rígido ayer. No hay una cosa
Que no sea una letra silenciosa
De la eterna escritura indescifrable
Cuyo libro es el tiempo. Quien se aleja
De su casa ya ha vuelto. Nuestra vida
Es la senda futura y recorrida.
El rigor ha tejido la madeja.
No te arredres. La ergástula es oscura,
La firme trama es de incesante hierro,
Pero en algún recodo de tu encierro
Puede haber una luz, una hendidura.
El camino es fatal como la flecha.
Pero en las grietas está Dios, que acecha.
El patrón crackle, imitando la difracción de la luz entre las hojas de un árbol

Etiquetas: ,