Recently in Programación Category

Control de Versiones Distribuido

| No Comments | No TrackBacks
Mi anterior apunte sobre contol de versiones tuvo buena aceptación, y recibí comentarios de gente que no es desarrolladora de software, lo que es bueno. Insisto en que el control de versiones es una herramienta que tiene aplicaciones fuera del mundo de la programación.

Pero la vez anterior hablamos de sistemas centralizados, como Subversion (SVN) o CVS, los más populares. Pero esta vez les quiero invitar a que prueben los sistemas de control distribuidos, o DCVS por sus siglas en inglés.

Si ustedes no tienen la mente dañada con sistemas como Subversion o CVS, no tendrán problemas en adoptar estos sistemas, y les sugiero explorar entre los dos más populares en la actualidad Mercurial (hg) y GIT.http://git-scm.com/.

Cuando uno se enfrenta por primera vez a un DCVS viniendo del mundo de los sistemas centralizados, cuesta adoptar este modelo. Hay que reaprender algunas cosas.

Sugiero leer este tutorial de Joel Spolsky, donde explica muy bien los cambios que debemos adoptar cuando entramos desde un sistema centralizado como Subversion a un DCVS, como Mercurial en su caso.

Lo principal es la libertad que podemos ganar como desarrolladores.

Subversion no nos deja beneficiarnos del control de versiones porque siempre nos obliga a hacer commit cuando el código está completamente probado y libre de errores, o de lo contrario estamos arruinando el trabajo de los demás.
Esta viñeta de Geek and Poke expone el problema:

problemas_con_svn2.jpg
Con un DVCS los programadores trabajan en sus copias particulares del repositorio sin interferir en el código central.

Los DCVS tienen muy buenos sistemas que permiten mezclar (merge) el código que viene desde distintas ramas.

En este sentido GIT es un ejemplo destacado, puesto que el mismo Linus Torvalds es capaz de hacer un merge desde 12 distintas ramas casi sin ningún problema.

Yo he evaluado 4 herramientas, usándolas bastante tiempo para mi trabajo personal. 

La primera herramienta que ocupé fue Bazaarr, hace unos 3 años. Bzr es la herramienta usada por Canonical, en el desarrollo de Ubuntu. Está desarrollada en Python, y es bastante simple de usar.

La segunda fue Darcs, es una herramienta interesante, desarrollada por un físico cuántico y que tiene un modelo matemático (algebra de parches), y está escrita en Haskell. Recomendable para el que tenga inclinaciones más académicas.

La tercera y cuarta son Mercurial y GIT.

Como dijo alguien por ahí, GIT es McGyver, Mercurial es James Bond, ambos tienen sus pro y contras.

A mi me gusta GIT, y es lo que uso en la actualidad en forma personal, llevar el cambio al ambiente corporativo donde trabajo creo que tomará tiempo, pero puedo seguir interoperando con Subversion gracias a GIT.

Lo que más me gusta de GIT es que es una herramienta muy rápida, es probablemente el controlador de versiones más rápido que he usado, incluso cuando hago un push a un servidor central en internet.

Pero la decisión es de ustedes. Si nunca han usado Subversion o CVS, es mejor elijan una herramienta DCVS. Si me preguntan a mi, les recomiendo GIT, o Mercurial, probablemente la última es más simple de usar, pero la primera es más flexible y rápida.

Sistemas de Control de Versiones

| 4 Comments | No TrackBacks
Subversion project visualization image.

Image via Wikipedia

¿Usaron alguna vez SCCS? ¿No? ¿Que tal RCS? ¿Tampoco? Bah, son ustedes muy jóvenes. Pero, usan un sistema de control de versiones, ¿verdad? Hoy es raro que un desarrollador no use un sistema de  control de versiones como CVS o Subversion (conocido también como SVN). 

Lo que no me explico es ¡por qué no lo usa todo el mundo!

Verán, mucha gente escribe un archivo word, "aburrido_informe_para_el_auditor.doc", empiezan a trabajar en este, si son astutos, y tienen experiencia,  antes de realizar una modificación hacen una copia y empiezan a trabajar en "aburrido_informe_para_el_auditor-version-2.doc", y después en "aburrido_informe_para_el_auditor-version-3.doc" y sigue la cosa, y la carepta se llena de archivos como  "informe_auditor_4.doc", informe-auditor-final.doc", "informe-para-el-auditor-revisado-por-mi-jefe.doc", "informe-ups-me-equivoqué-en-losplazos.doc" y así, la carpeta de trabajo es un desastre, si es que usan carpeta, y no lo tienen desparramado en el escritorio (los lectores de LNDS no hacen eso, por supuesto).

Los programadores tenemos este problema, pero multiplicado por mil, porque los usuarios están llenos de ideas creativas y el software va cambiando todo el tiempo, y a veces hay que volver atrás. O tienes código que implantas para un cliente, y luego se crea una rama de código con modificaciones ad hoc para un cliente. Los escenarios son múltiples.

Es por eso que desde 1972 tenemos sistemas de gestión de versiones de código. Los primeros eran los que mencioné en la primera parte de este artículo. Estos primeros sistemas estaban orientados al trabajo individual de cada programador.

El problema era cuando un equipo de programadores trabajaban en el mismo proyecto, ahí nacieron sistemas centralizados, como CVS, SourceSafe (de Microsoft), Perforce, y el más famoso de todos Subversion.

No les voy a explicar acá qué es un sistema de control de versiones, sólo les voy a sugerir, si no son programadores, que investiguen sobre estas cosas, y las utilicen en su trabajo, es una buena estrategía (para los diseñadores web esta es una herramienta muy útil).

En lo que si me quiero meter es en el tema de los sistemas de control de versiones distribuidos, que son, en mi opinión (y no estoy sólo en esto), un gran avance en nuestro campo. Pero eso lo vamos a dejar para el próximo post.

Linus Torvalds on GIT

| 2 Comments | No TrackBacks
Hace tiempo que no escribo artículos más técnicos, así que voy a retomar algunos temas que me interesa profundizar (que es una excusa para aprender nuevos tópicos). Y un tema que me interesa, desde hace un par de años al menos, es el desarrollo de los DCVS, los sistemas de administración de código distribuidos. En este tema estoy más o menos convencido de que GIT es el camino, pero antes de explicarles por qué, quiero dejarlos con este video de Linus Torvalds, en que habla de este DCVS. El video además es muy interesante, porque pocas veces se ve a Linus exponiendo, y expresando sus opiniones en vivo con tanta fuerza como lo hace por escrito.

Les dejo el video, está en inglés, sin sub títulos, pero si se van a dedicar a desarrollar software supongo que saben inglés, y son capaces de escuchar una charla en inglés sin traductores, right?


Detén el reloj, aplasta al "bicho"

| No Comments | No TrackBacks

fast-clock.gif

Si este blog fuera lo que alguna vez pretendí que fuera, me gustaría que se pareciera a Word Aligned, y para que tengan una idea de que les estoy hablando, he traducido un artículo de ese extraordinario blog, espero que lo disfruten (incluso los que son menos técnicos). (*)


Detén el reloj, aplasta el bicho [1]

Por Thomas Guest (Word Aligned)

¿Cuál de estos relojes es el mejor?

stopped-clock.gifslow-clock.giffast-clock.gif

Podríamos fácilmente argumentar que el que está detenido.

¿Podemos? En "Los Dos Relojes"[2] Lewis Carroll argumenta de otra manera.

¿Qué es mejor, un reloj que da la hora exacta una vez por año, o un reloj que es puntual dos veces por día? "Este último", contestarás, "incuestionablemente". Muy bien, ahora atiende.

Supongamos que tengo dos relojes: uno no funciona en lo absoluto, y el otro se retrasa un minuto al día: ¿cuál preferirías? "El que se retrasa", replicarías sin ninguna duda. Ahora observa: el que se retrasa un minuto al día tiene que emplear doce horas, o setecientos veinte minutos, hasta que de nuevo señale la hora correcta; por consiguiente es puntual una vez cada dos años, mientras que el otro es puntual evidentemente siempre que sea la hora por él indicada, lo que ocurre dos veces al día.[3]

Esta es una distracción divertida, pero no realmente problemática: por supuesto que el reloj que pierde tiempo es de uso más práctico, aún así es paradojal, mientras menos tiempo pierda menos a menudo dice la hora correcta. Un reloj que pierde sólo un segundo al día sólo indica la hora correcta cada 118 años aproximadamente.

Software Bugs

Mencione estos relojes defectuosos porque estoy pensando sobre los fallos en el software (bugs) y como los buscamos y corregimos.

stopped-clock.gifspider.jpg

El código que es obviamente correcto es más fácil de detectar que aquel que es casi correcto, y detectar fallos es previo a corregirlos. Esto implica - construyendo sobre la terminología de Carroll - que es improbable que despachemos muchos relojes detenidos, pero si no somos cuidadosos podemos terminar despachando unos pocos que pierden tiempo. Y, en general, el código que es obviamente erróneo es más fácil de corregir que el código que es casi correcto. Una función terriblemente rota claramente necesita un replanteo; mientras que una que casi funciona puede simplemente ser ajustada hasta que parezca funcionar, a menudo resultando en un error más sutil.

Fugas y Carreras

C y C++ proveen buenos ejemplos de lo que estoy hablando. Consideren un programa que usa mal la memoria. Un intento de alojar un espacio de trabajo de 4294967295 bytes falla instantáneamente [4]; una lenta fuga de memoria, como el reloj lento, puede causar un daño no perceptible por un periodo extendido.

Herramientas decentes detectarán fugas de memoria. Condiciones de carrera (race conditions) en un código multi hebras son dificiles de seguir y pueden mostrarse elusivas durante las pruebas del sistema. Más de una vez he dejado un programa corriendo en un depurador (debugger), siendo alimentado con entradas al azar, con la esperanza de de que una rara y aparentemente aleatoria condición gatille una interrupción en la ejecución. ¡Denme código realmente roto cualquier día!

75% correcto vs 50% correcto

Acá hay dos implementaciones de una función en C para encontrar el punto medio entre un par de valores enteros positivos y ordenados, aproximando hacia abajo. Antes de seguir leyendo, pregúntese cuál es mejor.

int midpoint1(int low, int high)
{
      return low/2 + high/2;
}

int midpoint2(int low, int high)
{
     return (low + high)/2;
}

midpoint1 es un reloj detenido, retornando 3 en vez de 4 como el punto medio entre 3 y 5, por ejemplo. Entrega la respuesta incorrecta el 25% del tiempo -- fatalmente erróneo si fuera usado en el corazón de, digamos, una búsqueda binaria. Creo que podríamos detectar fácilmente el problema.

Una corrección obvía sería la mostrada en midpoint2, la que retorna 4 como el punto medio entre 3 y 5.

Sin embargo, midpoint2 es un reloj que se retrasa Si la suma low + high produce un desbordamiento (overflow) el resultado es indefinido. En mi implementación obtengo un valor negativo -- una cosa peligrosas si lo usamos como índice de un arreglo. Este es un defecto notorio y muy real, y muy bien documentado en una nota de Joshua Bloch subtitulada ""Casi todas las búsquedas binarias y merge sorts están rotos":http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html".

Bloch ofrece más de una corrección así que voy a hacer notar acá que:

  • este defecto simplemente no existe en lenguajes de alto nivel como Python o Haskell, donde los enteros sólo están limitados por los recursos de la máquina.
  • Pienso que Bloch no es justo al sugerir que el análisis de Jon Bentley del capítulo 4 de Programming Pearls es incorrecto. El seudo código de ese capítulo está escrito en un lenguaje tipo C algo entre C y Python, y de hecho uno de los ejercicios propuestos de Bentley es examinar el efecto que el tamaño de las palabras (words) tiene en este análisis.
  • En cierto sentido midpoint2 está más roto que midpoint1: sobre el rango de las entradas bajas y altas, la suma desborda y gatilla el defecto el 50% de las veces.

Algoritmos Probabilísticos

Los computadores se suponen que son predecibles y típicamente buscamos programas correctos. No hay razones por las que consideremos programas que sean suficientemente correctos, sin embargo y de hecho muchos programas que son suficientemente buenos para ser útiles también tienen fallas. Los avisos de Google, por ejemplo, analizan el contenido de las páginas webs y despliegan enlaces relacionados. El algoritmo usado es secreto, ingenioso y rápido, pero a menudo resulta en errores semánticos y, en ocasiones, errores ofensivos. Sin embargo, pocos podrían negar cuan útil ha sido para Google este programa.

Acá hay un ejemplo más interesante de un algoritmo, el cual, como un reloj que se atrasa, es casi correcto:

def is_fprime(n):

"""Usa el teorema pequeño de Frmat para adivinar si n es primo.
"""
from random import randrange
tries = 3
xs = (randrange(1, n) for _ in range(tries))
return all((x ** n) % n == x for x in xs)

 

No entraremos en las  matemáticas aquí. Una rápida ejecución con esta función parece prometedora.

 

>>> all(is_fprime(n) for n in [2, 3, 5, 7, 11, 13, 17, 19])
True
>>> any(is_fprime(n) for n in [4, 6, 8, 9, 10, 12, 14, 15])
False

De hecho, si le damos un trabajo real con algunos números grandes, lo hace bien. Lo he usado para adivinar cual de los números entre 100000 and 102000 son primos, comparando la respuesta con el resultado corecto. Tiene una tasa de éxito mejor que un 99% (en términos de relojes, pierde 8 minutos al día) y al aumentar tries mejora su desempeño.

Mejorando is_fprime

Mientras mejor  is_fprime se desempeña, menos probable es detectar lo que tiene de malo. Lo que es peor, no puede ser mejorado con ajustes simples. Sin embargo mientras más alto ponemos el valor de la variable  tries no tenemos una función correcta. Podríamos aún tomar una prueba al azar de la función y probar todo valor de x en el rango 1 a n dentro del predicado:

def exhaustive_is_fprime(n):
    return all((x ** n) % n == x for x in range(1, n))

exhaustive_is_fprime es muy caro para ejecutar y ocasionalmente retornará True para algún número compuesto. Si quieres saber más busca por los números de Carmichael.

El punto que quiero establecer es que el código que es casi correcto puede ser peligroso. Estamos tentados a corregirlo haciendo ajustes a una implementación existente, aunque, en este caso, se requiere de una revisión completa. En contraste, todos sabemos lo que se necesita hacer con el código que es claramente erróneo.

Programación defensiva

Todos hemos visto esas funciones nerviosas que van más allá de su interfaz declarada en un intento de protegerse de los usuarios descuidados.

 

/**
 * Retorna el valor máximo encontrado en el arreglo de entrada.
 * Pre condición: el arreglo no debe ser vacío.
 */
int nervy_maximum_value(int const * items, size_t count)
{
    int M = -INT_MAX;
    
    if (items == NULL || count == 0)
    {
        return M;
    }
    for ( ; count-- != 0; ++items)
    {
        if (*items > M)
        {
            M = *items;
        }
    }
    return M;
}

 

Lo que realmente queremos es un más simple y fácil para los clientes que usen este código:

 

 

int maximum_value(int const * items, size_t count)
{
    int const * const end = items + count;
    int M = *items++;
    
    for ( ; items != end; ++items)
    {
        if (*items > M)
        {
            M = *items;
        }
    }
    return M;
}

¿Se dieron cuenta del error sutil en nervy_maximum_value? Usa -INT_MAX en vez de INT_MIN lo que causará problemas si los clientes codifican sobre este comportamiento no documentado; si nervy_maximum_value es corregido posteriormente, el código del cliente puede explotar. Noten que no estoy en contra del uso de asertos (assertions) para revisar las pre condiciones, y una revisión simple como assert(items != NULL && count != 0) funciona bien en maximum_value; es la escritura de código que contiene estas condiciones fallidas el que considero erróneo. 


Tiempo de vida medio de los defectos.

La ocurrencia de defectos en un sistema de software complejo puede ser modelado de la misma manera que el decaimiento radiactivo. No he estudiado esta teoría y mi física está algo oxidada, pero la idea básica es que la población de errores en un software es como la población de partículas radiactivas.

Un fallo dado aparece (cualquier partícula decae) al azar, así que no podemos predecir cuando este evento sucederá, pero es igualmente probable que surja en cualquier momento particular. Esto le da a cada defecto un tiempo de vida promedio: una expectativa de vida pequeña para los defectos más ruidosos, tales como acceder a un puntero NULL, y tiempos más largos para defectos más sutiles, tales como los errores de redondeo acumulado. Asumiendo que corregimos un bug cuando ocurre, la población de defectos decae exponencialmente, y obtenemos la clásica curva en forma de cola. 

chart.png

 

Cualquiera que haya tratado de liberar un producto de software sabe lo que se siente deslizarse por la pendiente de esta curva. Probamos el sistema, encontramos defectos, los corregimos, repetimos. Al principio puede ser estimulante en la medida que los "bichos" con corta vida son aplastados, pero hacia el final del juego es desmoralizante ver cómo los defectos son reportados y no pueden ser reproducidos, y nos encontramos sin progresar.  Cuando finalmente dibujamos una linea y despachamos el producto lo hacemos sospechando que los peores problemas aún están por ser encontrandos. Para ponerlo en forma sucinta:

Ship happens!

Una combinación de técnicas nos ayuda a escapar de esta imagen depresiva. La más obvia sería evitarla: más que intentar una liberación tipo "big-bang" cada pocos años, podemos movernos hacia una liberación más continua e incremental. Una arquitectura desacoplada ayuda. De ahí la insistencia en pruebas unitarias. En vez de agitar el sistema y ver cómo caen los bichos, deberíamos desarrollar un conjunto de errores automatizados que activamente buscan los varios caminos a través del código y ejercitan los casos de borde.  Dentro de la base de código, la programación defensiva puede causar que los defectos se atrincheren. En vez de eso, deberíamos adoptar un estilo más confiado, donde el código falla en forma dura y rápidamente.

 

¿Cómo llego a funciona ese código alguna vez?

Han arreglado un defecto y luego se han preguntado ¿cómo pudo ese código haber funcionado antes de que lo corrigieran? Es una pregunta importante y una que requiere investigación. Quizás el defecto que arreglaste está compensado por programación defensiva en otro lado. O quizás hay vastas rutas a través del código que aún deben ser probadas.

Conclusiones

stopped-clock.gif

slow-clock.gif

fast-clock.gif

Ninguno de estos relojes es muy bueno. El primero está detenido, el segundo 

pierde un segundo cada minuto, el tercero gana un segundo cada minuto. Al menos es fácil ver el problema con el primero: no nos tentaremos en tratar de parcharlo.

Nunca debermos esperar que nuestro código funcione la primera vez y deberíamos sospechar si así parece. La programación defensiva parece significar cosas distintas a diferentes personas. Si hemos usado mal el térmno aquí, lo siento. Nuestra mejor defensa es asumir que el código está roto hasta que los hayamos probado, asumir que se romperá en el futuro si nuestros test no son automatizados, y fallar rápidamane  cuando detectemos errores.

 

Artículo original en inglés

 

Notas del traductor

(*) Este articulo fue publicado originalmente en julio de 2008, pero ya saben lo que pasó, esta versión tiene algunas correcciones.

[1] Bicho por bug, un defecto en programación.

[2] En Word Aligned hacen ref

erencia a The Rectory Umbrella, pero el texto de Carroll, que yo copio de "Matemática Demente", aparece con el nombre de "Los Dos Relojes".

[3] Texto copiado del libro: "Matemática Demente". Lewis Carroll - Traducción de Leopoldo María Panero, Tusquets Editores, Barcelona 1999 

[4] Tal como acota el autor, no es correcto que al tratar de solicitar  4294967295 bytes se provoque un error, malloc retorna NULL en el evento de una falla y el operador estandard new de  C++ define como comportamiento disparar la excepción bad_alloc.

 

Imágenes tomadas de Word Aligned, del artículo original. 


Ser ingeniero

| 2 Comments | No TrackBacks
El software no se ajusta a las leyes de la física, por lo tanto no sé si tiene sentido ablar de la ingeniería de software como tal, sin embargo, el software, como código puro, no es de mucha utilidad, debe ejecutarse sobre una infraestructura tecnológica,el hardware. Así que en ese momento, cuando se implanta un sistema, el programador, el desarrollador de software deja paso al ingeniero de sistemas.

Cuando nos preocupamos de los problemas de implantación, cuando luchamos por bajar la latencia, cuando nos enfrentamos a las falacias de la computación distribuida,   intentamos burlar los límites de la Ley de Amdahl, o aprovechar la elasticidad de la nube, en ese momento, estamos siendo ingenieros puros.

No olviden que un programador es un artista, pero también es un ingeniero. Feliz día de la ingeniería.


1 de 5.000 ó 5.000 de 1 (batch y transaccional)

| No Comments | No TrackBacks
Es fácil engañarse cuando se intenta optimizar, lo que parece lógico, y hasta numéricamente plausible puede estallarnos en la cara cuando lo analizamos en detalle, especialmente cuando lo ponemos a prueba en la práctica.

Es típico que algún experto nos aconseje sobre usar determinada técnica, porque en teoría es más óptima. Los novatos tienden a aplicar estas propuestas sin cuestionarlas mucho. Los más viejos tendemos a confiar en la experiencia, y esta suele darnos la razón.

Es lo que le pasó a Jeff Attwood, quien en un muy brillante post demuestra que el experto no tenía razón, y que lo que la práctica le decía tenía un fundamento, después de todo. Y el problema que explica Attwood es interesante, porque me he topado con el mismo varias veces, incluso recientemente.

La cosa es más o menos así, de acuerdo a un experto, citado por Attwood

"SI estás construyendo aplicaciones web ASP.NET, que van a tener miles de hits por segudo, la sobrecarga de ejecución de las consultas Linq van a consumir demasiada CPU y harán tu sitio web lento. Hay un costo en momento de ejecución asociado con con cada una de las consultas Linq que escribes. Las consultas son analizadas y convertidas a una sentencia SQL en cada hit. Esto no es hecho en tiempo de compilación, porque no hay manera de saber que puedes enviar en los parámetros a las consultas durante la ejecución."

Aunque Jeff Attwood está hablando de Linq y ASP.Net, el problema puede extrapolarse a otros ambientes, como PHP, o Java, en que tenemos consultas (queries) que se interpretan en tiempo de ejecución.

El mecanismo de Linq es bien elegante, y en mi opinión, más poderoso que otros ORM. En el caso particular que expone el autor se tiene una consulta en Linq más o menos así

var query = from widget in dc.Widgets
            where widget.ID == id && widget.PageID == pageId
            select widget;
var widget = query.SingleOrDefault();

El experto afirma que estuvo analizando el problema de este tipo de ejecuciones y descubrió que la ejecución de este tipo de sentencias hace palidecer al tiempo que se gasta en el roundtrip a la base de datos!!
Es decir, al parecer, se gasta más tiempo analizando estas sentencias Linq que en la ejecución del código resultante en la base de datos. Por lo tanto, lo que hay que hacer, nos recomienda, es compilar estos queries, antes de ejecutarlas (algo que complejiza bastante el código, y hace de la mantención del mismo una tarea más ardua).

Se pueden imaginar la cara que puso Jeff Attwood cuando leyó estas afirmaciones:

coding-horror-official-logo-small.png
Porque simplemente no lo podía creer. De hecho, el sitio Stack Overflow, con sobre 1.6 millones de page views al día, tiene muchas lineas de código que dependen de sentencias Linq como las cuestionadas por Omar Al Zabir, el experto MVP, autor de libros sobre el tema.

Y para darle más peso a su argumento, Omar Al Zabir muestra los datos de Rico Mariani, un ingeniero de Microsoft, quien condujo una serie de experimentos en que probaban el desempeño de sentencias Linq compiladas versus las no compiladas.
Esta es la tabla que Al Zabir muestra, extraida del artículo con los expermientos de Rico Mariani:

zabir_argument.png
Pero hay una trampita.

Para entender porque las afirmaciones de Omar Al Zabir no cuadran con la experiencia de Attwood, hay que entender como se hicieron las pruebas, y así podemos ver que Al Zabir comete un grave error, generalizando una solución, que es altamente dependiente del tipo de operación que vamos a realizar.

Rico Mariani nos explica que para comparar los dos tipos de consultas el ejecutó básicamente estos dos test:

Para el caso de las queries pre compiladas:
start timer
for (j=0;j<batches;j++)

  compile query

  for (i=0;i<runs;i++)

    create new data context

    run query in new context

stop timer

print number of selects and time etc.


Para el caso de las consultas interpretadas (no pre compiladas):

start timer
for (j=0;j<batches;j++)
  for (i=0;i<runs;i++)
    create new data context
    run query in new context
stop timer

print number of selects and time etc.


Y lo que hace Rico Mariani es ir jugando con la cantidad de ejecuciones, y el tamaño del proceso batch, lo que genera una tabla de resultados mucho más larga, y que tiene al final de la misma unos resultados que son ignorados por Al Zabir (no sabemos si intencionalmente). Estos resultados ignorados por el "experto" son:

Testing 2500 batches of 2 selects 
5000 selects uncompiled 9165.0ms 25000 records total 545.55 selects/sec 
5000 selects compiled 7892.0ms 25000 records total 633.55 selects/sec
Testing 5000 batches of 1 selects 
5000 selects uncompiled 9157.0ms 25000 records total 546.03 selects/sec 

5000 selects compiled 10825.0ms 25000 records total 461.89 selects/sec

Seguramente se han mareado a estas alturas, pero se los voy a resumir:

Al partir el experimento, se prueba con 1 lote de 5.000 selects. En el caso compilado se logran 925 selects/segundo, y sin compilar 543 selects por segundo. Ganador la versión compilada.

Al final del experimento, se pruena con 5.000 lotes con 1 select. En el caso compilado se logran 461 selects/segundo y sin compilar se logran 546 selects/ segundo.

De hecho si graficamos el tamaño del lote versus el desempeño en selects/segundo, queda claro lo que pasa:

performance_comp_vs_nocomp.jpg
¿Qué siginifica esto?
Resulta que nos encontramos ante los dos extremos de aplicaciones posibles, la aplicación de Attwood es una aplicación web, fuertemente transaccional, es decir, está en el extremo de los 5.000 lotes de tamaño 1. Por otro lado, las recomendaciones de Omar Al Zabir son útiles cuando el tamaño del lote es grande (lotes de 5.000 o más selects). Las recomendaciones de Al Zabir son muy útiles cuando estamos ante aplicaciones batch, offline, no transaccionales.

Ese es el caso particular de las aplicaciones con las que tengo que lidiar en mi trabajo, aplicaciones batch, con procesamiento de miles o millones de registros, es todo lo contrario de las aplicaciones transaccionales web. En mi caso los consejos de Al Zabir podrían tener aplicación (si usara Linq).

¿Es esto aplicable a la discusión del uso de los lenguajes compilados, versus los lenguajes interpretados? ¿O a otras aplicaciones? ¿como el uso de un ORM, versus llamadas directas a la base de datos usando JDBC, por ejemplo? Sospecho que sí, pero es una afirmación que, como siempre, requiere ser demostrada, y para eso hay que mostrar el código.

Referencias:



La programación en 10 años más

| No Comments | No TrackBacks
De acuerdo a Bertrand Meyer, en diez años más:

  1. Seguiremos usando lenguajes orientados al objeto
  2. La programación profesional será mucho más rigurosa
  3. La verificación estará integrada en el proceso de desarrollo
  4. Cada programa tendrá una interfaz web
  5. La concurrencia estará en todas partes
  6. Tendremos más confianza en la evaluación de los objetivos
  7. La ingeniería de software no será solo un proceso, sino que una tecnología

Tomado de la presentación de Bertran Meyer "How you will be programming in 10 years"

La programación como un Arte (Parte V y final)

| No Comments | No TrackBacks

La programación de computadores como un arte

Por Donald Knuth, 1974

(Lee las partes IIIIII IV)

Menos Facilidades: Más Diversión

Una cosa bastante curiosa que he notado sobre la satisfacción estética es que nuestro placer es significativamente mayor cuando logramos algo con herramientas limitadas. Por ejemplo, el programa del que personalmente estoy muy contento y orgulloso es un compilador de que una vez escribí para una minicomputadora primitiva, que sólo tenía 4096 palabras de memoria, 16 bits por palabra. Una persona se siente como un virtuoso real al lograr algo con tan severas restricciones.

Un fenómeno similar ocurre en muchos otros contextos. Por ejemplo, las personas parecen más a menudo enamorarse de su Volkswagen, pero rara vez con su Continental Lincoln (que probablemente funcionen mucho mejor). Cuando aprendí programación, era un pasatiempo popular hacer todo lo posible con programas que cupieran sólo en una tarjeta perforada. Supongo que es el mismo fenómeno que hace que los entusiastas de APL gocen con sus "one-liners". Cuando enseñamos a la programación hoy en día, es curioso el hecho de que rara vez se captura el corazón de un estudiante de ciencias de la computación hasta que ha tomado un curso que le permite "meter las manos" y ganar experiencia con un miniordenador. El uso de nuestras máquinas a gran escala con sus sistemas operativos y lenguajes de fantasía en realidad no parece generar ningún amor por la programación, por lo menos no al principio.

No está claro cómo aplicar este principio para aumentar el disfrute de los programadores en su trabajo. Seguramente los programadores gruñrán si su administrador de repente les anuncia que la nueva máquina tendrá sólo la mitad de memoria que la antigua. Y yo no creo que nadie, ni siquiera los más dedicados "artistas de programación", vayan a dar la bienvenida a esta perspectiva, ya que a nadie le gusta perder las instalaciones de manera innecesaria. Otro ejemplo puede ayudar a aclarar la situación: Los cineastas se opusieron firmemente a la introducción del cine sonoro en la década de 1920 porque estaban orgullosos de la forma en que podían transmitir palabras sin sonido. Del mismo modo, un artista de programación bien podría resentirse de la introducción de equipos más poderosos, los dispositivos de almacenamiento masivo de hoy en día suelen echar a perder gran parte de la belleza de nuestros viejos métodos de ordenamiento en cintas. Pero los cineastas de hoy no quieren volver a las películas mudas, no porque sean perezosos, sino porque saben que es muy posible hacer películas bellas utilizando tecnología mejorada. La forma de su arte ha cambiado, pero todavía hay mucho espacio para el arte. 

¿Cómo desarrollaron estas habilidades? Los mejores realizadores de películas a través de los años por lo general parecen haber aprendido su arte en relativamente primitivas circunstancias, a menudo en países con una industria cinematográfica limitada. Y en años recientes las cosas más importantes que hemos estado aprendiendo sobre programación parecen tener su origen en personas que no tienen acceso a computadores muy grandes. La moraleja de esta historia, me parece, es que debemos hacer uso de la idea de recursos limitados en nuestra propia educación. Todos podemos beneficiarnos de hacer de vez en cuando "programas de juguete", cuando las restricciones artificiales se establecen, de manera que nos vemos obligados a impulsar nuestra capacidad hasta el límite. No debemos vivir en el regazo del lujo todo el tiempo, ya que que tiende a ponernos letárgicos. El arte de la lucha contra los mini problemas con toda nuestra energía va a agudizar nuestro talento para los problemas reales, y los la experiencia nos ayudará a obtener más placer de nuestros logros en equipos menos restringidos.

En una vena similar, no debemos eludir "el arte por el arte", no debemos sentirnos culpables por los programas que son sólo para divertirse. Una vez logre un gran placer alescribir un programa de ALGOL de un sóla sentencia que invocaba un procedimiento de product interno de una manera tan inusual que calculaba el m-ésimo número primo, en lugar de un producto interno. Hace algunos años los estudiantes de Stanford estaban entusiasmados por encontrar el programa más corto en FORTRAN que se imprimiera a sí mismo, en el sentido de que la salida del programa es idéntico al texto de su propia fuente. El mismo problema fue considerado para muchos otros lenguajes. No creo que fuera una pérdida de tiempo para que puedan trabajar en esto, ni que Jeremy Bentham, a quien ya he citado antes, negará la "utilidad" de tales pasatiempos. "Por el contrario," él escribió, "no hay utilidad de que sea más incontestable. ¿En qué se da el carácter de una utilidad, si no es una fuente de placer?


Proveyendo Herramientas Hermosas

Otra característica del arte moderno es su énfasis en la creatividad. Parece que muchos artistas hoy en día no parece importarle tanto la creación de cosas bellas; sólo la novedad de una idea es lo importante. No estoy recomendando que la programación de computadoras deba ser como el arte moderno en este sentido, pero me lleva a una observación que me parece importante. A veces nos asignan una tarea de programación que es casi irremediablemente aburrida, lo que no nos exige en absoluto nada de creatividad, y en esos momentos las personas suelen venir a mí y me dicen: "¿Así que la programación es hermosa? Está muy bien para que usted daclame que uno debería tener el placer de la creación de programas elegantes y encantadores, pero ¿cómo se supone que voy a hacer de este desastre una obra de arte? "

Bueno, es cierto, no todas las tareas de programación van a ser divertidas. Tengan en cuenta a la "ama de casa atrapada", que tiene que limpiar la misma mesa cada día: no hay espacio para la creatividad o el arte en cada situación. Pero incluso en esos casos, hay una manera de hacer una gran mejora: todavía es un placer hacer trabajos de rutina, si tenemos cosas bellas para trabajar. Por ejemplo, una persona puede realmente disfrutar el limpiar la mesa del comedor, día tras día, si es una mesa hermosamente diseñada, a partir de madera de fina calidad.

Por lo tanto, quiero dirigir mis palabras de clausura a los programadores de sistemas y los diseñadores de máquinas que producen los sistemas conque el resto de nosotros debe trabajar. Por favor, déjenos herramientas que sean un placer de usar, especialmente para nuestras tareas de rutina, en lugar de ofrecer algo con lo que tengamos que luchar. Por favor, déjenos herramientas que nos animen a escribir mejor los programas, mediante la mejora de nuestro placer cuando lo hacemos.

Es muy dificil para mí convencer a jóvenes universitarios de que la programación es hermosa, cuando tengo que decirles que es como "slash slash JOB es igual a esto y esto." Aún los lenguajes de control pueden ser diseñados de modo que sea un placer usarlos, en vez de ser estrictamente funcionales.

Los diseñadores de hardware de computador pueden hacer que sus máquinas sean mucho más agradables de utilizar, por ejemplo, proporcionando aritmética de coma flotante que cumpla las leyes matemáticas simples. Las instalaciones disponibles en la actualidad en la mayoría de las máquinas hacen el trabajo de análisis de errores de manera rigurosa e irremediablemente difícil, pero las operaciones de diseño adecuado sería alentar a los analistas numéricos que proporcionaran una mejor subrutinas que hayan certificado la exactitud.

Vamos a considerar también lo que los diseñadores de software pueden hacer. Una de las mejores maneras de mantener arriba el espíritu de un usuario del sistema es proporcionar  las rutinas con las que se pueda interactuar. No debemos hacer que los sistemas sean demasiado automáticos, por lo que la acción va siempre detrás de las escenas, debemos dar el programador-usuario la oportunidad de dirigir su creatividad hacia canales útiles. Una cosa que todos los programadores tienen en común es que les gusta trabajar con las máquinas, así que vamos a mantengámoslo en el ciclo. Algunas de las tareas se hacen mejor con la máquina, mientras que otros se hacen mejor con la supervisión humana, y en un sistema bien diseñado se encuentra el equilibrio adecuado. (He estado tratando de evitar la automatización mal dirigida durante muchos años.)

Programar instrumentos de medición es un buen caso de ejemplo. Durante años, los programadores han sido inconscientes de cómo los costos reales de computación se distribuyen en sus programas. La experiencia indica que casi todo el mundo tiene una idea equivocada acerca de los verdaderos cuellos de botella en sus programas, no es de extrañar que los intentos de mejorar la eficiencia vayan mal tan a menudo, cuando a un programador no se le da un desglose de los gastos de acuerdo a las líneas de código que ha escrito. Su trabajo es algo así como la de una pareja de recién casados que intentan planificar un presupuesto equilibrado sin saber cuales serán los costos de los elementos individuales, como alimentos, vivienda y ropa. Todo lo que hemos estado dando a los programadores es un compilador de optimización, que misteriosamente hace algo a los programas que se traduce, pero que nunca explica lo que hace. Afortunadamente ahora estamos finalmente viendo la aparición de sistemas que le dan el crédito de usuario para una cierta inteligencia, sino que proporciona automáticamente la instrumentación de programas y la información apropiada sobre los costos reales. Estos sistemas experimentales han sido un gran éxito, ya que producen mejoras mensurables, y sobre todo porque es divertido de usar, así que confío en que es sólo cuestión de tiempo antes de que el uso de estos sistemas sea un procedimiento operativo estándar. Mi artículo en Computer Survey  discute esto aún más, y presenta algunas ideas para otras formas en que una rutina interactiva apropiada puede mejorar la satisfacción de los programadores usuarios.

Los diseñadores del lenguaje también tienen la obligación de proporcionar los lenguajes que fomenten un buen estilo, ya que todos sabemos que el estilo es fuertemente influenciado por el lenguaje en el que se expresa. La oleada actual de interés en la programación estructurada ha puesto de manifiesto que ninguno de nuestros lenguajes existentes es realmente idóneo para abordar el programa y la estructura de datos, ni tampoco está claro como debe ser un lenguaje ideal. Por lo tanto, espero que muchos hagan experimentos cuidadosos en el diseño el lenguaje durante los próximos años.

Resumen

En resumen, hemos visto que la programación de computadoras es un arte, porque se aplica el conocimiento acumulado en el mundo, porque requiere habilidad e ingenio, y especialmente, debido a que produce objetos hermosos. Un programador que inconscientemente, se ve a sí mismo como un artista disfrutará de lo que hace y lo hará mejor. Por lo tanto, podemos alegrarnos de que la gente que da charla en las conferencias de computación hable sobre  estado del arte.

La programación como un Arte (Parte IV)

| No Comments | No TrackBacks

La programación de computadores como un arte

Por Donald Knuth, 1974

(Lee las partes III, y III)

Obras de Arte

Cuando estoy sentado en la audiencia escuchando una larga charla, mi atención usualmente empieza a desvanecerse en este punto, cerca de la hora. Así que ¿me pregunto si se están cansando de mi arenga sobre "ciencia" y "arte"? Realmente espero que sean capaces de escuchar atentamente el resto de esto, de todos modos, porque ahora viene la parte sobre la cual tengo sentimientos más profundos.

Cuando hablo de programar computadores como un arte, estoy pensando primariamente al respecto como una forma de arte, en un sentido estético. La principal meta de mi trabajo como educador y autor es ayudar a la gente a aprender como escribir programas hermosos. Es por esta razón que me he sentido satisfecho que mis libros actualmente aparezcan en la Biblioteca de Bellas Artes de la Universidad de Cornell. (Aunque, los tres volumenenes permanecen en la librera, sin ser usados, así que me temo que el bibliotecario pueden haber cometido un error al interpretar mi título literalmente.)

Mi sentimiento es que cuando preparo un programa, es como componer poesía o música, como dijo una vez Andrei Ershov, programar nos puede dar ambas una satisfacción intelectual y emocionale, porque es un verdadero logro dominar la complejidad y establecer un sistema de reglas consistentes.

Más aún, cuando leemos los programas de otras personas, podemos reconocer algunos de ellos como genuinas obras de arte.  Puedo aún recordar la gran emoción que fue para mí leer el listado del programa en assembler de Stan Poley SOAP II en 1958, probablemente ustedes pensarán que estoy loco, y los estilos ciertamente han cambiado grandemente desde entonces, pero en ese tiempo era un gran asunto para mí ver cuan elegante un programa de sistema podía ser, especialmente al compararlo con los pesados códigos que encontré en otros listados que estudiaba en ese tiempo. La posibilidad de escribir un programa hermoso, aún en lenguaje ensamblador, es lo que me dejó enganchado con la programación en primer lugar.

Algunos programas son elegantes, algunos exquisitos, otros son chispeantes. 
Some programs are elegant, some are exquisite, some are sparkling. ¡Mi afirmación es ue es posible escribir grandes programes, programas nobles, algunos realmente magníficos!

Gusto y Estilo

La idea del estilo en programación está apareciendo al fin, y espero que muchos de ustedes ya han visto el excelente pequeño libro sobre Los Elementos del Estilo de Programación, de Kernighan and Plauger. En esta conexión es importante para todos nosotros recordar que no hay un "mejor" estilo, todos tienen sus propias preferencias, y es un error tratar de forzar a la gente a adaptarse aun molde no natural. A menudo escuchamos decir, "No se nada sobre arte, pero sé lo que quiero." Lo importante es que a tí realmente te guste el estilo que estás usando, debería ser la mejor manera en que prefieres expresarte.

Edsger Dijkstra hizo hincapié en este punto en el prefacio a su Corta Introducción al Arte de Programar:

Es mi propósito transmitir la importancia del buen gusto y estilo en la programación, [pero] los elementos específicos de estilo presentados sirven sólo para ilustrar los beneficios que pueden ser derivados del "estilo" en general. Al respecto me siento afin al profesor de composición en el conservatorio: el no les enseña a sus pupilos como componer una sinfonía particular, el debe ayudar a sus pupilos a encontrar su propio estilo y debe explicarles las implicancias de esto. (Es esta analogía la que me hace hablar del "Arte de la Programación".)
Ahora debemos preguntarnos, ¿qué es buen estilo, y qué es mal estilo? No deberíamos ser demasiados rígidos sobre esto cuando juzguemos el trabajo de otras personas. El filósofo de principios del siglo diecinueve Jeremy Bentham lo puso de esta manera:

Los jueces de la elegancia y del gusto se consideran a si mismos como benefactores de la raza humana, cuando en realidad sólo son los interruptores del placer... No existe el gusto que merezca el epíteto de bueno, a menos que sea un gusto empleado de tal forma, que el placer actualmente producido por este, al unirlo con algo contingente o futuro sea de utilidad; no hay gusto que merezca ser llamado malo, a menos que sea un gusto ocupado para algo ue tenga una tendencia perjudicial
Cuando aplicamos nuestros prejuicios para "reformar" el gusto de alguien más, estamos inconcientemente negándole algo de su enteramente legítimo placer. Es por esto que no condeno un montón de las cosas que hacen los programadores, aunque nunca disfrutaría haciéndolas yo mismo. Lo importante es que ellos están creando algo que ellos sienten que es hermoso.

En el pasaje que he citado, Bentham nos da algunos consejos sobre ciertos pincipios de estética que son mejores ue otros, principalmente la "utilidad" del resultado. Tenemos algo de libertad al definir nuestros estándares personales de belleza, pero es especialmente agradable cuando las cosas que consideramos bellas son también consideradas por otra gente como útiles. Debo confesar que realmente disfruto escribiendo programas computacionales, y especialmente disfruto escribiendo programas que hacen el más grande bien, en algún sentido.

Hay muchos sentidos en los cuales un programa puede ser "bueno", por cierto. En primer lugar, es especialmente bueno tener un programa que opera correctamente. En segundo lugar es a menudo bueno tener un programa que no sea dificil de modificar, cuand llegue el tiempo de las adaptaciones. Ambas metas se pueden lograr cuando el programa es facilmente legible y entendible por una persona que conoce el lenguaje apropiado.

Otra forma importante para que un programa en producción sea bueno es que interactúe  amablemente con sus usuarios, especialmente cuando se recupera de los errores humanos en el ingreso de los datos.  Es un real arte componer mensajes útiles o diseñar formatos de entrada flexibles que sean a prueba de errores.

Otro aspecto importante de la calidad de un programa es la eficiencia con la cual los recursos del computador son usados. Lamento decir que mucha gente en estos días están condenando la eficiencia de los programas, dicéndonos que es de mal gusto. La razón para esto es que ahora estamos experimentando una reacción al tiempo en que la eficiencia era el único criterio reputable de calidad, y los programadores en el pasado estaban tan preocupados con la eficiencia que produjieron código innecesariamente complicado, el resultado de esta complejidad innecesaria ha sido que la eficiencia neta ha disminuido, debido a las dificultadoes de depurar y mantener.

El problema real es que los programadores han gastado mucho tiempo preocupándose de la eficiencia en los lugares equivocados en los momentos incorrectos, la optimización prematura es la raiz de todos los males 
(o al menos de la mayoría de ellos) en programación.

No deberíamos recompensar al sabio y aporrear al tonto, ni estar pensando siempre en términos cuanto porcentaje ganamos o perdimos en el tiempo total de ejecución o espacio. Cuando compramos un auto, muchos de nosotros ignoramos una diferencia de $50 o $100 en el precio, mientras que si somos capaces de hacer un viaje especial a una tienda particular para comprar un objeto de 50 centavos, en oferta a 25 centavos. Mi punto es que hay un momento y un lugar para la eficiencia, he discutido su rol en mi artículo sobre la programación estructurada, que aparecerá en la edición actual de Computing Surveys [Knuth, Donald E. Structured programming with go to statements. Computing Surveys 6 (Dec. 1974)].

La programación como un Arte (Parte III)

| No Comments | No TrackBacks

La programación de computadores como un arte

Por Donald Knuth, 1974

(Lee las partes I, y II)

Ciencia y Arte

Nuestra discusión indica que la programación de computadores es para ahora ambas: una ciencia y un arte, y que los dos aspectos se complementan bien una con otra. Aparentemente muchos autores que examinan tal pregunta llegan a la misma conclusión, que su área de estudio es ambas una ciencia y un arte, cualquiera sea esta área. Encontré un libro sobre fotografía elemental, escrito en 1893, que decía "el desarrollo de la imagen fotográfica es ambas un arte y una ciencia." De hecho, cuando tomé primero un diccionario para estudiar las palabras "arte" y "ciencia" le eché un vistazo al prefacio del editor, el que empezaba diciendo, "la construcción de un diccionario es una ciencia y un arte a la vez". El editor del diccionario de Funk & Wagnall's dictionary observaba que la penosa acumulación y clasificación de los datos sobre las palabras tiene un carácter científico, mientras que un fraseo bien escogido para la definición demanda la habilidad de escribir con la economía y la precisión: "La ciencia sin este arte es probablemente inefectiva, el arte sin la ciencia es ciertamante inexacto."

Cuando preparaba esta charla miré las tarjetas del catálogo en la biblioteca de Standford para ver como otras personas han usado las palabras "arte" y "ciencia" en los títulos de sus libros. Esto resultó bastante interesante.

Por ejemplo, encontré dos libros titulados "El arte de Tocar el Piano" y otros llamados La Ciencia de la Técnica del PianoforteLa Ciencia de la Práctica del Pianoforte. Hay también un libro llamado El Arte de tocar Piano: Una Aproximación Científica.

Entonces encontré un simpático librillo titulado El Gentil Arte de las Matemáticas, el que me puso algo tristre ya que honestamente no puedo describir la programación de computadores como un "arte gentil". He sabido por muchos años de un libro llamado El Arte de la Computación, publicado en San Francisco en 1879, por un hombre llamado C. Frusher Howard. Era un libro sobre aritmética práctica para los negocios que ha vendido sobre 400.00 copias en varias ediciones hacia 1890. Me asombré al leer el prefacio, pues muestra que la filosofía de Howard y la intención del título era bastante diferente de la mía, el escribe: "El conocimiento de la ciencia de los números es de la menor importancia, el arte de ajustar las cuentas es absolutamente indispensable."

Varios libros mencionan a ambas la ciencia y el arte en sus títulos, notablemente La Ciencia del Ser y el Arte de Vivir por el Maharishi Mahesh Yogi. Hay también un libro llamado El Arte del Descubrimiento Científico, el que analiza como algunos de los grandes descubrimientos de la ciencia se han hecho.

Suficiente de la palabra "arte" en su sentido clásico. Realmente cuando escogí el título de mis libros, yo no estaba pensando principalmente en arte en este sentido, estaba pensando más en sus connotaciones actuales. Probablemente el libro más interesante que inició mi búsqueda fue un trabajo reciente de Robert E. Mueller llamado La Ciencia del Arte. De todos los libros que he mencionado, el de Mueller es el que más se acerca a expresar lo que quiero hacer el tema central de mi charla hoy, en términos artísticos reales tales como entendemos ahora el término. El observa: "Una vez se pensó que las perspectivas imaginativas del artista eran la muetre del científico. Y la lógica de la ciencia parecía significar la perdición a todos los posibles vuelos de la imaginación artística." El continúa explorando las ventajas que resultan de la síntesis de la ciencia y el arte.

Una aproximación científica es caracterizada generalmente por las palabras lógica, sistemática, impersonal, calma, racional, mientras que la aproximación artística se caracteriza por la palabras estética, creativa, humanitaria, ansiosa, irracional. Me parece que esta aparente contradicción de aproximaciones tiene gran valor con respecto a la programación de los computadores.

Emma Lehmer escribió en 1956 que había encontrado que  la programación era "una ciencia exacta junto con un arte intrigante." H.S.M. Coxeter recordaba en 1957 que a veces se sentía "más como un artista que como un científico".  Estos eran los tiempos en que .P. Snow empezó a vocear su alarma sobre la creciente polarización de "las dos culturas" entre la gente educada. El apuntó que necesitamos combinar los valores científicos y artistico y queremos hacer progresos reales.

Ingresa tu dirección de correo electrónico:

Despachado por FeedBurner

<

Distribución

Sobre este archivo

This page is an archive of recent entries in the Programación category.

Privacidad is the previous category.

Seguridad is the next category.

Contenido reciente en el indice principal o busque en los archivos para encontrar todo el contenido.

 

Blogalaxia
OpenID accepted here Learn more about OpenID