Frage

Ich bin Überarbeitung nur Kapitel 4 von C # in Tiefe, die sie mit Nullable-Typen, und ich hinzufüge, einen Abschnitt über die Verwendung des „als“ Operator, die Sie schreiben kann:

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

Ich dachte, das wirklich ordentlich war, und dass es könnte Verbesserung der Leistung über die C # 1 Äquivalent unter Verwendung „wird“, gefolgt von einer Besetzung - immerhin auf diese Weise brauchen wir nur fragen dynamischen Typ einmal überprüft, und dann ein einfache Wertprüfung.

Dies scheint nicht der Fall zu sein, aber. Ich habe eine Probe-Test app unten enthalten, die im Grunde alle summiert die Zahlen innerhalb eines Objekts Array - aber das Array enthält eine Menge von null Referenzen und String Verweise sowie boxed ganze Zahlen. Die Benchmark misst den Code, den Sie in C # 1 zu verwenden haben würden, der Code die „als“ Operator, und nur eine LINQ-Lösung für Kicks. Zu meinem Erstaunen ist der C # 1 Code 20-mal schneller in diesem Fall - und auch der LINQ-Code (die ich langsamer sein erwartet haben würde, da die Iteratoren beteiligt) schlägt den „als“ Code

.

Ist die .NET-Implementierung von isinst für Nullable Types nur sehr langsam? Ist es die zusätzlichen unbox.any, das das Problem verursacht? Gibt es eine andere Erklärung dafür? Im Moment fühlt es sich wie ich werde eine Warnung gegen die Verwendung dieser Leistungs sensiblen Situationen müssen schließen ...

Ergebnisse:

  

Darsteller: 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);
    }
}
War es hilfreich?

Lösung

Klar, dass der Maschinencode der JIT-Compiler für den ersten Fall erzeugen können, ist viel effizienter. Eine Regel, die wirklich hilft, ist, dass ein Objekt nur unboxed sein kann, um eine Variable, die den gleichen Typ wie der Box-Wert hat. Das ermöglicht die JIT-Compiler sehr effizienten Code zu generieren, keine Wert Umwandlungen in Betracht gezogen werden müssen.

Der ist Operator Test ist einfach, nur überprüfen, ob das Objekt nicht null ist und von dem erwarteten Typ, dauert aber ein paar Maschinencode Anweisungen. Die Besetzung ist auch leicht, der JIT-Compiler kennt den Standort des Wert Bits in dem Objekt und verwendet sie direkt. Vervielfältigung oder Umwandlung auftritt, werden alle Maschinencode ist inline und dauert aber etwa ein Dutzend Anweisungen. Dies benötigt wirklich effizient wieder in .NET 1.0 sein, wenn Boxen üblich waren.

Casting auf int? nimmt viel mehr Arbeit. Der Wert Darstellung der geschachtelte Ganzzahl ist mit dem Speicher-Layout von Nullable<int> nicht kompatibel. Eine Konvertierung ist erforderlich, und der Code ist schwierig wegen möglichen boxed Enum-Typen. Der JIT-Compiler generiert einen Aufruf an eine CLR Hilfsfunktion namens JIT_Unbox_Nullable den Job zu erledigen. Dies ist ein Mehrzweck-Funktion für jeden Werttyp, eine Menge Code dort Typen zu überprüfen. Und der Wert wird kopiert. Schwer, die Kosten abzuschätzen, da dieser Code innerhalb mscorwks.dll eingesperrt, aber Hunderte von Anweisungen Maschinencode wahrscheinlich ist.

Die Linq OfType () Erweiterungsmethode verwendet auch die is Operator und die Besetzung. Dies ist jedoch eine Umwandlung in einen generischen Typ. Der JIT-Compiler erzeugt einen Aufruf einer Hilfsfunktion, JIT_Unbox (), die ein gegossenes auf einen beliebigen Wert Typ durchführen kann. Ich habe keine große Erklärung, warum es so langsam wie die Besetzung zu Nullable<int> ist, da weniger Arbeit sollte erforderlich sein. Ich vermute, dass ngen.exe könnte Probleme verursachen hier.

Andere Tipps

Es scheint mir, dass die isinst nur sehr langsam auf Nullable-Typen. In Verfahren FindSumWithCast geändert I

if (o is int)

if (o is int?)

, die deutlich verlangsamt auch die Ausführung. Der einzige differenc in IL Ich kann sehen, ist, dass

isinst     [mscorlib]System.Int32

wird geändert

isinst     valuetype [mscorlib]System.Nullable`1<int32>

Das ursprünglich begann als Kommentar zu Hans Passant die ausgezeichnete Antwort, aber es hat zu lange, damit ich hier ein paar Bits hinzufügen möchten:

Zuerst wird der C # as Operator einen isinst IL Befehl (so auch den is operator) emittiert. (Eine weitere interessante Anweisung ist castclass, emited, wenn Sie tun, um eine direkte Besetzung und der Compiler weiß, dass Laufzeitprüfung nicht ommited werden kann.)

Hier ist, was isinst tut ( ECMA 335 Partition III, 4.6 ):

  

Format: isinst typeTok

     

typeTok ein Metadatentoken (a typeref, typedef oder typespec), die gewünschte Klasse angibt.

     

Wenn typeTok ist ein Nicht-Nullable-Wertetyp oder ein generischer Parameter Typ interpretiert wird als „boxed“ typeTok .

     

Wenn typeTok ist ein Nullable Type, Nullable<T>, wird es als „boxed“ T interpretiert

Am wichtigsten ist:

  

Wenn die Ist-Typ (nicht der Verifizierer verfolgt Typ) von obj ist Verifizierer zuweisbare-to der Typ typeTok dann isinst erfolgreich ist und obj (wie Ergebnis ) unverändert zurückgegeben wird, während Überprüfung verfolgt seine Art als typeTok . Im Gegensatz zu Nötigungen (§1.6) und Conversions (§3.27), isinst ändert sich nie die tatsächliche Typ eines Objekts und Konserven Objektidentität (siehe Partition I).

So ist der Leistungskiller nicht isinst in diesem Fall, aber die zusätzlichen unbox.any. Dies war nicht klar, von Hans' Antwort, als er nur am JITed Code aussah. In der Regel wird der C # -Compiler eine unbox.any nach einem isinst T? emittieren (aber wird es für den Fall auslassen Sie tun isinst T, wenn T ist ein Referenz-Typ).

Warum tut sie das? isinst T? hat nie den Effekt, dass offensichtlich gewesen wäre, das heißt Sie wieder eine T?. Stattdessen werden alle diese Anweisungen sicherstellen, dass Sie eine "boxed T" haben, die T? unboxed sein kann. Um eine tatsächliche T? zu erhalten, müssen wir noch unsere "boxed T" zu T? unbox, weshalb der Compiler einen unbox.any nach isinst emittiert. Wenn man darüber nachdenkt, das macht Sinn, weil das „Box-Format“ für T? nur "boxed T" und machen castclass und isinst die unbox führen wäre inkonsistent.

Sichern von Hans' zu finden, mit einigen Informationen aus dem Standard , hier geht es:

(ECMA 335 Partition III, 4,33): unbox.any

  

Wenn an die geschachtelte Form eines Werttyp angewendet wird, die Anweisung unbox.any extrahiert die enthaltenen Wert innerhalb obj (vom Typ O). (Es ist äquivalent zu unbox von ldobj gefolgt.) Wenn auf einen Referenztyp angewandt, die unbox.any Anweisung hat die gleiche Wirkung wie castclass typeTok.

(ECMA 335 Partition III, 4,32): unbox

  

Typischerweise berechnet unbox einfach die Adresse des Werttypen, die bereits innerhalb des geschachtelten Objekts ist. Dieser Ansatz ist nicht möglich, wenn Nullable-Wertetypen Unboxing. Da Nullable<T> Werte boxed Ts während des Box-Betrieb umgewandelt werden, oft eine Implementierung eines neuen Nullable<T> auf dem Heap herstellen müssen und die Adresse an den neu zugeordneten Objekts berechnen.

Interessanterweise kam ich auf Feedback über Bedienerunterstützung über dynamic eine Auftrag-of-Größenordnung langsamer für Nullable<T> sein (ähnlich diese frühe Test ) - ich vermute, dass für sehr ähnlichen Gründen

.

Gotta love Nullable<T>. Ein weiterer Spaß ist, dass, obwohl die JIT-Spots (und entfernt) null für Nicht-Nullable-Strukturen, es Borks es für Nullable<T>:

using System;
using System.Diagnostics;
static class Program {
    static void Main() { 
        // JIT
        TestUnrestricted<int>(1,5);
        TestUnrestricted<string>("abc",5);
        TestUnrestricted<int?>(1,5);
        TestNullable<int>(1, 5);

        const int LOOP = 100000000;
        Console.WriteLine(TestUnrestricted<int>(1, LOOP));
        Console.WriteLine(TestUnrestricted<string>("abc", LOOP));
        Console.WriteLine(TestUnrestricted<int?>(1, LOOP));
        Console.WriteLine(TestNullable<int>(1, LOOP));

    }
    static long TestUnrestricted<T>(T x, int loop) {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
    static long TestNullable<T>(T? x, int loop) where T : struct {
        Stopwatch watch = Stopwatch.StartNew();
        int count = 0;
        for (int i = 0; i < loop; i++) {
            if (x != null) count++;
        }
        watch.Stop();
        return watch.ElapsedMilliseconds;
    }
}

Dies ist das Ergebnis von FindSumWithAsAndHas oben:

Dies ist das Ergebnis von FindSumWithCast:

Ergebnisse:

  • Mit as, ist es zunächst testen, ob ein Objekt eine Instanz von Int32 ist; unter der Haube ist es mit isinst Int32 (der handgeschriebenen Code ähnlich ist: if (o int)). Und mit as, es unbox auch bedingungslos das Objekt. Und es ist ein echter Performance-Killer eine Eigenschaft zu nennen (es ist immer noch eine Funktion unter der Haube), IL_0027

  • Mit gegossen, testen Sie zuerst, wenn das Objekt ein int if (o is int) ist; unter der Haube wird mit diesem isinst Int32. Wenn es eine Instanz von int ist, dann kann man sicher unbox den Wert, IL_002D

Einfach ausgedrückt, ist dies der Pseudo-Code von as Ansatz:

int? x;

(x.HasValue, x.Value) = (o isinst Int32, o unbox Int32)

if (x.HasValue)
    sum += x.Value;    

Und das ist der Pseudo-Code aus gegossenem Ansatz:

if (o isinst Int32)
    sum += (o unbox Int32)

So der Besetzung ((int)a[i], auch die Syntax sieht aus wie ein Guss, aber es ist tatsächlich Unboxing, Guss- und Unboxing die gleiche Syntax, das nächste Mal, wenn ich pedantisch mit der richtigen Terminologie sein wird) Ansatz ist wirklich schneller, nur benötigt einen Wert unbox wenn ein Objekt entscheidet eine int ist. Das gleiche kann man nicht mit einem as Ansatz gesagt werden.

Profil weiter:

using System;
using System.Diagnostics;

class Program
{
    const int Size = 30000000;

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

        FindSumWithIsThenCast(values);

        FindSumWithAsThenHasThenValue(values);
        FindSumWithAsThenHasThenCast(values);

        FindSumWithManualAs(values);
        FindSumWithAsThenManualHasThenValue(values);



        Console.ReadLine();
    }

    static void FindSumWithIsThenCast(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("Is then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenValue(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 then Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenHasThenCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (x.HasValue)
            {
                sum += (int)o;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Has then Cast: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithManualAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            bool hasValue = o is int;
            int x = hasValue ? (int)o : 0;

            if (hasValue)
            {
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Manual As: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithAsThenManualHasThenValue(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {
            int? x = o as int?;

            if (o is int)
            {
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("As then Manual Has then Value: {0} : {1}", sum,
                            (long)sw.ElapsedMilliseconds);
    }

}

Ausgabe:

Is then Cast: 10000000 : 303
As then Has then Value: 10000000 : 3524
As then Has then Cast: 10000000 : 3272
Manual As: 10000000 : 395
As then Manual Has then Value: 10000000 : 3282

Was können wir aus diesen Zahlen ableiten?

  • Erstens ist-then-Guss Ansatz ist deutlich schneller als wie Ansatz. 303 vs 3524
  • Zweitens .Wert ist geringfügig langsamer als Guss. 3524 vs 3272
  • Drittens .HasValue ist geringfügig langsamer als die manuelle Verwendung hat (d. Mit ). 3524 vs 3282
  • Viertens macht einen Apfel zu Apfel Vergleich (dh beide simulierten HasValue Zuordnung und Umwandlung simulierten Wert zusammen geschieht) zwischen simuliert wie und real wie Ansatz, wir sehen kann simuliert wie ist immer noch deutlich schneller als real wie . 395 vs 3524
  • Schließlich basierend auf erste und vierte Schlussfolgerung, es ist etwas falsch mit wie Implementierung ^ _ ^

Um diese Antwort zu halten up-to-date, es ist erwähnenswert, dass die meisten der Diskussion auf dieser Seite ist jetzt strittig jetzt mit C # 7.1 und .NET 4.7 , die eine schlanke Syntax unterstützt, dass auch die beste IL-Code erzeugt.

Die ursprüngliche Beispiel der OP ...

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

wird einfach ...

if (o is int x)
{
    // ...use x in here
}

Ich habe festgestellt, dass eine gemeinsame Nutzung für die neue Syntax, wenn Sie eine .NET Werttyp Schreiben (dh struct in C # ), dass Geräte IEquatable<MyStruct> (wie die meisten sollte). die stark typisierte Equals(MyStruct other) Methode Nach der Implementierung können Sie jetzt umleiten anmutig die untypisierten Equals(Object obj) Korrektur (von Object geerbt), um es wie folgt:

public override bool Equals(Object obj) => obj is MyStruct o && Equals(o);


Anhang: Die Release build IL Code für die ersten zwei Beispielfunktionen in dieser Antwort oben gezeigt (jeweils) ist hier gegeben. Während der IL-Code für die neue Syntax in der Tat 1 Byte kleiner ist, ist es meist großes gewinnt durch Null Anrufe (gegenüber zwei) und der Vermeidung des unbox Betriebes insgesamt, wenn möglich.

// static void test1(Object o, ref int y)
// {
//     int? x = o as int?;
//     if (x.HasValue)
//         y = x.Value;
// }

[0] valuetype [mscorlib]Nullable`1<int32> x
        ldarg.0
        isinst [mscorlib]Nullable`1<int32>
        unbox.any [mscorlib]Nullable`1<int32>
        stloc.0
        ldloca.s x
        call instance bool [mscorlib]Nullable`1<int32>::get_HasValue()
        brfalse.s L_001e
        ldarg.1
        ldloca.s x
        call instance !0 [mscorlib]Nullable`1<int32>::get_Value()
        stind.i4
L_001e: ret

// static void test2(Object o, ref int y)
// {
//     if (o is int x)
//         y = x;
// }

[0] int32 x,
[1] object obj2
        ldarg.0
        stloc.1
        ldloc.1
        isinst int32
        ldnull
        cgt.un
        dup
        brtrue.s L_0011
        ldc.i4.0
        br.s L_0017
L_0011: ldloc.1
        unbox.any int32
L_0017: stloc.0
        brfalse.s L_001d
        ldarg.1
        ldloc.0
        stind.i4
L_001d: ret

Für weitere Tests, die untermauern meine Bemerkung über die Leistung des neuen C # 7 Syntax, um die bisher verfügbaren Optionen übertreffen, finden Sie unter hier (insbesondere Beispiel 'D').

Ich habe keine Zeit, es zu versuchen, aber Sie können haben wollen:

foreach (object o in values)
        {
            int? x = o as int?;

als

int? x;
foreach (object o in values)
        {
            x = o as int?;

Sie werden jedes Mal ein neues Objekt zu erzeugen, die das Problem nicht vollständig erklären, kann aber dazu beitragen.

Ich habe versucht, den genauen Typ Prüfung Konstrukt

typeof(int) == item.GetType(), das führt so schnell wie die item is int Version, und gibt immer die Nummer (Schwerpunkt: auch wenn Sie eine Nullable<int> auf das Array geschrieben, die Sie verwenden typeof(int) benötigen würde). Sie müssen auch ein zusätzliches null != item Check hier.

Allerdings

typeof(int?) == item.GetType() Aufenthalte schnell (im Gegensatz zu item is int?), aber immer false zurück.

Das typeof-Konstrukt ist in meinen Augen der schnellste Weg für genau Typprüfung, da sie die Runtimetypehandle verwendet. Da die genauen Typen in diesem Fall nicht mit NULL festlegbare übereinstimmen, meine Vermutung ist, haben is/as hier zusätzliche heavylifting tun zu gewährleisten, dass es in der Tat ist eine Instanz eines Nullable-Typ.

Und ehrlich gesagt: was macht Ihr is Nullable<xxx> plus HasValue Sie kaufen? Nichts. Sie können jederzeit direkt auf den darunter liegenden (Wert) Typen (in diesem Fall) gehen. Sie erhalten entweder den Wert oder „nein, nicht eine Instanz des Typs Sie fragen“. Auch wenn Sie (int?)null auf das Array geschrieben, wird die Typprüfung false zurück.

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);
        FindSumWithAsAndHas(values);
        FindSumWithAsAndIs(values);


        FindSumWithIsThenAs(values);
        FindSumWithIsThenConvert(values);

        FindSumWithLinq(values);



        Console.ReadLine();
    }

    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 FindSumWithAsAndHas(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 and Has: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }


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







    static void FindSumWithIsThenAs(object[] values)
    {
        // Apple-to-apple comparison with Cast routine above.
        // Using the similar steps in Cast routine above,
        // the AS here cannot be slower than Linq.



        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {

            if (o is int)
            {
                int? x = o as int?;
                sum += x.Value;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then As: {0} : {1}", sum,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindSumWithIsThenConvert(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        foreach (object o in values)
        {            
            if (o is int)
            {
                int x = Convert.ToInt32(o);
                sum += x;
            }
        }
        sw.Stop();
        Console.WriteLine("Is then Convert: {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);
    }
}

Ausgänge:

Cast: 10000000 : 456
As and Has: 10000000 : 2103
As and Is: 10000000 : 2029
Is then As: 10000000 : 1376
Is then Convert: 10000000 : 566
LINQ: 10000000 : 1811

[EDIT: 2010-06-19]

Hinweis: Zurück Test innerhalb VS, Konfiguration Debug getan wurde, VS2009 verwenden, mit Core i7 (Unternehmensentwicklung Maschine).

Das Folgende wurde auf meinem Rechner getan Core 2 Duo mit, mit VS2010

Inside VS, Configuration: Debug

Cast: 10000000 : 309
As and Has: 10000000 : 3322
As and Is: 10000000 : 3249
Is then As: 10000000 : 1926
Is then Convert: 10000000 : 410
LINQ: 10000000 : 2018




Outside VS, Configuration: Debug

Cast: 10000000 : 303
As and Has: 10000000 : 3314
As and Is: 10000000 : 3230
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 418
LINQ: 10000000 : 1944




Inside VS, Configuration: Release

Cast: 10000000 : 305
As and Has: 10000000 : 3327
As and Is: 10000000 : 3265
Is then As: 10000000 : 1942
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1932




Outside VS, Configuration: Release

Cast: 10000000 : 301
As and Has: 10000000 : 3274
As and Is: 10000000 : 3240
Is then As: 10000000 : 1904
Is then Convert: 10000000 : 414
LINQ: 10000000 : 1936
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top