5 Stimmen

Was ist in einer Schleife schneller: der zweimalige Aufruf einer Eigenschaft oder das einmalige Speichern der Eigenschaft?

Dies ist eher eine akademische Frage über die Leistung als ein realistisches "was sollte ich verwenden", aber ich bin neugierig, wie ich nicht viel in IL überhaupt zu sehen, was konstruiert ist und ich habe nicht eine große Datenmenge auf der Hand zu Profil gegen.

Was ist also schneller:

List<myObject> objs = SomeHowGetList();
List<string> strings = new List<string>();
foreach (MyObject o in objs)
{
    if (o.Field == "something")
        strings.Add(o.Field);
}

oder:

List<myObject> objs = SomeHowGetList();
List<string> strings = new List<string>();
string s;
foreach (MyObject o in objs)
{
    s = o.Field;
    if (s == "something")
        strings.Add(s);
}

Beachten Sie, dass ich nicht wirklich wollen, um die Leistung Auswirkungen der string.Add(s) (als was auch immer Betrieb getan werden muss, kann nicht wirklich geändert werden), nur die Leistung Unterschied zwischen Einstellung s jede Iteration (sagen wir, dass s kann jeder primitive Typ oder String) Verse Aufruf der Getter auf das Objekt jede Iteration.

9voto

Andy Gaskell Punkte 30777

Ihre erste Option ist in meinen Tests deutlich schneller. Ich bin so ein Flip-Flopper! Aber im Ernst: Es wurden einige Kommentare zum Code in meinem ursprünglichen Test gemacht. Hier ist der aktualisierte Code, der zeigt, dass Option 2 schneller ist.

    class Foo
    {
        public string Bar { get; set; }

        public static List<Foo> FooMeUp()
        {
            var foos = new List<Foo>();

            for (int i = 0; i < 10000000; i++)
            {
                foos.Add(new Foo() { Bar = (i % 2 == 0) ? "something" : i.ToString() });
            }

            return foos;
        }
    }

    static void Main(string[] args)
    {

        var foos = Foo.FooMeUp();
        var strings = new List<string>();

        Stopwatch sw = Stopwatch.StartNew();

        foreach (Foo o in foos)
        {
            if (o.Bar == "something")
            {
                strings.Add(o.Bar);
            }
        }

        sw.Stop();
        Console.WriteLine("It took {0}", sw.ElapsedMilliseconds);

        strings.Clear();
        sw = Stopwatch.StartNew();

        foreach (Foo o in foos)
        {
            var s = o.Bar;
            if (s == "something")
            {
                strings.Add(s);
            }
        }

        sw.Stop();
        Console.WriteLine("It took {0}", sw.ElapsedMilliseconds);
        Console.ReadLine();
    }

7voto

mmx Punkte 400975

In den meisten Fällen sollte Ihr zweiter Codeschnipsel lauten mindestens so schnell wie den ersten Schnipsel.

Diese beiden Codeschnipsel sind funktional nicht gleichwertig. Es ist nicht garantiert, dass die Eigenschaften bei jedem einzelnen Zugriff das gleiche Ergebnis liefern. Folglich ist der JIT-Optimierer nicht in der Lage, das Ergebnis zwischenzuspeichern (außer in trivialen Fällen), und es ist schneller, wenn Sie das Ergebnis einer lang laufenden Eigenschaft zwischenspeichern. Sehen Sie sich dieses Beispiel an: warum foreach beim Lesen von Richtextbox-Zeilen schneller ist als eine for-Schleife .

In einigen besonderen Fällen wie z. B.:

for (int i = 0; i < myArray.Length; ++i)

donde myArray ein Array-Objekt ist, ist der Compiler in der Lage, das Muster zu erkennen und den Code zu optimieren, so dass die Bindungsprüfungen entfallen. Es könnte langsamer sein, wenn Sie das Ergebnis von Length Eigenschaft wie:

int len = myArray.Length;
for (int i = 0; i < myArray.Length; ++i)

4voto

Marc Gravell Punkte 970173

Es kommt wirklich auf die Umsetzung an. In den meisten Fällen wird davon ausgegangen, dass eine Eigenschaft kostengünstig ist (als eine Frage der allgemeinen Praxis / Höflichkeit). Es könnte jedoch sein, dass jedes "get" eine nicht zwischengespeicherte Suche über eine entfernte Ressource durchführt. Bei einfachen Standardeigenschaften werden Sie nie einen wirklichen Unterschied zwischen den beiden Verfahren feststellen. Im schlimmsten Fall ist "fetch-once", Speichern und Wiederverwenden viel schneller.

Ich wäre versucht, die get zweimal, bis ich weiß, dass es ein Problem gibt... "Vorzeitige Optimierung" usw... Aber wenn ich es in einer engen Schleife verwenden würde, dann Ich könnte sie in einer Variablen speichern. Außer bei Length auf ein Array, das eine besondere JIT-Behandlung erfährt ;-p

2voto

Tor Haugen Punkte 19063

Die Speicherung des Wertes in einem Feld ist die schnellere Option.

Obwohl ein Methodenaufruf keinen großen Overhead verursacht, überwiegt er bei weitem das einmalige Speichern des Wertes in einer lokalen Variablen auf dem Stack und das anschließende Abrufen.

Ich für meinen Teil tue das konsequent.

2voto

Piotr Czapla Punkte 24266

Im Allgemeinen ist die zweite Variante schneller, da die erste Variante die Eigenschaft bei jeder Iteration neu berechnet. Hier ein Beispiel für etwas, das sehr viel Zeit in Anspruch nehmen kann:

 var d = new DriveInfo("C:");
 d.VolumeLabel; // will fetch drive label on each call

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