26 Stimmen

Instanziierung einer Variablen bei Null

if (x == null) x = new X();

gegen

x = x ?? new X();

Welche dieser beiden Varianten ist tatsächlich leistungsfähiger? Sind sie nach der Kompilierung im Grunde genommen dasselbe (würde x = x; ein NO-OP als Ergebnis sein)?

38voto

Mark Byers Punkte 761508

Ein Blick auf den Code der Zwischensprache zeigt einen Unterschied:

.method private hidebysig instance void Method1() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class X Program::x
    L_0006: brtrue.s L_0013
    L_0008: ldarg.0 
    L_0009: newobj instance void X::.ctor()
    L_000e: stfld class X Program::x
    L_0013: ret 
}

.method private hidebysig instance void Method2() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldarg.0 
    L_0002: ldfld class X Program::x
    L_0007: dup 
    L_0008: brtrue.s L_0010
    L_000a: pop 
    L_000b: newobj instance void X::.ctor()
    L_0010: stfld class X Program::x
    L_0015: ret 
}

Hier ist der Code, den ich kompiliert habe, um dies zu erreichen:

void Method1()
{
    if (x == null) x = new X();
}

void Method2()
{
    x = x ?? new X();
}

Um sicher zu sein, was schneller ist, sollten Sie beide Zeiten messen.

Method     Initial condition   Iterations per second
---------------------------------------------------
NullCheck  x is null           33 million  
Coalesce   x is null           33 million
NullCheck  x is not null       40 million
Coalesce   x is not null       33 million

Schlussfolgerung:

  • In diesem Fall, in dem der Wert anfänglich Null ist, sind sie ungefähr gleich.
  • Die Methode, die eine if-Anweisung verwendet, ist wesentlich schneller als der Null-Koaleszenz-Operator, wenn x bereits nicht null ist.

Der Unterschied, wenn x nicht null ist, könnte darauf zurückzuführen sein, dass der Null-Koaleszenz-Operator den Wert von x wieder x zuordnet ( stfld in IL), während die Nullprüfung über die stfld Anweisung, wenn x nicht null ist.

Beide sind so schnell, dass man schon einen muy enge Schleife, um den Unterschied zu bemerken. Sie sollten diese Art von Leistungsoptimierung nur vornehmen, wenn Sie Ihren Code mit Ihren Daten profiliert haben. Unterschiedliche Situationen, unterschiedliche Versionen von .NET, unterschiedliche Compiler usw. können zu unterschiedlichen Ergebnissen führen.

Falls jemand wissen möchte, wie ich zu diesen Ergebnissen gekommen bin oder sie reproduzieren möchte, hier ist der Code, den ich verwendet habe:

using System;

class X { }

class Program
{
    private X x;

    private X xNull = null;
    private X xNotNull = new X();

    private void Method1Null()
    {
        x = xNull;
        if (x == null) x = xNotNull;
    }

    private void Method2Null()
    {
        x = xNull;
        x = x ?? xNotNull;
    }

    private void Method1NotNull()
    {
        x = xNotNull;
        if (x == null) x = xNotNull;
    }

    private void Method2NotNull()
    {
        x = xNotNull;
        x = x ?? xNotNull;
    }

    private const int repetitions = 1000000000;

    private void Time(Action action)
    {
        DateTime start = DateTime.UtcNow;
        for (int i = 0; i < repetitions; ++i)
        {
            action();
        }
        DateTime end = DateTime.UtcNow;
        Console.WriteLine(repetitions / (end - start).TotalSeconds);
    }

    private void Run()
    {
        Time(() => { Method1Null(); });
        Time(() => { Method2Null(); });
        Time(() => { Method1NotNull(); });
        Time(() => { Method2NotNull(); });
        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    private static void Main()
    {
        new Program().Run();
    }
}

Haftungsausschluss: Kein Benchmark ist perfekt, und dieser Benchmark ist weit von perfekt, hauptsächlich um die Dinge einfach zu halten. Ich habe zahlreiche verschiedene Tests durchgeführt, z. B. mit den Methoden in einer anderen Reihenfolge, mit und ohne vorheriges "Aufwärmen", über unterschiedlich lange Zeiträume usw. Ich erhalte jedes Mal ungefähr die gleichen Ergebnisse. Ich hatte nichts zu beweisen, weder das eine noch das andere, so dass jede Voreingenommenheit zugunsten der einen oder anderen Methode unbeabsichtigt ist.

18voto

Daniel A. White Punkte 180762

Ich würde mir keine Sorgen um diese vorzeitige Optimierung machen. Ich bin sicher, die Designer des C#-Compilers waren klug genug, dies für Sie zu tun.

6voto

Ondrej Tucny Punkte 26742

Es gibt keine Interessant Leistungsunterschied überhaupt . Das Wichtigste ist, dass if (x == null) x = new X(); es viel besser lesbar .

Um Ihre ursprüngliche Frage zu beantworten: Ein intelligenter, optimierender Compiler würde kompilieren x = x ?? new X() auf die gleiche Weise wie if (x == null) x = new X(); .

Überlassen Sie daher Mikro-Optimierungen dem Compiler und konzentrieren Sie sich auf die Lesbarkeit und Klarheit des Codes.


Aktualisierung: Weitere Informationen über Optimierungsverfahren finden Sie unter dieser Artikel auf wikipedia und dann, im Hinblick auf die Art Ihrer Frage, Google für "Vorzeitige Optimierung ist die Wurzel allen Übels" .

4voto

Erik Funkenbusch Punkte 91649

Wie andere bereits erwähnt haben, ist der Leistungsunterschied zwischen den beiden nicht so groß, aber es gibt einen wichtigen Unterschied zwischen den beiden, auch wenn er in Ihrem Beispiel nicht so offensichtlich ist.

if (x == null) x = new X(); erzeugt eine potentielle Wettlaufsituation, bei der x von einem anderen Thread zwischen der Prüfung auf Null und der Erstellung neu erstellt werden kann, wodurch ein Objekt verloren geht.

x = x ?? new X(); erstellt zuerst eine Kopie der Variablen, so dass es keine potenzielle Race Condition gibt.

Dies ist eher ein Problem in Situationen, in denen Sie auf das Objekt verweisen. Zum Beispiel:

if (x != null) x.DoSomething();  // this can cause a null reference exception
                                 // if another thread nulls x between the check
                                 // and the call to DoSomething()

(x = x ?? new X()).DoSomething()     // this should be fine.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X