December 2010 - Posts
Estuve escribiendo una librería de clases C# para manejar grupos finitos. Un grupo es un conjunto G de elementos dotados de una operación binaria *, dondee:
a * b is in G (clausura)
a * (b * c) = (a * b) * c (asociatividad)
a * a’ = e (existencia de inverso e identidad)
a * e = e * a (identidad)
El código de la solución está en AjCodeKata Google Project, en trunk/AjGroups:
Las principales interfaces son:
IElement tiene un método Multiply, que recibe otro IElement y retorna el resultado IElement. IElement.Order es el número de elementos a ser multiplicados para obtener la identidad:
a * a * a ….. * a = e
IGroup.Elements es la colección de IElements que son elementos del grupo. IGroup.Order es la cantidad de elementos.
Hay dos implementaciones de esas interfaces: una basada en permutaciones, otra basada en elementos con nombre (como “a”, “e”) y una tabla describiendo sus multiplicaciones.
Hay métodos para:
- Obtener los subgrupos de un grupo
- Obtener los subgrupos normales de un grupo
- Determinar si dos grupos son isomorfos
Seguí Test-Driven Development (TDD). Todos los tests en verde:
Buen code coverage:
Debería hacer algún refactoring, pero el proyecto va tomando forma. Escribiré posts describiendo la implementación de permutaciones, y la otra, la basada en elementos abstractos.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
En mi anterior post, implementé un simple worker role, consumiendo y produciendo números desde y en una cola. Ahora, una nueva aplicación:
El worker role implementa la generación de una secuencia Collatz. Para conocer sobre esta secuencia, problema:
http://mathworld.wolfram.com/CollatzProblem.html
http://en.wikipedia.org/wiki/Collatz_conjecture
http://www.ericr.nl/wondrous/
Pueden bajar la solución desde AjCodeKatas Google project. El código está en la rama:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/Azure/AzureCollatz
La página inicial es simple, sin validaciones:
El rango de números es enviada a la cola:
protected void btnProcess_Click(object sender, EventArgs e)
{
int from = Convert.ToInt32(txtFromNumber.Text);
int to = Convert.ToInt32(txtToNumber.Text);
for (int k=from; k<=to; k++)
{
CloudQueueMessage msg = new CloudQueueMessage(k.ToString());
WebRole.Instance.NumbersQueue.AddMessage(msg);
}
}
El worker role toma cada mensaje, y calcula la secuencia Collatz del número recibido en cada mensaje:
Agregué una clase en Azure.Library: un MessageProcessor que consumen mensajes de una cola, en su propio thread:
public MessageProcessor(CloudQueue queue, Func<CloudQueueMessage, bool> process)
{
this.queue = queue;
this.process = process;
}
public void Start()
{
Thread thread = new Thread(new ThreadStart(this.Run));
thread.Start();
}
public void Run()
{
while (true)
{
try
{
CloudQueueMessage msg = this.queue.GetMessage();
if (this.ProcessMessage(msg))
this.queue.DeleteMessage(msg);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message, "Error");
}
}
}
public virtual bool ProcessMessage(CloudQueueMessage msg)
{
if (msg != null && this.process != null)
return this.process(msg);
Trace.WriteLine("Working", "Information");
Thread.Sleep(10000);
return false;
}
El worker role, en vez de atender un mensaje a la vez, lanza un número fijo (12) de MessageProcessor. De esta manera, cada instancia de Azure que tengamos disponible se dedica a procesar varios mensajes, en paralelo. No sería necesario para este simple ejemplo. Pero es una “prueba de concepto” para ir viendo de usar esta idea de múltiples threads. Parte del método Run en el worker role:
QueueUtilities qutil = new QueueUtilities(account);
CloudQueue queue = qutil.CreateQueueIfNotExists("numbers");
CloudQueueClient qclient = account.CreateCloudQueueClient();
for (int k=0; k<11; k++)
{
CloudQueue q = qclient.GetQueueReference("numbers");
MessageProcessor p = new MessageProcessor(q, this.ProcessMessage);
p.Start();
}
MessageProcessor processor = new MessageProcessor(queue, this.ProcessMessage);
processor.Run();
El método ProcessMessage se encarga del trabajo real:
private bool ProcessMessage(CloudQueueMessage msg)
{
int number = Convert.ToInt32(msg.AsString);
List<int> numbers = new List<int>() { number };
while (number > 1)
{
if ((number % 2) == 0)
{
number = number / 2;
numbers.Add(number);
}
else
{
number = number * 3 + 1;
numbers.Add(number);
}
}
StringBuilder builder = new StringBuilder();
builder.Append("Result:");
foreach (int n in numbers)
{
builder.Append(" ");
builder.Append(n);
}
Trace.WriteLine(builder.ToString(), "Information");
return true;
}
Próximos pasos: más aplicaciones distribuidas (algoritmo genético, web crawler, etc…). También podría mejorar el ejemplo para conseguir ver los resultados en una página web, o lanzar y consultar resultados de forma remota, desde un WinForm, por ejemplo.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Despues de un tiempo sin postear sobre el tema, vuelvo a programar en Azure. Veamos un ejemplo simple: una aplicación con un web role, un worker role:
Pueden bajar el código que está contenido enmi AjCodeKatas Google project. El código está en:
http://code.google.com/p/ajcodekatas/source/browse/#svn/trunk/Azure/AzureNumbers
En la página inicial, podemos ingresar un número a procesar:
Si enviamos el número 10, este datos es enviado a una cola:
protected void btnProcess_Click(object sender, EventArgs e)
{
int number = Convert.ToInt32(txtNumber.Text);
CloudQueueMessage msg = new CloudQueueMessage(number.ToString());
WebRole.NumbersQueue.AddMessage(msg);
}
El worker role es el encargado de leer esa cola. Decrementa el número, y si el resultado es mayor que cero, lo reinyecta en la cola:
public override void Run()
{
// This is a sample worker implementation. Replace with your logic.
Trace.WriteLine("NumberWorkerRole entry point called", "Information");
CloudStorageAccount account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
QueueUtilities qutil = new QueueUtilities(account);
CloudQueue queue = qutil.CreateQueueIfNotExists("numbers");
while (true)
{
CloudQueueMessage msg = queue.GetMessage();
if (msg != null)
{
int number = Convert.ToInt32(msg.AsString);
Trace.WriteLine(string.Format("Processing number: {0}", number), "Information");
number--;
if (number > 0)
{
CloudQueueMessage newmsg = new CloudQueueMessage(number.ToString());
queue.AddMessage(newmsg);
}
queue.DeleteMessage(msg);
}
else
{
Thread.Sleep(10000);
Trace.WriteLine("Working", "Information");
}
}
}
Pueden ver la salidad desde el Development Fabric UI:
Estoy usando un código que estoy armaando, AzureLibrary, para crear una cola:
public CloudQueue CreateQueueIfNotExists(string queuename)
{
CloudQueueClient queueStorage = this.account.CreateCloudQueueClient();
CloudQueue queue = queueStorage.GetQueueReference(queuename);
Trace.WriteLine("Creating queue...", "Information");
Boolean queuecreated = false;
while (queuecreated == false)
{
try
{
queue.CreateIfNotExist();
queuecreated = true;
}
catch (StorageClientException e)
{
if (e.ErrorCode == StorageErrorCode.TransportError)
{
Trace.TraceError(string.Format("Connect failure! The most likely reason is that the local " +
"Development Storage tool is not running or your storage account configuration is incorrect. " +
"Message: '{0}'", e.Message));
System.Threading.Thread.Sleep(5000);
}
else
{
throw;
}
}
}
return queue;
}
Tome esa forma de crear una cola de código que venía en los ejemplos de Azure SDK.
Próximos pasos para explorar:
- Agregar instrumentación al worker role
- Usar más instancias, e ir generando más mensajes en el proceso
- Agregar soporte de múltiples threads dentro del worker role
- Algún ejemplo usando Azure Storage (table, blob)
Y algunos pasos más grandes:
- Inyectar y ejecutar código AjSharp (o AjTalk) en los worker roles
- Implementar una aplicación distribuida (algoritmo genético distribuido, cálculo de fractar o ray tracer, web crawler, simulación con Montecarlo, etc…)
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez