gRPC y “no gRPC” todo junto en el mismo proyecto

Una de las novedades que incluye Net Core 3 es el soporte para gRPC. ¿No conoces gRPC? Bueno, pues básicamente se trata del RPC de toda la vida, pero vestido a la moda, duchado y perfumado. Vamos, si te lees los puntos principales de la página oficial de gRPC (definición de servicio independiente del lenguaje, soporte de muchos lenguajes, streaming bi-direccionales) es como si volvieses unos cuantos años atrás y Don Box estuviese en la bañera vendiéndote SOAP. Y de todos modos, cuando hablábamos de SOAP ya era difícil no acordarse de CORBA (ya fuese el estándard o el de Microsoft xD).

Continúa leyendo gRPC y “no gRPC” todo junto en el mismo proyecto

Terminales y millones de colores: una historia complicada

Los que más o menos me seguís por Twitter, quizá os habréis enterado de que estoy escribiendo una librería cross-platform (netstandard2) para desarrollar aplicaciones de consola. Evidentemente no es la única, es simplemente otra más y puedo asegurar que me lo paso genial desarrollándola.

Uno de los objetivos principales cuando empecé era permitir usar true color (es decir 16 millones de colores) en aquellos terminales que lo soportan y la verdad es que la historia del soporte de colores en terminales da para un post… y aquí estamos 😉

Continúa leyendo Terminales y millones de colores: una historia complicada

C#: Structs de un solo campo como typedefs

No hace mucho me preguntaba si usar structs de un solo campo tenía alguna penalización respecto a usar, simplemente, una variable del tipo del campo. Es decir, me preguntaba si tener:

struct Sint {
   public int value;
}

Tenía alguna penalización al respecto de usar, simplemente, una variable int.

A nivel de memoria sospechaba que no: una struct ocupa lo mismo que la suma de todos sus campos, más los paddings que se agregan para que los campos estén alineados, más el padding final que se agrega para que, en el caso de un array, los elementos estén alineados. En el caso de una estructura que solo tenga un int esos paddings no existen (*):

namespace PaddingTest
{
    struct NoPadding
    {
        public int i1;
    }

    struct Padding
    {
        public bool b1;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"{sizeof(int)} vs {Marshal.SizeOf<NoPadding>()}");
            Console.WriteLine($"{sizeof(bool)} vs {Marshal.SizeOf<Padding>()}");
            Console.ReadLine();
        }
    }
}

Si ejecutas este código, lo más habitual es que veas la primera línea “4 vs 4” y la segunda línea “1 vs 4”. Eso es porque, a pesar de que un bool ocupa un solo byte, una estructura que contenga un bool ocupa 4 bytes, debido al padding para alinear la memoria. Esos alineamientos son importantes, ya que no tener los datos alineados supone una pérdida de rendimiento importante (el procesador debe efectuar operaciones adicionales).

(*) Cuando digo que usando un int esos paddings no existen me estoy refiriendo a mi arquitectura (x64). Eso es importante. Observa que no uso sizeof para medir el tamaño de una struct. Eso es porque no se puede. Las structs no tienen un tamaño predefinido (a diferencia de los tipos propios como Int32 que siempre son 32 bits). Eso es debido, precisamente a ese padding: el CLR puede decidir de empaquetar una estrucutra con distintos paddings en función de la arquitectura sobre la cual se ejecuta el programa y por ello, al no ser el tamaño constante, no podemos usar sizeof.

La otra pregunta era si el rendimiento afectaba. Encontré este post donde parece ser que demuestra que no. De todos modos el test que se hace en este post se limita a comprobar si acceder a un campo de la estructura penaliza. Pero yo quiero más que eso: yo quiero ver si recibir, crear y devolver la estructura tiene impacto en el rendimiento. En definitiva quiero comprobar la diferencia entre esas dos funciones:

static int IntOp(int i)
{
    var j = i + 10;
    return j;
}
static Sint SintOp(Sint i)
{
    var j = new Sint(i.Value + 10);
    return j;
}

La primera opera solo con ints y la segunda solo con mi estructura (que solo tiene un int). Así que elaboré un test que ejectuaba esas funciones 100 millones de veces:

struct Sint
{
    public readonly int Value;
    public Sint(int v) => Value = v;
}
class Program
{
    static void Main(string[] args)
    {
        const int MAX_LOOP= 100000000;
        const int MAX_REPS = 10;

        long[] intTimes = new long[MAX_REPS];
        long[] sintTimes = new long[MAX_REPS];

        for (var rep = 0; rep<MAX_REPS; rep++)
        {
            var (tint, tsint) = Test(MAX_LOOP);
            intTimes[rep] = tint;
            sintTimes[rep] = tsint;
        }

        for (var idx=0; idx < MAX_REPS; idx++)
        {
            var diff = sintTimes[idx] - intTimes[idx];
            var percent = ((double)diff / (double)intTimes[idx]) * 100;
            Console.WriteLine($"int took {intTimes[idx]}. Sint took {sintTimes[idx]}. Diff is {diff} ({percent}%)");
        }
        Console.ReadLine();
    }
    private static (long, long) Test(int times)
    {
        var s = new Stopwatch();
        s.Start();
        for (var idx = 0; idx < times; idx++)
        {
            var r = IntOp(idx);
        }
        s.Stop();
        var ms1 = s.ElapsedMilliseconds;
        s.Reset();
        s.Start();
        for (var idx = 0; idx < times; idx++)
        {
            var r = SintOp(new Sint(idx));
        }
        s.Stop();
        var ms2 = s.ElapsedMilliseconds;
        s.Reset();
        return (ms1, ms2);
    }
    static int IntOp(int i)
    {
        var j = i + 10;
        return j;
    }

    static Sint SintOp(Sint i)
    {
        var j = new Sint(i.Value + 10);
        return j;
    }
}

El programa repite el test 5 veces y al final muestra los resultados. Lo ejecuté varias veces, pero el patrón es siempre muy similar. En mi máquina, compilado en Release esos fueron los resultados:

int took 254. Sint took 569. Diff is 315 (124.015748031496%)
int took 244. Sint took 447. Diff is 203 (83.1967213114754%)
int took 249. Sint took 436. Diff is 187 (75.1004016064257%)
int took 236. Sint took 460. Diff is 224 (94.9152542372881%)
int took 235. Sint took 441. Diff is 206 (87.6595744680851%)
int took 251. Sint took 427. Diff is 176 (70.1195219123506%)
int took 241. Sint took 446. Diff is 205 (85.0622406639004%)
int took 241. Sint took 452. Diff is 211 (87.551867219917%)
int took 263. Sint took 472. Diff is 209 (79.467680608365%)
int took 258. Sint took 473. Diff is 215 (83.3333333333333%)

Es decir hay una pérdida de rendimiento que oscila entre el 70 y el 100% aproximadamente. Ahora ya se trata de juzgar de si es asumible.

En mi caso decidí que sí, a pesar de la pérdida de rendimiento, tampoco iba a tener un número de operaciones tan bestia (observad que estamos hablando de alrededor de perder un cuarto de segundo cada 100 millones de operaciones).

¿Y por qué hacer esto?

Esa es una buena pregunta, claro. O sea, que ventajas tiene usar una estructura de un solo campo respecto a usar una variable. Es decir, si ya tengo ints, para ¿qué narices quiero usar la estructura Sint?

Una  respuesta es para dar semántica al código. Te pongo mi ejemplo concreto. Tengo una función al que se le pasa un color. Hay 8 colores predefinidos, con valores de 0 a 7, pero luego se pueden definir colores propios a los que se les asignan índices de 8 para arriba (el número de colores que se pueden definir depende de la plataforma de ejecución). Todas las funciones de pintado esperan el color que como puedes ver es un int (cuyo rango va de 0 hasta un valor máximo que no se conoce a priori).

Usar una estructura Color con un solo campo int, me permite definir una semántica propia: las funciones que reciben/devuelven un Color sé que trabajan con colores. En definitiva estoy tipando el parámetro o valor de retorno. Otra opción es que puedo usar métodos de extensión (o en la propia estructura) para agrupar operaciones que se pueden hacer con colores. Y además todas aquellas operaciones que se pueden hacer con ints y no con colores (como dividirlos p. ej.) no se pueden realizar.

En mi caso, por si te pica la curiosidad, terminé con esa estructura:

public static class TvColorNames
{
    public const int Black = 0;
    public const int Red = 1;
    public const int Green = 2;
    public const int Yellow = 3;
    public const int Blue = 4;
    public const int Magenta = 5;
    public const int Cyan = 6;
    public const int White = 7;
    public static IEnumerable<int> AllStandardColors() => new[] { Black, Red, Green, Yellow, Blue, Magenta, Cyan, White };
    public const int StandardColorsCount = 8;
}

public struct TvColor
{
    public readonly int Value;
    public TvColor(int value) => Value = value;
    public TvColor Plus(int valueToAdd) => new TvColor(Value + valueToAdd);

    public readonly static TvColor Black = new TvColor(TvColorNames.Black);
    public readonly static TvColor Red = new TvColor(TvColorNames.Red);
    public readonly static TvColor Green = new TvColor(TvColorNames.Green);
    public readonly static TvColor Yellow = new TvColor(TvColorNames.Yellow);
    public readonly static TvColor Blue = new TvColor(TvColorNames.Blue);
    public readonly static TvColor Magenta = new TvColor(TvColorNames.Magenta);
    public readonly static TvColor Cyan = new TvColor(TvColorNames.Cyan);
    public readonly static TvColor White = new TvColor(TvColorNames.White);

    public static explicit operator short(TvColor color) => (short)color.Value;
    public static bool operator ==(TvColor one, TvColor other) => one.Value == other.Value;
    public static bool operator !=(TvColor one, TvColor other) => one.Value != other.Value;

    public override bool Equals(object obj)
    {
        if (obj is TvColor) return ((TvColor)obj).Value == Value;
        if (obj is int) return (int)obj == Value;
        return base.Equals(obj);
    }
    public override int GetHashCode() => Value.GetHashCode();
}

Y el código queda un poco más legible, ya que las funciones que operan con colores se definen en base a este tipo, no en base a un int como p. ej:

public interface IStyleBuilder
{
    IStyleBuilder DesiredStandard(TvColor fore, TvColor back,CharacterAttributeModifiers attributes = CharacterAttributeModifiers.Normal);
    IStyleBuilder DesiredFocused(TvColor fore, TvColor back, CharacterAttributeModifiers attributes = CharacterAttributeModifiers.Normal);
}

En general uso esta estructura para el mismo caso en que, si los valores fueran fijos, usaría un enum. Si solo hubiese la posibilidad de usar los 8 colores, hubiese creado un enum con esos 8 valores. Pero, al haber la posibilidad de crear más colores y de no saber a priori cuantos son, me impide usar un enum y por ello uso la estructura. Pero en ambos casos la idea es la misma: dotar el código de semántica, hacerlo más claro.

Que valga la pena o no pagar el precio de rendimiento eso ya queda a criterio de cada cual.

Unicode y encodings

Uno de los conceptos que hoy en día siguen causando más confusión es el de Unicode y sus distintos tipos de codificación. Pero… ¿qué es realmente Unicode? Para ello, déjame que remonte unos cuantos años atrás…

El inicio: ASCII

Los ordenadores los inventaron los americanos y como suele ocurrir se preocuparon de lo suyo: que un ordenador pudiese presentar textos en su idioma. Tampoco hay tantos caracteres en el inglés: las veinte y poco letras (en mayúsculas y minúsculas), los símbolos de puntuación, paréntesis, operaciones matemáticas y poco más. En total eran menos de 128 carácteres. Genial, porque esos espacios que sobraban se podían aprovechar para colocar otros caracteres que no tienen representación gráfica pero que eran (y son) necesarios para controlar el teletipo: retornos de carro, saltos de línea y similares. Había nacido el código ASCII.

Continúa leyendo Unicode y encodings

Azure Functions y SignalR: serverless push

El hecho de ofrecer SignalR como servicio PaaS en Azure y su integración con Azure Functions nos abre un escenario interesante: ahora es facilísimo hacer notificaciones push desde una Azure Function (AF) a un cliente SignalR (p. ej. una Web).

Continúa leyendo Azure Functions y SignalR: serverless push

Novedades .NET Core 2.1: Generic host

Ahora que .NET Core 2.1 ya es oficial ya podemos desgranar algunas de sus novedades más interesantes. La verdad es que, por fin, se vislumbra una madurez en la plataforma. Realmente a no ser que haya algún motivo de fuerza mayor (librería no disponible), .NET Core 2.1 debería ser la opción por defecto a la hora de empezar cualquier proyecto nuevo.

Continúa leyendo Novedades .NET Core 2.1: Generic host

Sobre NuGet y versiones…

Que NuGet ha supuesto una revolución en .NET es más que evidente. Lejos han quedado aquellos tiempos en que gestionábamos las dependencias como podíamos. Poco a poco el modelo de desarrollo está migrando de estar basado en “dependencias a ensamblados” a “dependencias a paquetes”, y a medida que netcore vaya teniendo una mayor relevancia esto irá a más.

Pero esta gestión semi-automatizada de las dependencias también trae sus propios quebraderos de cabeza…

En nodejs es muy común hablar del “npm hell” o el infierno que puede suponer la gestión de paquetes usando npm. Que al cabo de un tiempo alguien se baje el código de tu repositorio y que no le funcione o bien que actualices un paquete y se terminen rompiendo 400 más, es algo muy (demasiado) habitual. ¿Tenemos en .NET un nuget hell?

Continúa leyendo Sobre NuGet y versiones…

Algunas consideraciones sobre las structs

El otro día un tweet de Juan Quijano, animó una pequeña discusión sobre la diferencia entre clases y estructuras en .NET. Este no es el primer post que escribo al respecto, pero bueno, aprovechando la coyuntura vamos a comentar algunas de las cosas que se mencionaron en el pequeño debate que generó el tweet de Juan.

Continúa leyendo Algunas consideraciones sobre las structs

netstandard–El “estándar” que viene

Cuando .NET salió, las cosas eran muy sencillas: había una sola versión de .NET, el .NET Framework, así que como mucho debíamos saber para que versión de .NET era una determinada librería. “Oh, la librería es solo para .NET 2.0 y yo uso .NET 1.1, que mala suerte”. Al margen de eso no había mucho más, todos teníamos claro que significaba .NET.

Continúa leyendo netstandard–El “estándar” que viene