lunes, 11 de junio de 2018

Golang, Ruby y mi decisión de aprender uno de ellos

Este artículo no es una disertación técnica acerca de ambos lenguajes y cuál es mejor o peor, simplemente es el relato de mis razones personales y muy subjetivas para haber tomado la decisión de aprender uno de ellos.

Siempre me ha gustado programar, aprender nuevas tecnologías, lenguajes y herramientas, de hecho esa es la razón por la que me encontré en el mundo de la informática a muy temprana edad, programando Basic en un XT: simplemente quería saber que podía hacer con aquello que estaba aprendiendo.

Recientemente (hace un par de meses) estaba leyendo acerca de Golang y sus ventajas, lo mismo con Ruby entonces decidí que era momento de aprender algo nuevo, aclaro que llevo 7 u 8 años usando Python y no pretendo dejarlo, sin embargo pienso que es bueno tener otra herramienta disponible en caso de necesitarla.

Aprender ambos además de tener que trabajar y balancear el resto de mi vida personal resulta complicado entonces tenía que tomar una decisión y de eso se trata este artículo, de mi proceso mental y personal para decidir cual usar.

En primer lugar quiero dejar claro que no se trata de cuál es mejor o más rápido, es un asunto de conveniencia. Con los lenguajes de programación modernos no se trata de "cuál es mejor", se trata de cuál te conviene más: cuál se hace más familiar, fácil de aprender, te conviene en el trabajo, proyectos personales, ayudan a moldear tu carrera profesional o simplemente el que parezca más divertido.

Al final mi decisión fue Golang y no es que Ruby tenga algo malo, de hecho la decisión estuvo bastante cerrada, pero Ruby como lenguaje tiene más o menos las mismas características que Python: es interpretado, fácil de aprender, etc.

¿Por qué Golang? Debo admitir que a penas comencé a entender su sintaxis me sentí algo nostálgico al recordar cuando programaba en C y creo que tiene sentido porque entre sus creadores están Ken Thompson[1] y Rob Pike[2] (ceadores del Lenguaje B, participaron en la creación de Unix y como si fuera poco padres del UTF8), tiene sentido la similitud.

Dejando la nostalgia a un lado, ¿qué tiene Golang para gustarme tanto? Entre los detalles que me parecieron más llamativos está el hecho de que es compilado, lo que hace muy fácil su distribución, es el chico nuevo de la cuadra por lo que se espera que evolucione de manera significativa y las goroutines[3].


Para quienes estén pensando que todo lo que mencioné lo puedo hacer también con X lenguaje o usando Y herramienta de Ruby o Python, lo sé, pero no es el punto.


[1]https://es.wikipedia.org/wiki/Ken_Thompson
[2]https://es.wikipedia.org/wiki/Rob_Pike
[3]https://golangbot.com/goroutines/

martes, 30 de agosto de 2016

¿El blog murió?

Más de dos años después, saludos mis estimados. No, no ha muerto, sólo estaba de parranda (como dice la canción).

Entre el trabajo, la pereza y una gran cantidad de cosas había abandonado mi pasión por escribir y enseñar, tengo como meta comenzar a redactar al menos un par de artículos al mes. Sí, un par...comencemos por metas pequeñas, luego crecemos si es necesario.

¿Seguiremos manteniendo la misma temática? Sí, Python, Linux, software libre, programación, tecnología, web, etc, etc.


martes, 28 de enero de 2014

Comenzando el año

Antes que otra cosa quiero desearles un prospero año nuevo.

Comenzamos el año con nuevos proyectos, metas y ganas de hacer algo productivo (aún más). Este año sí mantendré más actualizado mi blog y no es promesa típica de año nuevo (como las dietas, metas irreales, etc). El asunto de la paternidad me ha tenido ocupado, sí ya soy padre de un hermoso niño que tiene 10 meses, pesa unos 12 Kgs y es muy inquieto.

Por ahí les tengo el resultado de la decisión que tomé entre Fedora y Manjaro, además estoy aprendiendo Qt usando C++ (ya sé C++, pero no había usado Qt), además esto se puede integrar con Python también, que como ya saben es mi lenguaje predilecto.

No creo que escriba algo muy avanzado de Qt, obviamente estoy comenzando con todo ese proceso, sin embargo espero retomar los artículos de programación paralela, retocarlos donde haga falta y continuar con la serie:

Parte 1
Parte 2

Saludos y éxito.

martes, 25 de junio de 2013

Dejando Ubuntu

Para ser más específico, Xubuntu, es la distro que uso actualmente y que estoy a punto de abandonar ¿Por qué? ¿a cual me cambio? De eso se trata este artículo.

Como ya saben, quienes leyeron la primera entrada del blog,  mi trabajo principalmente es como programador, aunque también estoy a cargo de algunos servidores, pero mi rama principal es el desarrollo de aplicaciones  por ello me gusta una distribución que sea simple de usar, rápida y sobre todo estable. Para ser más eficiente necesito una distro que me permita enfocarme en lo que realmente es mi trabajo y no en mantenerla funcionando.

Mi historia con Ubuntu comienza con la versión 5.04, mi distro de escritorio era Suse (antes de ser OpenSuse), pero Ubuntu me pareció tan genial que decidí adoptarla para mi uso diario (aunque no tenía quejas de Suse). Con el tiempo comencé a contribuir reportando bugs, usaba las primeras alfas, las betas y podía ver la evolución de las distintas versiones.

Al pasar el tiempo comienzo a notar que en cada nueva versión hay menos errores que se corrigen, las versiones definitivas son bastante inestables, al punto que instalarlas resultaba toda una odisea, quienes intentaron hacer una instalación gráfica con una tarjeta de video NVidia saben de lo que hablo, luego los informes interminables de errores que ahora parecen formar parte de la decoración del escritorio:


Actualmente estoy usando Xubuntu 13.04, pero en la versión anterior también aparecía constantemente el ícono en el escritorio y si se fijan bien no aparece el network-manager, esto es debido a que tuve que desinstalarlo para poder conectarme a la red (irónico, ¿verdad?)

Cuando Ubuntu adoptó Gnome 3 y Unity decidí cambiarme a Kubuntu, luego de varias experiencias que me llenaron de frustración, muchos widgets, efectos y detalles que no me gustaron cambié a Xubuntu. XFCE con todas las ventajas de Ubuntu, básicamente estuve saltando entre distintos escritorios, pero la misma distro.

Durante el primer año aproximadamente pude manejar la situación, pero ahora la razón para querer cambiar no era el gestor de escritorios, no eran los efectos, la razón para querer cambiar era la distro en sí misma. Siento que pierdo demasiado tiempo tratando de que algunas cosas funcionen, pero ¿para qué seguir hablando mal de una distro con la que pasé tan buenos momentos?

¿Qué busco? 


  • Estabilidad
  • Simplicidad
  • Velocidad
  • Repositorios con multitud de aplicaciones
  • Amistosa con los equipos de escritorio y portátiles
  • Buena documentación
  • Fácil de usar, instalar y configurar

Aparentemente la mayoría de las distribuciones encaja en estas características, pero he probado unas cuantas recientemente y mis opciones se reducen a dos:


Esta es la parte en la que algunos lectores se preguntarán "¿por qué no Linux X o Y Linux?" Porque de todas las que probé, estas dos son las que a mi criterio cumplen mejor las características que menciono anteriormente.

Es importante resaltar que esta elección es completamente personal ya que seré yo quien use la distribución, pero me agrada tener opciones, es lo mejor del mundo libre en el que aún vivimos. Para llegar a esta lista de dos distribuciones hice lo que comentan en todos los foros, listas, artículos, etc, "Prueba hasta que consigas una que te guste" y es mi recomendación para el lector que todavía esté en proceso de tomar una decisión.

Ambas tienen una comunidad muy amplia, los repositorios son muy generosos, estables hasta donde pude probar, la documentación es amplia y detallada, ambas funcionaron de manera brillante desde el comienzo (aunque instalé la beta de Fedora 19 no tuve problema alguno).

Una vez tome alguna decisión comentaré mi elección y las razones para ello, en caso de que esto pueda servir de ayuda.

jueves, 15 de marzo de 2012

Introducción a la computación paralela (parte 2: el problema)

Continuando con la serie de artículos asociados a la computación paralela vamos a plantear un problema bastante simple, pero que nos servirá para desarrollar y aplicar los conceptos de computación paralela sin enfocarnos demasiado en "el problema".

El problema consiste en encontrar las raíces de una función usando el método de bisección. Este método no es el más eficiente para la tarea y justamente por ello lo usaremos, así se podrá apreciar aún más la mejora al momento de paralelizar las tareas.

Antes de continuar le recomiendo al lector que visite algunas páginas para que entienda el algoritmo, que es bastante simple.

Ahora bien, como función usaremos el coseno ya que como todos saben es una función periódica que corta el eje X (como dije antes, la idea es que "el problema" sea simple), como es costumbre lo desarrollaré en Python:
import math

def f(x):
    return math.cos(x)
    
def biseccion(f, a, b, nIter, eps = 0.0001):
    fa = f(a)
    fb = f(b)
    m = (a + b) / 2.0
    fm = f(m)
    n = 0
    while n <= nIter and math.fabs(fm) > eps:
        if fa * fm < 0:
            b = m
            fb = fm
        else:
            a = m
            fa = fm
        m = (a + b) / 2.0
        fm = f(m)
        n += 1
    res = \
        {
            'r' : m,
            'fr': fm,
            'iteraciones': n,
            'criterio': n > nIter and \
                        'iteraciones' or 'tolerancia'
        }
    return res
    
if __name__ == '__main__':
    r = biseccion(f, 0, 4*math.pi, 100)
    print r

Como podrán notar no está escrito de la manera más eficiente posible (hablando en términos de Python), sin embargo me interesa que sea fácil de entender. La función bisección recibe como parámetros la función a la cual le buscará las raíces, el intervalo de búsqueda [a, b], la cantidad máxima de iteraciones y por último la tolerancia, error o epsilon, como prefieran llamarlo, así tendremos ambos criterios de convergencia en caso de que el otro falle. La función retorna un diccionario con los valores obtenidos (la raíz, el valor de la raíz en el punto, el número de iteraciones y el criterio de convergencia.

Ahora bien, compliquemos un poco más el problema para hacerlo más retador para el cumputador. Supongamos que deseamos encontrar todas las raíces en un intervalo muy amplio, por ejemplo [-1000, 1000], el método de bisección solo nos va a encontrar una raíz ¿cómo buscamos las otras? Para responder a esto usamos el método de búsqueda incremental.

El método de búsqueda incremental, como su nombre lo indica, consiste en buscar haciendo pequeños incrementos por lo que al final nos quedará una serie de conjuntos más pequeños en los cuales hacer la búsqueda, esto no garantiza todas las raíces, sin embargo se aproxima bastante bien (al menos para el ejemplo que deseamos desarrollar).

El método nos quedaría como sigue:
def incremental(inicio, fin, nIntervalos, nIter):
    inc = (fin - inicio) / nIntervalos
    res = []
    for i in range(nIntervalos):
        r = biseccion(f, inicio, inicio + inc, nIter)
        r.update({'a' : inicio, 'b' : inicio+inc})
        res.append(r)
        inicio += inc
    return res
Recibe como parámetros el inicio y el fin del intervalo, el número de trozos en el que se va a cortar dicho intervalo y el número de iteraciones que se hará en cada búsqueda de las raíces. Nos retorna una lista de diccionarios con los resultados de cada ejecución del método de bisección, para las pruebas iniciales recomiendo usar parámetros más conservadores, pero hice una prueba de la siguiente manera:

inicio = time.time()
    r = incremental(-10000.0, 10000.0, 100000, 1000)
    print "Tiempo transcurrido: ", time.time() - inicio, "s"

Aquí una captura de htop mientras se ejecuta la aplicación:


El computador en el que hice las pruebas es un Intel Core i3 con dos núcleos HT (por eso se ven 4) de los cuales únicamente se está usando uno a su máxima capacidad.

Se llevó aproximadamente 64.4822211266 s. Pues bien, ahora nos preguntamos: ¿hace falta ejecutarlo de manera secuencial? ¿no podría aprovechar de mejor manera varios núcleos?

La respuesta es un rotundo "sí", pero la respuesta al "¿cómo?" la veremos en el siguiente artículo.

Retomando el Blog

Luego de unos meses de estar desaparecido por estos lados por razones académicas y profesionales decidí retomar el Blog, continuar con la serie de artículos relacionados a la computación paralela y distribuida, además de otros artículos con temas mencionados en el primer post del blog.

Espero tener más tiempo ahora y al menos culminar los artículos que están en borrador...

Saludos a todos y feliz blogging ;)

viernes, 24 de junio de 2011

Introducción a la computación paralela (parte 1)

La idea es crear una serie de artículos en los que podamos cubrir algunos aspectos básicos y no tan básicos de la computación paralela para que el lector tenga un punto de partida en esta área tan fascinante y continúe investigando por su cuenta. En este primer artículo comenzaremos con los conceptos básicos que nos permitirán abordar de mejor manera los siguientes posts de esta serie.

Hoy en día se ha vuelto tan común tener PC's o portátiles con más de un procesador, núcleo o core (usen el que más les guste) por esto resulta importante explotar de manera adecuada esta ventaja, sobre todo con aplicaciones que consumen mucho tiempo de procesador como aplicar filtros y efectos complejos con Gimp, renderización de imáges, cálculos intensivos como simulaciones, programas que hagan uso de las técnicas de computación emergente, métodos de elementos finitos, juegos, etc.

Para aquellos que trabajamos en el área y sobre todo si nos gusta "jugar" con las técnicas de la computación emergente aprovechar todo el poder de cálculo que nos brindan los computadores resulta ideal. ¿Por qué uso Python y no C/C++ si lo que necesito es aumentar el rendimiento al máximo? Todas las librerías matemáticas que usa Python están escritas en C (numpy por ejemplo) así que básicamente lo que hacemos es un script que interactúa con las funciones hechas en C. Ahora bien, en este sentido podríamos usar Perl o el lenguaje de su preferencia, mi preferencia es Python =)

Comencemos con una breve introducción a la computación paralela, como podemos leer en la wikipedia:
La computación paralela es una técnica de programación en la que muchas instrucciones se ejecutan simultáneamente. Se basa en el principio de que los problemas grandes se pueden dividir en partes más pequeñas que pueden resolverse de forma concurrente ("en paralelo"). 
Tratemos de explicar este concepto con un ejemplo sencillo, deseamos realizar una encuesta a 1000 personas acerca de un asunto X. ¿Nos podríamos nosotros mismos a entrevistar a las 1000 personas? o ¿podríamos a un grupo de empleados a hacerlo? Durante el día cada empleado entrevistará una cuantas personas, cada uno de ellos con un grupo distinto. Acabamos de paralelizar el trabajo, además llamada "paralelismo de datos":
Paralelismo de datos es un paradigma de la programación concurrente que consiste en subdividir el conjunto de datos de entrada a un programa, de manera que a cada procesador le corresponda un subconjunto de esos datos. Cada procesador efectuará la misma secuencia de operaciones que los otros procesadores sobre su subconjunto de datos asignado. En resumen: se distribuyen los datos y se replican las tareas.
Otro ejemplo que nos servirá más adelante es el típico ejemplo de un banco. Supongamos que hablamos ahora de un banco en el que tenemos una cola de personas esperando ser atendidas y 4 taquillas en las que serán de ello (Quad Core o X4 jejeje). ¿Tendría sentido poner a todas las personas en la misma taquilla? o ¿pasaríamos a cada persona por una taquilla diferente a medida que se desocupen? Supongo que la respuesta más evidente para nosotros es atender a los clientes a medida que se desocupen las taquillas.

Entonces terminamos con una cola de "jobs" o trabajos por hacer y 4 "workers" o trabajadores que se encargarán de atender estas peticiones. Nuevamente tenemos la parelización de los trabajos:



A medida que las taquillas o workers (como se les conoce en la computación paralela) se van desocupando atienden a los clientes o jobs que tengan pendientes con estos.

También tenemos el paralelizmo de tareas:
Paralelismo de tareas es un paradigma de la programación concurrente que consiste en asignar distintas tareas a cada uno de los procesadores de un sistema de cómputo. En consecuencia, cada procesador efectuará su propia secuencia de operaciones.
Para entender este concepto vamos a imaginar una fábrica automotriz, en todo momento se están haciendo asientos, puertas, chasis, motores, etc. Al mismo tiempo se realizan tareas diferentes en distintas partes de la fábrica, incluso en fábricas diferentes (lo que en nuestro contexto sería computación distribuida).

Aquí nos topamos con otro concepto interesante, el de la computación distribuida, podemos tener poderosos clústers de cálculo capaces de realizar muchas más operaciones de las que podríamos en un único computador, puede estar compuesto desde un par de computadores personales hasta una gran cantidad de computadores especializados. La idea principal tras de esto es distribuir las tareas no solo entre los procesadores, si no también entre distintos computadores.

Como se podrá imaginar se puede hacer una combinacion de estos tres coceptos para obtener el mejor rendimiento en una aplicación que consuma una gran cantidad de recursos, por ejemplo podríamos tener un cluster en el que le asignamos una tarea a cada nodo (computador en nuestro cluster) y este a su vez paraleliza la tarea en varias partes.

En los siguientes artículos desarrollaremos algunos ejemplos de cada tipo de paralización que mencionamos en este post (hay más tipos, pero son los principales) incluso de computación distribuida.