Warum ist Any langsamer als Enthält?

8

Ich habe den folgenden Test entworfen:

%Vor%

Die Ergebnisse, wenn null nicht vorhanden ist:

  • Contains False 00:00:00.0606484
  • Any == False 00:00:00.7532898
  • Any object.Equals False 00:00:00.8431783

Wenn null bei Element 4000 vorhanden ist:

  • Contains True 00:00:00.0494515
  • Any == True 00:00:00.5929247
  • Any object.Equals True 00:00:00.6700742

Wenn null bei Element 10 vorhanden ist:

  • Contains True 00:00:00.0038035
  • Any == True 00:00:00.0025687
  • Any True 00:00:00.0033769

Wenn also das Objekt in der Nähe der Front ist, ist Any etwas schneller; wenn es auf der Rückseite ist, ist es viel langsamer. Warum?

    
Maslow 05.02.2010, 15:40
quelle

4 Antworten

8

Any muss einen Delegaten für jedes von ihm überprüfte Element aufrufen (eine zusätzliche Anweisung callvirt , die wahrscheinlich nicht vom JIT in die Warteschlange gestellt wird). Contains führt diese Überprüfung nur durch. Deshalb ist Any langsamer. Ich vermute, die Tatsache, dass Any schneller aussieht als enthält, wenn das Element sehr früh gesehen wird, ist, dass der Benchmark dies nicht einfach widerspiegeln kann, da sie sehr nahe sind. Die Setup-Zeit für den Methodenaufruf ist der Hauptteil der in diesem Fall durchgeführten Arbeit (und nicht der eigentliche Suchvorgang).

%Vor%     
Mehrdad Afshari 05.02.2010, 15:41
quelle
4

Any ist langsamer, weil Contains auf den von Ihnen verwendeten Container (Array / List / etc) zugeschnitten ist. Es hat also nicht den Aufwand, ein IEnumerable zu starten, MoveNext () ständig aufzurufen usw.

Die Verwendung von Beliebig vereinfacht jedoch das Refactoring, da Sie diese Sammlung nur dann verwenden können, wenn Sie sie ändern. Daher würde ich sie nur in Contains ändern, wenn Sie über einen Profiler wissen, dass dies ein wichtiger Code ist. Und wenn dies der Fall ist, sollten Sie wahrscheinlich eine intelligentere Datenstruktur wie ein HashSet verwenden, da sowohl Any als auch Contains O (n) sind.

    
Paul Betts 05.02.2010 15:46
quelle
4

Wie andere Leute bereits bemerkt haben, sind die Methoden Contains und Any beide Erweiterungsmethoden von Enumerable. Der große Leistungsunterschied hat mehrere Gründe:

Zunächst geben Sie einen Delegaten für Any an, der für jedes Objekt aufgerufen werden muss, während die Contains-Methode nicht benötigt wird. Delegiertenanrufe sind ungefähr so ​​schnell wie ein Aufruf einer Schnittstellenmethode. Aus diesem Grund ist Any langsamer.

Als Nächstes, etwas, das andere Leute anscheinend übersehen haben, bietet die Contains-Erweiterungsmethode eine Leistungsoptimierung für Collections, die ICollection implementieren. Da object [] ICollection implementiert, führt der Aufruf der Erweiterungsmethode zu einem Methodenaufruf für das Array selbst. Intern verwendet diese Methode array.Contains eine einfache for-Schleife, um über das Array zu iterieren, um den Wert zu vergleichen. Dies bedeutet, Array Checks erfolgt nur einmal Iteration des Arrays.

Da die Any-Methode Ihren Delegaten aufrufen muss, ist eine Leistungsoptimierung wie bei der Contains-Methode nicht möglich. Dies bedeutet, dass die Any-Methode über die Auflistung mit der IEnumerable-Schnittstelle iteriert, was zu einem Schnittstellenaufruf + einem Array-Begrenzungs-Check + einem Delegat-Aufruf für jedes Element führt. Vergleichen Sie das mit dem Array. Enthält, wo keine Schnittstellenaufrufe, keine Delegataufrufe und eine Überprüfung einzelner Grenzen vorhanden sind.

[Aktualisierung]: Eine letzte Notiz. Der Grund, dass Any mit kleinen Sammlungen schneller ist (und in Ihrem Fall mit einem Nullwert am Anfang der Sammlung), hat mit der Umwandlung in ICollection zu tun, die Enumerable.Contains tut. Wenn Sie die Umwandlung in ICollection selbst durchführen, Ich sehe, dass der Aufruf von Contains schneller ist als Any:

%Vor%     
Steven 05.02.2010 16:36
quelle
2

Ich vermute, dass es etwas mit der Tatsache zu tun hat, dass Any eine Erweiterungsmethode ist, Teil der LINQ-Bibliothek und die Verwendung von Delegaten (über die Func & lt; & gt; -Syntax) beinhaltet. Jedes Mal, wenn Sie zu einer separaten Methode aufrufen müssen (insbesondere als Delegierter), wird es langsamer.

    
Adam Greene 05.02.2010 15:49
quelle

Tags und Links