Archive for the 'SparkSharp' Category

SparkSharp, Spark in C# (2) Implementando Map y Reduce

Monday, May 25th, 2015

Anterior Post

El proyecto está en:

https://github.com/ajlopez/SparkSharp

Como es usual, lo estoy desarrollando siguiendo el flujo de TDD (Test-Driven Development), así que el código va evolucionando a medida que encuentro nuevas soluciones a pequeños casos de uso planteados por los tests. Lo que muestro hoy puede cambiar mañana, al necesitarse en nuevos tests nueva funcionalidad o nuevas implementaciones. Por ejemplo, en el proyecto original de Apache Spark los dataset se crean desde métodos factorías en algo llamado Spark Context. Pero esa funcionalidad todavía no la necesité. Así que por ahora mis tests apuntan a crear simples objetos Dataset, directamente con el operador new, y a consumirlos.

Actualmente, nacida en algún refactor, está la clase abstracta BaseDataset. Parte del código:

public abstract class BaseDataset<T> : IEnumerable<T>
{
    public abstract IEnumerable<T> Elements { get; }

    public BaseDataset<S> Map<S>(Func<T, S> map)
    {
        return new EnumDataset<S>(this.ApplyMap(map));
    }

    public S Reduce<S>(Func<S, T, S> reduce)
    {
        S result = default(S);

        foreach (var elem in this)
            result = reduce(result, elem);

        return result;
    }
    
    // ...

    private IEnumerable<S> ApplyMap<S>(Func<T, S> map)
    {
        foreach (var elem in this)
            yield return map(elem);
    }
    
    // ...
}

Vemos que la implementación de enumerar los elementos que contiene queda delegada a la clase concreta. Pero en el código de arriba dejé la implementación base de los métodos Map y Reduce. Gracias a C#, estos métodos pueden recibir un lamba, o una Func (una función como objeto) como veremos en algún test.

Vean como el ApplyMap usa el yield de C# para devolver cada elemento, y el foreach sólo se vuelve a ejecutar cuando el consumidor del IEnumerable necesita el próximo item. Tanto el uso de lambdas como de yield han simplificado la implementación de estas ideas. Esta es una prueba de lo que se necesita en un lenguaje para cumplir mejor con alguna necesidad. Como digresión, comento que me parece que la historia de C# ha sido bastante acertada, incorporando estas ideas, mientras que en el mundo Java, se ha dado el caso de lenguajes como Scala que, siendo un gran lenguaje, me parece que trata de sumar demasiadas cosas.

No se ejecutan tests sobre la clase abstracta (que de nuevo, vean la historia de commits, nació como un refactor), sino sobre alguna concreta. Siendo EnumDataset una clase concreta (a examinar en próximos posts), sea un test típico del Map:

[TestMethod]
public void MapIncrement()
{
    EnumDataset<int> ds = new EnumDataset<int>(new int[] { 1, 2, 3 });
    BaseDataset<int> mapds = ds.Map(i => i + 1);
    var enumerator = mapds.GetEnumerator();

    for (int k = 1; enumerator.MoveNext(); k++)
        Assert.AreEqual(k + 1, enumerator.Current);

    Assert.AreEqual(3, mapds.Count());
}

Y un test del Reduce:

[TestMethod]
public void ReduceSum()
{
    EnumDataset<int> ds = new EnumDataset<int>(new int[] { 1, 2, 3 });
    var result = ds.Reduce<int>((x, y) => x + y);

    Assert.IsNotNull(result);
    Assert.AreEqual(6, result);
}

Próximos temas: más métodos de BaseDataset, datasets concretos, datasets con clave, etc…

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

SparkSharp, Spark in C# (1) Primeras ideas

Monday, May 18th, 2015

Siguiente Post

En el post de ayer mencionaba el uso de Spark por parte de gente de Medallia. Conocía un poco de ese proyecto Apache, pero hoy me puse a ver la interfaz de programación que tienen, y me pareció interesante reproducir alguna parte en C#.

Ellos trabajan con Dataset, conjuntos de datos que pueden venir, por ejemplo, de archivos locales o distribuidos, y a los que aplican funciones de transformación (Map) y funciones de reducción, obtención de algún valor resultante (Reduce). Los trabajos definidos se pueden correr en varios nodos (tengo que revisar cómo consiguen consolidar la etapa de Reduce final).

Pero para comenzar una implementación en C#, me parece interesante comenzar en pequeño, y como es usual, usar el flujo de trabajo de TDD (Test-Driven Development). Así que ni lerdo ni perezoso, en la tarde de ayer comencé este proyecto:

https://github.com/ajlopez/SparkSharp

Si ven los primeros commits, se siguió la idea de flujo de TDD. Va quedando que los datasets implementan como base a un IEnumerable<T> sobre el que se aplican de forma ordenada funciones Map, Reduce, Split, Take, Skip y otras que vayan apareciendo. Esos datasets pueden ser simples “wrappers” de otros IEnumerable<T> (como un arreglo de tipo T, o una lista), o pueden venir de tomar un texto y partirlo en líneas (ver el TextDataset), o tomar un archivo y procesarlo en líneas.

Todos esos datasets son, digamos, locales, no distribuidos. El próximo paso simple (siempre hay que ir por lo más simple) es exponer un dataset cualquiera para que se pueda consumir ordenadamente de forma remota. Por ejemplo, en un nodo/máquina podemos tener un gran archivo de texto a analizar. Queremos procesar sus líneas. Para esto hoy ya en el proyecto está el TextFileDataset, que procesa las líneas a medida que se van leyendo. Pero se podría implementar un ServerDataset o RestDataset, que sea un “wrapper” sobre ese dataset local, y se exponga para afuera, mediante TCP o una API que devuelva JSON o un simple string via HTTP. Entonces, distintas clases clientes (me imagino RestClientDataset, o ServerClientDataset), podrán consumir esos datos desde nodos remotos, como si fueran datasets locales. En el caso normal, un TextFileDataset expondría sus líneas a los nodos remotos, para que se puedan consumir, pero de una forma controlada: cada línea iría al próximo nodo que pida un item del dataset.

Despues de implementar la exposición de un dataset local como remoto (con ese cuidado de que cada item SOLO vaya a un nodo solicitante, que no se REPITA el proceso de un item entregándolo a DOS o más nodos), implementar la serialización/deserialización (temas ya encarados en AjErl y Aktores), automáticamente comenzamos a tener procesamiento distribuido. Claro que todo esto es el caso feliz: si el proyecto progresa, habrá que contemplar fallas en la comunicación, ingreso y egreso de nodos dinámicamente en el medio del proceso, coordinación/recolección de datos en un Reduce final que consuma resultados parciales de varios nodos, etc.

Pero piano, piano se va a lontano. Por ahora, baby steps, la neurona atenta, vermú con papas fritas, y good show!

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez