c# library Ist die Reihenfolge der LINQ-Funktionen wichtig?




c# lync (6)

Grundsätzlich, wie die Frage sagt ... ist die Reihenfolge der LINQ-Funktionen in Bezug auf die Leistung wichtig? Offensichtlich müssten die Ergebnisse noch identisch sein ...

Beispiel:

myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);

Beide geben mir die gleichen Ergebnisse zurück, befinden sich jedoch in einer anderen LINQ-Reihenfolge. Mir ist klar, dass das Umordnen einiger Elemente zu unterschiedlichen Ergebnissen führen wird, und ich mache mir keine Sorgen darüber. Worauf es bei mir am meisten ankommt, ist zu wissen, ob die Bestellung die Leistung beeinträchtigen kann, wenn die gleichen Ergebnisse erzielt werden. Und zwar nicht nur bei den 2 LINQ-Anrufen, die ich gemacht habe (OrderBy, Where), sondern auch bei allen LINQ-Anrufen.


Answer #1

In Ihrem speziellen Beispiel kann es für die Leistung einen Unterschied machen.

Erste Abfrage: Ihr OrderBy Aufruf muss die gesamte OrderBy durchlaufen, einschließlich der Elemente, für die Code 3 oder weniger ist. Die Where Klausel muss dann auch die gesamte geordnete Sequenz durchlaufen.

Zweite Abfrage: Der Where Aufruf begrenzt die Sequenz auf nur die Elemente, bei denen Code größer als 3 ist. Der OrderBy Aufruf muss dann nur die reduzierte Sequenz durchqueren, die von Where call zurückgegeben wird.


Answer #2

Beachten Sie, dass Sie bei der Optimierung einer LINQ-Abfrage vorsichtig sein sollten. Wenn Sie beispielsweise die deklarative Version von LINQ verwenden, gehen Sie folgendermaßen vor:

public class Record
{
    public string Name { get; set; }
    public double Score1 { get; set; }
    public double Score2 { get; set; }
}


var query = from record in Records
            order by ((record.Score1 + record.Score2) / 2) descending
            select new
                   {
                       Name = record.Name,
                       Average = ((record.Score1 + record.Score2) / 2)
                   };

Wenn Sie sich aus irgendeinem Grund dafür entscheiden, die Abfrage zu "optimieren", indem Sie zuerst den Durchschnitt in eine Variable speichern, erhalten Sie nicht die gewünschten Ergebnisse:

// The following two queries actually takes up more space and are slower
var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            order by average descending
            select new
                   {
                       Name = record.Name,
                       Average = average
                   };

var query = from record in Records
            let average = ((record.Score1 + record.Score2) / 2)
            select new
                   {
                       Name = record.Name,
                       Average = average
                   }
            order by average descending;

Ich weiß, dass nicht viele Leute deklarative LINQ für Objekte verwenden, aber es ist ein gutes Denkanstoß.


Answer #3

Ja.

Aber genau dieser Leistungsunterschied hängt davon ab, wie der zugrunde liegende Ausdrucksbaum vom LINQ-Provider ausgewertet wird.

Zum Beispiel kann Ihre Abfrage beim zweiten Mal (mit der WHERE-Klausel zuerst) schneller für LINQ-to-XML ausgeführt werden, aber schneller beim ersten Mal für LINQ-to-SQL.

Um herauszufinden, was genau der Leistungsunterschied ist, möchten Sie Ihre Anwendung wahrscheinlich profilieren. Wie immer bei solchen Dingen ist eine vorzeitige Optimierung in der Regel nicht die Mühe wert - Sie könnten also feststellen, dass andere Probleme als die LINQ-Leistung wichtiger sind.


Answer #4

Dies hängt vom verwendeten LINQ-Anbieter ab. Für LINQ to Objects könnte das sicherlich einen großen Unterschied machen. Angenommen, wir haben tatsächlich:

var query = myCollection.OrderBy(item => item.CreatedDate)
                        .Where(item => item.Code > 3);

var result = query.Last();

Dazu muss die gesamte Sammlung sortiert und gefiltert werden. Wenn wir eine Million Elemente hätten, von denen nur eines einen Code größer als 3 hätte, würden wir viel Zeit damit verschwenden, Ergebnisse zu ordnen, die weggeworfen würden.

Vergleichen Sie das mit der umgekehrten Operation und filtern Sie zuerst:

var query = myCollection.Where(item => item.Code > 3)
                        .OrderBy(item => item.CreatedDate);

var result = query.Last();

Dieses Mal bestellen wir nur die gefilterten Ergebnisse, die im Beispielfall "nur ein einziges Objekt, das zum Filter passt", viel effizienter sein wird - sowohl zeitlich als auch räumlich.

Es könnte auch einen Unterschied machen, ob die Abfrage korrekt ausgeführt wird oder nicht. Erwägen:

var query = myCollection.Where(item => item.Code != 0)
                        .OrderBy(item => 10 / item.Code);

var result = query.Last();

Das ist in Ordnung - wir wissen, dass wir niemals durch 0 teilen werden. Aber wenn wir die Reihenfolge vor der Filterung ausführen, wird die Abfrage eine Ausnahme auslösen.


Answer #5

Offensichtlich müssten die Ergebnisse noch identisch sein ...

Beachten Sie, dass dies nicht wirklich zutrifft - insbesondere ergeben die folgenden zwei Zeilen unterschiedliche Ergebnisse (für die meisten Provider / Datasets):

myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);

Answer #6

Es hängt von der Relevanz ab. Angenommen, Sie haben nur sehr wenige Elemente mit Code = 3, dann funktioniert die nächste Bestellung mit einem kleinen Satz von Sammlungen, um die Reihenfolge nach Datum zu erhalten.

Wenn Sie jedoch viele Artikel mit demselben CreatedDate haben, funktioniert die nächste Bestellung mit einem größeren Sammlungssatz, um die Bestellung nach Datum zu erhalten.

In beiden Fällen wird es also einen Leistungsunterschied geben





linq