Sorpresa de rendimiento con tipos "as" y anulables.

Solo estoy revisando el capítulo 4 de C # en profundidad, que trata sobre tipos que admiten nulos, y estoy agregando una sección sobre el uso del operador "como", que le permite escribir:

object o = ...;
int? x = o as int?;
if (x.HasValue)
{
    ... // Use x.Value in here
}

Pensé que esto era realmente bueno, y que podría mejorar el rendimiento en comparación con el equivalente de C # 1, usando "es" seguido de una conversión - después de todo, de esta manera solo necesitamos solicitar la comprobación dinámica de tipos una vez, y luego una simple comprobación de valores .

Esto parece no ser el caso, sin embargo. He incluido una aplicación de prueba de muestra a continuación, que básicamente resume todos los enteros dentro de una matriz de objetos, pero la matriz contiene muchas referencias nulas y referencias de cadena, así como también enteros en caja. El punto de referencia mide el código que tendrías que usar en C # 1, el código que usa el operador "as" y solo para una solución LINQ. Para mi sorpresa, el código C # 1 es 20 veces más rápido en este caso, e incluso el código LINQ (que habría esperado que fuera más lento, dados los iteradores involucrados) supera al código "como".

Es la implementación .NET deisinst para tipos anulables realmente lento? Es el adicionalunbox.any que causa el problema? ¿Hay otra explicación para esto? En este momento, siento que voy a tener que incluir una advertencia contra el uso de esto en situaciones sensibles al rendimiento ...

Resultados:

Reparto: 10000000: 121
Como: 10000000: 2211
LINQ: 10000000: 2143

Código:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i+1] = "";
            values[i+2] = 1;
        }

        FindSumWithCast(values);
        FindSumWithAs(values);
        FindSumWithLinq(values);
    }

    static void FindSumWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            if (o is int)
            {
                int x = (int) o;
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;
            if (x.HasValue)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }

    static void FindSumWithLinq(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = values.OfType<int>().Sum();
        sw.Stop();
        Console.WriteLine("LINQ: {0} : {1}", sum, 
                          (long) sw.ElapsedMilliseconds);
    }
}

Respuestas a la pregunta(10)

Su respuesta a la pregunta