miércoles, julio 08, 2009

Tipos anónimos, Expresiones Linq y APIs fluidas

Hace ya una semana a través de twitter me llegó me llego un post (Using C# 3.0 Anonymous Types as Dictionaries) que presentaba una propuesta para usar tipos anónimos como diccionarios. Para seguir les recomiendo ver el post pues asumo que se conoce el contexto del problema

El post genero muchas reacciones y comentarios, que pueden verse en el mismo post, personalmente me gustó mucho como se ve el código y me hizo recuerdo a las notaciones JSON, o bien a como se harían estas cosas en lenguajes dinámicos, pero como dije en twitter pero siempre está la duda ¿rendimiento?

Luego en la semana me puse a pensar de nuevo en esto y se me ocurrió que este caso en particular .net 3.5 tenía otra caracteristica que podía ser de ayuda Expresiones Linq (para referencia Árboles de expresiones). Las expresiones no solo nos permiten navegar en declaraciones para distintos fines sino que además nos permiten crear y compilar funciones Lambda. Esto me parecía interesante porque dado un tipo cualquiera esposible crear una función Lambda que la procesara y generará la cadena de atributos completa.

Sin embargo, por ahora lambda expressiones siempre terminan en una sola expresión y no es posible construir todo un método de esta manera. Y es aqui donde las APIs fluidas (también conocidas como interfaces fluidas, Fluent API, Fluent Interfaces) entran en juego, Las APIs Fluidas brindan, a través de patrones como encadenamiento de métodos, la posibilidad de escribir código de manera consecuente y asegurando que el contexto en la ejecución se mantenga. Por ejemplo todos los métodos Append de la clase StringBuilder, retornan el objeto StringBuilder lo que permite escribir código de este tipo:

StringBuilder sb= new StringBuilder;
sb.Append("Hola").Append(" ").Append("mundo");

Dado que en este caso el principal objetivo es generar una cadena con un StringBuilder, la interfaz Fluida es de mucha utilidad pues se puede generar una sola expresión que agregue todos los atributos al elemento. Esto podía tener un impacto positivo en el rendimiento (mi preocupación del momento).


Así que decidí hacer un ejercicio. Tomé el código de Eilon e hice dos nuevas implementaciones de GetHtmlLink.


GetHtmlLinkDictionary: Que recibe un diccionario y que utiliza inicializadores para el paso de parámeros


GetHtmlLinkLambda: Esta es la que implementa la idea que tenía en la cabeza. Esta función recibe una clase cualquier y para cada propiedad pública de la clase genera un atributo en el vínculo html que genera.


Para ello primero es necesario obtener la información de las propiedades de la clase, luego generar la expresión que agrega los atributos y finalmente compilarla. Como la idea de esta prueba es que la expresión compilada sea utilizada en distintas llamadas cree un diccionario simple para que actue como repositorio de las funciones precompiladas. Con esto en mente el flujo básico de la función es el siguiente:


image


Y la implementación base es la siguiente.


static IDictionary<Type, Action<StringBuilder, object>> functionCache = new Dictionary<Type, Action<StringBuilder, object>>();
public static string GetHtmlLinkLamba(string text, object properties)
{
StringBuilder sb = new StringBuilder();
sb.Append("<a");
if (!functionCache.ContainsKey(properties.GetType()))
{
functionCache.Add(properties.GetType(), BuildFunction(properties))
}
functionCache[properties.GetType()](sb, properties);
sb.Append(">");
sb.Append(text);
sb.Append("</a>");
return sb.ToString();
}


Como notarán el delegado que se genera recibe dos parámetros la clase StringBuilder y el objeto que se utilizará. Un aspecto que no pude optimizar es el paso del objeto de manera no tipada esto debido a que para poder invocar las funciones precompiladas es necesario conocer la firma del delegado con anticipación y eso incluye el tipo del parámetro (este es un tema pendiente a resolver).


Obviamente la parte jugosa está en la función BuildFunction. Lo primero, en mi caso, fue ordenar las ideas de la construcción de la expresión. Mi primer enfoque fue construir una expresión que hiciera lo mismo que la función que carga la información del diccionario y dado que el número de propiedades es variable necesito que mi expresión vaya agrengadose. Es decir que si hay solo la propiedad class la expresión generada sea la siguiente:


sb.Append(" ").Append(“class”).Append("=\"").Append(obj.class).Append("\"")

Y si hay una propiedad adicional target la expresión pase a ser:


sb.Append(" ").Append(“class”).Append("=\"").Append(obj.class).Append("\" ").Append(“target”).Append("=\"").Append(obj.target).Append("\"")

Esta alternativa la llegué a implementar, pero luego me di cuenta, lento que fuí, que al conocer toda la infomración de la clase de antemano podía generar una expresión mucho más óptima.


sb.Append(" class =\"").Append(obj.class).Append("\"  target=\"").Append(obj.target).Append("\"")

Esta "realización" simplifico mucho mi código y ese es el que presento. Lo primero que tuve que crear fueron expresiones que representen lo parámetros que la función recibiría, para ello cree expresiones del tipo ParameterExpression:


var paramSb = ParameterExpression.Parameter(typeof(StringBuilder), "sb");
var paramObj = ParameterExpression.Parameter(typeof(object), "obj");

Luego tuve que crear el primer elemento de mi expresión que es simplemente la referencia al objeto StringBuilder.


Expression body = paramSb;

Claramente si quería acceder a las propiedades del objeto era necesario que hiciera un cast al tipo particular del objeto, para esto cree una expresión UnaryExpression utilizando el método Convert


var convert = Expression.Convert(paramObj, anonymousType);

Con estos elementos dispuestos podía empezar a iterar a través de las propiedades de la clase. Para ello tuve que recurrir a Reflection (gulp!) y en este punto me di cuenta que para poder crear la expresión de invocación también necesitaba la información de los métodos a los que deseaba invocar. Así que estas lineas son Reflection y nada más.


MethodInfo appendString = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(string) });
MethodInfo appendObject = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(object) });
PropertyInfo[] props = anonymousType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

Con todo listo armé el bloque que recorre todas las propiedades y va generando la expresión


bool first = true;           
foreach (PropertyInfo prop in props)
{
body =
Expression.Call(
Expression.Call(
body,
appendString,
Expression.Constant((first ? " ":"\" ") + prop.Name + "=\"")),
appendObject,
Expression.Property(convert, prop));
first=false;
}

Paso a explicar el bloque, lo primero que hage es crear una llamada al método Append de la última expresión (que siempre es un StringBuilder gracias a la interfaz fluida) y paso como parámetro el nombre de la propiedad seguida del igual y apertura de comillas (Hay una diferenciación para la primera ejecución que solo genera el nombre). Esta acción corresponde a la expresión anidada.


Expression.Call(
body,
appendString,
Expression.Constant((first ? " ":"\" ") + prop.Name + "=\""))


Luego agrego una llamada al método Append de la clase StringBuilder, pero esta vez a la implementación que recibe un objeto como parámetro, pasando como parámetro el objeto ya tipado por la conversión
(potencialmente podría optimizarse esta generación para diferenciar las propiedades que son cadena o no)


Expression.Call(
/*...*/,
appendObject,
Expression.Property(convert, prop))


La expresión resultante es asignada para que se la base de la próxima itearción. Una vez que se hayan procesado todas las propiedades solo queda agregar el cierre de comillas (si es que se proceso al menos un atributo)



 if (!first)
{
body = Expression.Call(
body,
appendString,
Expression.Constant("\""));
}

Y el toque final la compilación, en este caso estoy especificando el formato del delegado a generar para que pueda incluirlo en el diccionario de funciones


return Expression.Lambda<Action<StringBuilder, object>>(body, paramSb, paramObj).Compile()

Listo, ya tenemos una función que procesa el objeto de manera tipada. luego de organizar y para evitar creaciones innecesarias la estructura final de la función es la siguiente:


        static MethodInfo appendString = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(string) });
static MethodInfo appendObject = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(object) });

private static Action<StringBuilder, object> BuildFunction(object properties)
{
Type anonymousType = properties.GetType();
var paramSb = ParameterExpression.Parameter(typeof(StringBuilder), "sb");
var paramObj = ParameterExpression.Parameter(typeof(object), "obj");
var convert = Expression.Convert(paramObj, anonymousType);
Expression body = paramSb;
PropertyInfo[] props = anonymousType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
bool first = true;
foreach (PropertyInfo prop in props)
{
body =
Expression.Call(
Expression.Call(
body,
appendString,
Expression.Constant((first ? " ":"\" ") + prop.Name + "=\"")),
appendObject,
Expression.Property(convert, prop));
first=false;
}
if (!first)
{
body = Expression.Call(
body,
appendString,
Expression.Constant("\""));
}
return Expression.Lambda<Action<StringBuilder, object>>(body, paramSb, paramObj).Compile();
}


Hecha la implementación llegó el momento de medirla. Lo que hice fue medir el tiempo de la primera ejecución y el promedio de subsecuentes ejecuciones para cada implementación. La razón para medir de esta forma es que el ejemplo esta orientado a una página Web donde cada ejecución no iniciaría de nuevo la función y solo el primer acceso sería penalizado por la compilación (similar a lo que hace ASP.Net para sitios que no están precompilados).


Las llamadas ejecutadas son las siguientes


HtmlHelpers.GetHtmlLink("Ejemplo", new { @class = "style" , target ="top" });
HtmlHelpers.GetHtmlLinkDictionary("Ejemplo", new Dictionary {{"class", "style"}, {"target", "top"}});
HtmlHelpers.GetHtmlLinkLamba("Ejemplo", new { @class = "style" , target ="top" }));

He aqui los resultados.


Función Primera Ejecución Promedio Ejecuciones Posteriores
GetHtmlLink 15195 2
GetHtmlLinkDictionary 7561 36
GetHtmlLinkLambda 12032 2

Como era de esperarse la implementación inicial es la que menor rendimiento tiene en todos los casos así que no la revisaré. Lo que quiero rescatar es que si bien la primera ejecución del método que recibe el diccionario es más rápida (estos son ticks lo que significa que es medio segundo más rápida) posteriores ejecuciones son mucho más rápidas pues la expresión compilada no necesita hacer ningún tipo de recorrido o búsqueda.


Con esos resultado, y para un sitio web o servicio donde el proceso se mantiene en memoria para atender múltiples requerimientos, me quedaría con la opción de precompilar y generar un código más agradable a la vista

domingo, mayo 10, 2009

Primer día en Windows 7

Bueno, finalmente instale Windows 7, lo único que puedo decir es que es altamente recomendable. A continuación hago un resumen de mi experiencia (todo esto lo hice en mi portatil Lenovo ThinkPad X61 2GB RAM)

Preparación

Antes de iniciar obviamente obtuve un backup pero a falta de uno hice tres: solo archivos, imagen de disco, windows easy transfer. El último lo hice para ver si me aceleraba el proceso de resintalación.

Instalación base

Basado en esperiencias anteriores habia destinado un par de horas para realizar la instalación básica, mi sorpresa fue enorme cuando 18 minutos después de prender la máquina para instalar Windows 7 estaba listo para instalar otras aplicaciones.

Lo primero que hice fue agregar todos los componentes que no se instalan por defecto (IIS, MSMQ, etc) y nuevamente me sorprendi pues la activación tomo 2 minutos.

Instalación Aplicativos

Lo siguiente a instalar es Office 2007 que igualmente esperaba tomará unos cuantos minutos por lo que inicie la instalación y me fui a hacer otras cosas, 8 minutos después volvi a buscar algo y vi que la instalación estaba completa.

Emocionado por el éxito decidí reinstalar todo pero tenía cosas para hacer (la vida normal que se interpone) así que durante la tarde finalmente pude sentarme a continuar con la instalación.

En general el proceso de instalación consistía en revisar la última versión de la aplicación, descarga (si es necesario), instalación. Para darles una idea de lo que esto significa listo las aplicaciones instaladas

Esto tomo unas 6 horas lo que me hace pensar que la instalación es mucho más rápida pues recuerdo que en otras  ocasiones me tomaba mucho más llegar a tener todo instalado.

transferencia de preferencias

Lo primero que intente fue utilizar la imagen de Windows Easy Transfer que habia generado. Windows 7 la proceso en un tiempo que encuentro es un poco excesivo (50 minutos para 17 GB de datos) pero al ser un proceso de una única vez deje que se completará.

Al finalizar la recuperación pude observar, con alegria, que todas mis datos, personalizaciones y configuraciones se recuperaron sin ningún problema con lo que luego de 8 horas tenía todo mi entorno listo en Windows 7.

Adicionales

Una vez que termine la instalación note que había un par de cosas que extrañaba:

  • Utilitarios ThinkPad. Principalmente Access Connections, Presentation Director y Power Manager.
    • Access Connections es un utilitario de Lenovo que permite definir configuraciones de redes (IP, Proxy, impresora, seguridad, etc.) y asociarlo con puertos, MACs para facilitar el cambio de configuración. En el último año ha sido de mucha utilidad. En fin pude instalarlo y funciona, el icono de acceso rápido no se inicia, pero eso no es muy preocupante pues no lo uso habitualmente.
    • Power Manager: Las capacidades de optimización que brinda power manager me han ayudad mucho. Lo bunoe instalado y funcionando :)
    • Presentation Director: La combinación Windows+P en Windows 7 lo elimina de mi lista :)
  • Windows Virtual PC: windows 7 utilizará la nueva versión de Virtual PC por lo que fue necesario que descargue el beta y lo instale. Posteriormente pude abrir mis máquinas virtuales, tengo que decir que aun no estoy muy familiarizado con la nueva interfax y creo que me tomara un tiempo

Toque finales

Obviamente descargar todos mis correos con Outlook, y descargar actualizaciones necesarias. La verdad eso lo deje corriendo durante la noche por lo que no se cuanto tomó pero hoy ya tengo todo  listo.

Primeras apreciaciones

Mis primeras apreciaciones son muy positivas.

  • El sistema tiene una respuesta mucho más adecuada en general
  • El proceso de reinicio es ligeramente más rápido pero el acceso al escritorio es mucho mas inmediato
  • El consumo de memoria es notoriamente menor, con Vista tenía deshabilitado Aero, transparencias, todos los gadgets y varios servicios de presentación y al momento de inicio de Windows (sin aplicaciones) no tenía más de 960 MB libres. En este momento estoy ejecutando outlook, IE, live writer, con todas las ventajas de UI y tengo 1015MB libres.
  • El trabajo del equipo de Windows en el stack de IU es sin duda muy notorio la interacción en general con Windows es mucho mejor

En resumen ha sido una experiencia muy gratificante. Desde hoy empiezo a recomendar la migración a todos :)

domingo, mayo 03, 2009

Alternativas de Bloqueo en .Net

Recientemente pude continuar mi interrumpida lectura de Concurrent Programming on Windows y durante la misma se discuten los distintos tipos de Sincronización que se pueden usar con .Net. Como en casi toda lectura que he visto se menciona el costo adicional que puede incorporar el uso de las clases ReaderWriterLock, ReaderWriterLockSlim frente al uso de Monitor.Enter, Monitor.TryEnter (lock o syncLock), en el libro se hace un análisis del porque el impacto adicional en el rendimiento, pero como siempre me quedaban dudas respecto a cual es el verdadero margen de diferencia.

Existen varias comparaciones del impacto en rendimiento siendo casi siempre el comentario la gran diferencia que hay a favor de Monitor/lock frente a ReaderWriterLock , por ejemplo http://is.gd/wrDH, http://blogs.msdn.com/ricom/archive/2006/05/05/590955.aspx (comentarios, no el post en si mismo) sin embargo siempre he encontrado dichos números un poco vagos (espero no caer en el mismo error)

Usualmente toda tarea de sincronizacion siempre la hago con lock por ser la más fácil de implementar y porque nunca he tenido problemas de contención, sin embargo aprovechando que hoy en la tarde tenía tiempo decidí hacer una prueba muy simple para comparar las alternativas en distintos escenarios.

Componentes

Mi prueba es muy simple tengo 3 clases que exponen dos métodos uno de lectura (Shared Lock) y uno de escritura (Exclusive Lock). Ambos métodos sincronizan el acceso a través de cada uno de los mecanismos a considerar en la prueba, la clase TraditionalLock<T> utiliza el tradicional lock sobre una clase creada en el constructor de la clase, la clase ReaderWriteLock<T> utiliza una instancia de la clase del mismo nombre y finalmente ReaderWriteLockSlim<T> utiliza la clase del framework 3.5.

   1:
   2:     class TraditionalLock<T> : ILockTest<T>
   3:     {
   4:         private T data;
   5:         private object sync = new object();
   6:     
   7:         public T  Read()
   8:         {
   9:             lock (sync)
  10:             {
  11:                 // Thread.Sleep(50);
  12:                 return this.data;
  13:             }
  14:         }
  15:  
  16:         public void  Write(T value)
  17:         {
  18:             lock (sync)
  19:             {
  20:                 // Thread.Sleep(50);
  21:                 this.data = value;
  22:             }
  23:         }
  24:     }
  25:  
  26:     class ReaderWriteLock<T> : ILockTest<T>
  27:     {
  28:         private T data;
  29:         private ReaderWriterLock sync = new ReaderWriterLock();
  30:  
  31:         public T Read()
  32:         {
  33:             sync.AcquireReaderLock(System.Threading.Timeout.Infinite);
  34:             try
  35:             {
  36:                 // Thread.Sleep(50);
  37:                 return this.data;
  38:             }
  39:             finally
  40:             {
  41:                 sync.ReleaseReaderLock();
  42:             }
  43:         }
  44:  
  45:         public void Write(T value)
  46:         {
  47:             sync.AcquireWriterLock(System.Threading.Timeout.Infinite);
  48:             try
  49:             {
  50:                 // Thread.Sleep(50);
  51:                 this.data = value;
  52:             }
  53:             finally
  54:             {
  55:                 sync.ReleaseWriterLock();
  56:             }
  57:         }
  58:     }
  59:  
  60:     class ReaderWriteLockSlim<T> : ILockTest<T>
  61:     {
  62:         private T data;
  63:         private ReaderWriterLockSlim sync = new ReaderWriterLockSlim();
  64:  
  65:         public T Read()
  66:         {
  67:             sync.EnterReadLock();
  68:             try
  69:             {
  70:                 // Thread.Sleep(50);
  71:                 return this.data;
  72:             }
  73:             finally
  74:             {
  75:                 sync.ExitReadLock();
  76:             }
  77:         }
  78:  
  79:         public void Write(T value)
  80:         {
  81:             sync.EnterWriteLock();
  82:             try
  83:             {
  84:                 // Thread.Sleep(50);
  85:                 this.data = value;
  86:             }
  87:             finally
  88:             {
  89:                 sync.ExitWriteLock();
  90:             }
  91:         }
  92:     }
  93:  
  94:     class VolatileLock : ILockTest<int> 
  95:     {
  96:         private volatile int data;
  97:  
  98:         public int Read()
  99:         {
 100:             return this.data;
 101:         }
 102:  
 103:         public void Write(int value)
 104:         {
 105:             this.data = value;
 106:         }
 107:     }

Todas estas clases son utilizadas por un controlador que utiliza el ThreadPool de .Net para solicitar la ejecucion de los métodos de escritura y lectura en una proporción de n:1 que varia desde 1:1 hasta 29:1 para estas pruebas. El tiempo total de la ejecución de todos los métodos es medido utilizando la clase StopWatch


   1: static long MeasureLockTest<T>(ILockTest<T> instance,T value, int totalExecutions, int ratio)
   2: {
   3:     Stopwatch sw = new Stopwatch();
   4:     int writerCounter = 0;
   5:     int readerCounter = 0;
   6:  
   7:     ratio++;
   8:     sw.Start();
   9:     for (int counter = 0; counter < totalExecutions; counter++)
  10:     {
  11:         if (counter % ratio == 0)
  12:         {
  13:             ThreadPool.QueueUserWorkItem((state) => instance.Write(value));
  14:             writerCounter++;
  15:         }
  16:         else
  17:         {
  18:             ThreadPool.QueueUserWorkItem((state) => instance.Read());
  19:             readerCounter++;
  20:         }
  21:     }
  22:     ThreadPool.QueueUserWorkItem((state) => sw.Stop());
  23:     while (sw.IsRunning) ;
  24:  
  25:     return sw.ElapsedMilliseconds;
  26: }



Escenarios

Claramente la prueba es muy simple pues mide actividad sobre operaciones muy simples. Sin embargo, si miran el código de todas las clases en ellas esta comentado código que hace Sleep, esta inserción es la poco imaginatica implementacion de cierto tipo de costo en uso de recursos en una operación (la elección de Sleep es intencional pues dada la carga de requerimientos en el thread obliga a evaluar la ejecución de otro thread lo que también puede jugar un factor en escenarios de concurrencia). Con esta simple linea me permitió crear cuatro escenarios de ejemplo:

  • Operaciones sin costo: Tipicamente son aquellas operaciones donde el bloqueo tiene por unico sentido la sincronización y brindar propiedades thread-safe. En este caso, uno de los más comunes, la operación es muy corta y con muy poco impacto. Por ello para obtener un grado de medida adecuado esta operación tuvo que realizarse 100000 veces.
  • Operaciones con costo en acceso exclusivo: En este escenario la operación que requiere el bloqueo exclusivo (tipicamente la escritura) tiene un costo alto en tiempo (50 milisegundos), mientras que la operación con bloqueo compartido es bastante simple y rápida. Un escenario típico de este tipo de bloqueo son los caches implementados en consultas a servicios o fuentes de datos remotas donde la búsqueda inicial tiene un costo alto pero subsecuentes accesos son de bajo costo.
  • Operaciones con costo en acceso compartido: En este escenario es la operación de acceso compartido la que tiene un costo alto mientras que la operación con costo exclusivo es de corta duración. Este tipo de escenario es un poco más dificil de darse y la verdad solo lo hice como un ejercicio.
  • Operaciones con costo: Finalmente en este escenario ambas operaciones tiene un costo alto que es igual para ambas operaciones. Ejemplos de este tipo de escenarios se refieren principalmente al acceso sincronizado a recursos externos.

Nota: En ninguno de los casos analice el uso de recursos de sistema que puede ser un factor en el cual Monitor/lock salen faavorecidos por no usar objetos de sincronización de Kernel

Resultados

A continuación se presenta los resultados de las pruebas en un grafico que muestra el tiempo promedio de las operaciones ejecutadas durante la prueba

Operaciones sin costo: 10000 iteraciones sin costo,

Gráfico de linea de tiempo promedio

Como verán en este caso ReaderWriterLock es claramente un perdedor pero no en la proporción abismal que siempre leí.

Operaciones con costo en acceso exclusivo: 100 iteraciones con costo de 50 milisegundos

Gráfico de linea de tiempo promedio

En este caso la diferencia entre todos los mecanismos es muy baja y en distintas ejecuciones demostró ser prácticamente nulo

Operaciones con costo en acceso compartidos: 100 iteraciones con costo de 50 milisegundos

Gráfico de linea de tiempo promedio

En este escenario es muy claro que la ventaja la tienen los criterios de bloqueo compartido con una ventaja inicial a favor de la nueva clase del framework 3.5, Como dije anteriormente no se me ocurre un escenario común.

Operaciones con costo: 100 iteraciones con costo de 50 milisegundos

Gráfico de linea de tiempo promedio

Al igual que en el caso anterior Monitor/lock son claros perdedores conforme mayor proporción de lecturas vs. escrituras existen. Es más en este caso la ventaja es muy notoria aún con una baja proporción de distribución (2:1).

Volatile

Al terminar la prueba se me ocurrió que para el primero de los escenarios (propiedades thread-safe) tambíen pueden utilizarse variables volatiles así que hice una prueba y aqui estan los resultados de 100000 ejecuciones (dado que volatile no es una sección protegida solo el primer escenario es aplicable)

image

Como muestra el gráfico sacrificar el uso del cache de thread tiene un impacto en rendimiento bastante importante por lo que a pesar de su facilidad de uso y menor uso de recursos esta opción no es válida si lo que se busca es el mejor rendimiento.

Conclusiones

Considerando solo los tiempos y sin incluir el uso de recursos (que como mencioné anteriormente brinda una ventaja a Monitor/lock) las siguientes conclusiones vienen a mi cabeza:

  • Para sincronización en acceso a propiedades y cache claramente no hay ganancia en rendimiento apreciable en ninguno de los casos por lo que considerando el uso de recursos y facilidad de uso y lectura Monitor/lock/SyncLock parece ser la mejor opcion.
  • En aquellos casos donde la sincronización se realiza sobre una sección que puede tener un costo medio o alto en tiempo es recomendable el uso de ReadWriterLockSym en Framework 3.5 o superior, o ReadWriterLock en versiones anteriores. En particular el acceso sincronizado a recursos de red puede ser un muy escenario de uso

sábado, agosto 30, 2008

Microsoft F# Developer Center

Está disponible en MSDN un área dedicada a F# junto con el primer CTP de la integración en Visual Studio. Definitivamente algo para entretenerse

Microsoft F# Developer Center

domingo, junio 22, 2008

Mi humilde opinión en F#

El día de hoy después de un gran esfuerzo y de una parcial cambio en el modelo de programación terminé de leer Expert F#. Que puedo decir, este es uno de esos libros que me cambió la percepción de muchos aspectos del desarrollo de software.

Algunas opiniones en distintos aspectos

Escritura

La escritura es muy clara y a pesar de que hay temas muy complejos que son abordados de manera muy simple para este humilde mortal los ejemplos de código están muy bien elegidos para explicar el mensaje.

Es interesante que si bien no se trata de entrar en detalle en todos los aspectos clarifica muchas cosas de lenguajes funcionales, .Net framework, modelos de programación, parsers, etc.

Claramente este libro será mi libro de cabecera por un buen tiempo, aún si no estoy programando en F#

Programación

La cantidad de cosas que aprendí, recorde y corregí de mis idas es muy grande para enumerarla. Los conceptos, explocaciones y recomendaciones incluidas en el libro son generales y pueden ser aplicadas en cualquier entorno y lenguje de programación.

Otro punto alto y loable.

El lenguaje

Debo decir que si bien hay muchos lenguajes que me gustan y con los cuales tuve química desde el principio (C#, Python, Lisp, Turbo Pascal) F# no fue uno de ellos. Sin embargo una vez terminada la lectura del libro debo decir que quede muy impresionado por el mismo y con muchas ganas de programar un módulo real (no quiero ejemplos ni ejercicios). Tengo en mente un muy buen ejemplo pero por ahora lo mantengo en el misterio :)

PS: Esta lectura me ha hecho aprender y apreciar cosas de otras lenguajes y debo decir que estoy muy interesado en aprender más de los conceptos de Erlang. El lenguaje no me parece atractivo pero su diseño y conceptos es ciertamente MUY interesante

domingo, mayo 25, 2008

Prácticas Funcionales en desarrollos empresariales

Hace unos meses que estoy trabajando, muy lentamente, en el uso de F# para ello estoy constantemente releyendo el excelente libro Expert F#. Durante este periodo empezé el desarrollo de un proveedor de ADO.Net y decidí aplicar algunos de los linemientos y conceptos de programación funcional. Para ser más exacto decidí tratar de crear objetos inmutables y funciones sin efectos colaterales y esta experiencia me deja algunos aprendizajes:

  • Estamos muy mal acostumbrados. Es decir que la evolución de los lenguajes nos han llevado a pensar en un modelo donde las variables pueden ser alteradas en cualquier momento sin entender las implicaciones de estos cambios. Una vez que uno empieza a pensar en concurrencia, multi.core, etc. este tipo de programación es muy nocivo.
  • Es muy práctico diferenciar los datos mutables e inmutables. Dado que el proveedor será usado en un entorno donde la mutabilidad es "recomendada" es necesario soportar algunos de estos aspectos pero resulta interesante que al separar aquellos aspectos mutables de los inmutables se puede ahorrar mucho esfuerzo.
  • Las funciones sin efecto colateral generan código que facilmente puede ser paralelizado y ejecutado de manera asincrónica.

En fin creo que el ejercicio fue muy bueno y estoy contento con los resultados obtenidos y justo cuando estaba en el proceso de detallar todos estos aspectos este post me evita el trabajo :)

domingo, abril 27, 2008

Programación Paralela, Concurrencia y otros

Estoy tratando de entender la tendencia en este campo y la cantidad de esfuerzos y variedad de librerias me esta volviendo loco.
En cuanto tenga una idea de algo volveré a comentar en el tema

Seguridad en Frameworks - Parte I

Durante los últimos meses he estado trabajando mucho en la generación de un framework de uso no controlado. Es decir un framework que será utilizado por un conjunto variado de desarrolladores con los cuales no habrá una interacción. Esto no es una novedad para quienes desarrollan frameworks opens-source o redistribuibles, sin embargo a diferencia de ellos este framework es de uso corporativo y con un foco muy alto en la seguridad y restricción en lo que los usuarios del mismo pueden realizar. Para ser más claros la idea no es solo proveer piezas reutilizables de código y lógica sino asegurar que dichas piezas sean utilizadas obligatoriamente y limitar el código desarrollado en lo que puede realizar directamente.

Duante el desarrollo de dicho framework utilizamos muchos, sino todos, de los recursos disponibles en lo que se refiere a diseño de frameworks y librerias base pero llegó un punto donde la información disponible no era suficiente para poder lograr nuestro objetivo. Esto tenía que ver con el uso de CAS (Code Access Security). Ha sido establecido por más de uno que la documentación relacionado con el tema es bastante críptica por lo que luego de una breve investigación logramos establecer un mecanismo para lograr nuestro objetivo. Así que creo que vale la pena resumir el resultado del análisis.

¿Que es CAS?

CAS es un mecanismo que permite controlar la ejecución de porciones de código limitando de esta forma el acceso a operaciones y recursos protegidos. En el caso de un framework empresarial el uso de CAS es muy importante pues permite que los administradores puedan limitar el acceso de las aplicaciones a los recursos declarados. Algunos de los elementos que son controlados a traves de CAS son:

  • Acceso a red
  • Acceso a bases de datos
  • Acceso al sistema de archivos
  • Acceso al Registro de Windows

CAS ha sido muy poco utilizado en general debido a que la mayoria de las aplicaciones se ejecutan en FullTrust.

El Framework como Proxy

Framework como Proxy

Si bien el escenario ideal permite que una aplicación pueda estar limitado a acceder a los recursos exclusivos a través de CAS esto implica que toda aplicación deberia tener un inventario de los recursos utilizados (el escenario ideal incluso para un modelo de amenazas) pero esto no es posible cuando se habla de cientos de aplicaciones modificadas varias veces por año. Por esto es que en el escenario en el que estuvimos trabajando se planteó utilizar el framework de desarrollo como proxy es decir, todo acceso a un recurso sensible es realizado por el framework y las aplicación solo tienen permiso de invocar al framework.

En este caso se da un cambio en las premisas de administración donde en lugar de buscar la mayor restricción en el acceso a recursos se favorece el control y auditoria de acceso. Con el escenario propuesto si bien las aplicaciones podrán acceder a todos los recursos el framework genera toda la información de auditoria necesaria y puede brindar mecanismos de restricción de acceso que sea configurable de manera más simple que la consola y herramientas de CAS.

En próximos posts haré un análisis básico del uso de CAS en una configuración de permisos delegados.

Recursos para el diseño de frameworks

Design Guidelines for Developing Class Libraries

LINQ Framework Design Guidelines

Framework Design Guidelines

Designing .NET Class Libraries: Security