Leistungsüberraschung mit As- und Nullable-Typen

Ich überarbeite gerade Kapitel 4 von C # in Depth, das sich mit nullbaren Typen befasst, und füge einen Abschnitt über die Verwendung des Operators "as" hinzu, in dem Sie Folgendes schreiben können:

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

Ich fand das wirklich gut und konnte die Leistung gegenüber dem C # 1-Äquivalent verbessern, indem "is" gefolgt von einem Cast verwendet wurde. Auf diese Weise müssen wir nur einmal nach einer dynamischen Typprüfung und dann nach einer einfachen Wertprüfung fragen .

Dies scheint jedoch nicht der Fall zu sein. Ich habe unten ein Beispiel für eine Testanwendung eingefügt, in der im Grunde alle Ganzzahlen in einem Objektarray zusammengefasst sind. Das Array enthält jedoch viele Null- und Zeichenfolgenreferenzen sowie Ganzzahlen in Kästchen. Der Benchmark misst den Code, den Sie in C # 1 verwenden müssten, den Code mit dem Operator "as" und nur zum Kicken einer LINQ-Lösung. Zu meinem Erstaunen ist der C # 1-Code in diesem Fall 20-mal schneller - und sogar der LINQ-Code (von dem ich erwartet hätte, dass er angesichts der beteiligten Iteratoren langsamer ist) schlägt den "as" -Code.

Ist die .NET-Implementierung vonisinst für nullable typen nur ganz langsam? Ist es das zusätzlicheunbox.any das verursacht das problem? Gibt es eine andere Erklärung dafür? Im Moment habe ich das Gefühl, dass ich eine Warnung davor einfügen muss, dies in leistungssensiblen Situationen zu verwenden ...

Ergebnisse:

Besetzung: 10000000: 121
As: 10000000: 2211
LINQ: 10000000: 2143

Code:

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);
    }
}

Antworten auf die Frage(10)

Ihre Antwort auf die Frage