In-Memory-LINQ-Leistung

8

Bei LINQ to [Ihren bevorzugten Anbieter hier einfügen] handelt es sich um eine Frage zum Suchen oder Filtern von speicherinternen Sammlungen.

Ich weiß, dass LINQ (oder Erweiterungsmethoden für Suchen / Filtern) in Objekten funktioniert, die IEnumerable oder IEnumerable<T> implementieren. Die Frage ist: wegen der Art der Aufzählung, ist jede Abfrage Komplexität mindestens O (n) ?

Zum Beispiel:

%Vor%

In diesem Fall wird jeder Algorithmus mindestens O (n) annehmen, es sei denn, list ist in Bezug auf 'something' geordnet. In diesem Fall sollte die Suche O (log (n)) : Es sollte eine binäre Suche sein. Wenn ich jedoch richtig verstehe, wird diese Abfrage durch Enumeration aufgelöst, also sollte sein, auch in list wurde vorher bestellt.

  • Gibt es etwas, das ich tun kann, um eine Abfrage in O (log (n)) zu lösen?
  • Wenn ich Leistung möchte, sollte ich Array.Sort und Array.BinarySearch verwenden?
Pablo Marambio 27.09.2008, 16:29
quelle

3 Antworten

5

Auch bei Parallelisierung ist es immer noch O (n). Der konstante Faktor wäre unterschiedlich (abhängig von Ihrer Anzahl an Kernen), aber wenn n variiert, würde die Gesamtzeit immer noch linear variieren.

Natürlich könnten Sie Ihre eigenen Implementierungen der verschiedenen LINQ-Operatoren über Ihre eigenen Datentypen schreiben, aber sie wären nur in sehr spezifischen Situationen geeignet - Sie müssten sicher sein, dass das Prädikat nur auf der Basis des Operators funktioniert optimierte Aspekte der Daten. Zum Beispiel, wenn Sie eine Liste von Personen haben, die nach Alter sortiert ist, wird es Ihnen nicht mit einer Abfrage helfen, die versucht, jemanden mit einem bestimmten Namen zu finden:)

Um das Prädikat zu untersuchen, müssten Sie anstelle von Delegaten Ausdrucksbäume verwenden, und das Leben würde viel schwieriger werden.

Ich vermute, dass ich normalerweise neue Methoden hinzufügen würde, die es offensichtlich machen, dass Sie die indizierte / geordnete / was auch immer Art des Datentyps verwenden und welche immer angemessen funktionieren wird. Sie können natürlich nicht einfach diese zusätzlichen Methoden aus Abfrageausdrücken aufrufen, aber Sie können immer noch LINQ mit Punktnotation verwenden.

    
Jon Skeet 27.09.2008, 17:28
quelle
3

Ja, der generische Fall ist immer O (n), wie Sklivvz sagte.

Es gibt jedoch viele LINQ-Methoden, bei denen das Objekt, das IEnumerable implementiert, tatsächlich z. ICollektion. (Ich habe das für IEnumerable.Contains zumindest gesehen.)

In der Praxis bedeutet dies, dass LINQ IEnumerable.Contains die schnellen HashSet.Contains aufruft, wenn IEnumerable zum Beispiel ein HashSet ist.

%Vor%

Sie können reflector verwenden, um genau zu überprüfen, wie die LINQ-Methoden definiert sind. So habe ich das herausgefunden.

Oh, und auch LINQ enthält die Methoden IEnumerable.ToDictionary (weist den Schlüssel einem einzelnen Wert zu) und IEnumerable.ToLookup (weist den Schlüssel mehreren Werten zu). Diese Wörterbuch- / Nachschlagetabelle kann einmal erstellt und mehrmals verwendet werden, wodurch einige LINQ-intensive Codes um Größenordnungen beschleunigt werden können.

    
Tobi 27.09.2008 17:29
quelle
2

Ja, das muss es sein, denn die einzige Möglichkeit, auf ein Mitglied eines IEnumerable zuzugreifen, ist die Verwendung seiner Methoden, also O (n).

Es scheint ein klassischer Fall zu sein, in dem sich die Sprachdesigner dafür entschieden haben, Performance gegen Allgemeingültigkeit einzutauschen.

    
Sklivvz 27.09.2008 16:59
quelle