[英]Loop implementation of List.Contains() appears faster than the built-in one. Is it? If so, why?
( 這個問題來自於此處開始的討論 )
我正在比較使用List.Contains()
在List<bool>
查找true
值的時間與手動循環的時間。
我看到其他人報道的結果不同。 我已經在幾個系統上嘗試過它,在我嘗試過的所有系統上,循環看起來要快2到3.5倍。 這些系統的范圍從運行XP。4的筆記本電腦到運行Windows 8和.Net 4.5的最新PC。
其他人報告了不同的結果,即List.Contains()
與循環的速度大致相同或略快。
這是我的測試代碼。
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main()
{
int size = 10000000;
int count = 10;
List<bool> data = new List<bool>(size);
for (int i = 0; i < size; ++i)
data.Add(false);
var sw = new Stopwatch();
for (int trial = 0; trial < 5; ++trial)
{
sw.Restart();
for (int i = 0; i < count; ++i)
TestViaLoop(data);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds + " TestViaLoop()");
sw.Restart();
for (int i = 0; i < count; ++i)
TestViaListContains(data);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds + " TestViaListContains()");
Console.WriteLine();
}
}
static bool TestViaLoop(List<bool> data)
{
for (int i = 0; i < data.Count; ++i)
if (data[i])
return true;
return false;
}
static bool TestViaListContains(List<bool> data)
{
return data.Contains(true);
}
}
}
要測試此代碼,您應該將其編譯為x86 RELEASE構建,並從調試器外部運行它。
以下是我使用.Net 4.5框架的Windows 8 x64 PC的結果(盡管我在.Net 4中獲得了類似的結果):
Times are in milliseconds
126 TestViaLoop()
441 TestViaListContains()
122 TestViaLoop()
428 TestViaListContains()
131 TestViaLoop()
431 TestViaListContains()
138 TestViaLoop()
426 TestViaListContains()
122 TestViaLoop()
439 TestViaListContains()
如您所見,循環在我的系統上花費大約1/3的時間。
現在,如果我們使用Resharper
來查看List.Contains()
的實現,它看起來像這樣:
bool Contains(T item)
{
if (item == null)
{
for (int j = 0x0; j < this._size; j++)
{
if (this._items[j] == null)
{
return true;
}
}
return false;
}
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0x0; i < this._size; i++)
{
if (comparer.Equals(this._items[i], item))
{
return true;
}
}
return false;
}
雖然它使用Comparer.Equals()
(它應該使它比循環慢),但它也直接使用private _items[]
數組,這避免了將用於我的循環實現的索引范圍檢查。
我有三個問題:
List.Contains()
快得多嗎? 這不僅僅是學術上的興趣,因為我編寫的代碼可以處理大量的數字數據,並且需要盡可能快,這是我需要了解的事情。 (注意:是的,我描述了一些東西,只是嘗試優化需要優化的東西......我知道過早優化的問題。)
[編輯]
我發現這可能與處理器有關。 我嘗試過的所有系統都配備了英特爾處理器,雖然型號從3.8GHz的四核到1.6GHz的奔騰M單核不等......
對於那些看到循環運行較慢的人,你在運行英特爾處理器嗎?
它使用GenericEqualityComparer,如果我們看一下Equals方法的實現是這樣的:
public override bool Equals(T x, T y)
{
if ((object) x != null)
{
if ((object) y != null)
return x.Equals(y);
else
return false;
}
else
return (object) y == null;
}
當它檢查對象是否不等於null時,它會使它們裝箱並且你得到兩個裝箱操作。 這個IL代碼顯示了它的外觀:
IL_0002: box !T
IL_0007: ldnull
IL_0008: ceq
編輯280Z28:相同方法的CIL在.NET 4.5中略有不同。
public override bool Equals(T x, T y)
{
if (x != null)
return ((y != null) && x.Equals(y));
if (y != null)
return false;
return true;
}
這是IL。 對於任何查看Reflector的人,請注意brfalse.s
和brnull.s
是相同的指令。
L_0000: ldarg.1
L_0001: box !T
L_0006: brnull.s L_0021
...
基線JIT編譯器沒有優化盒子操作,但是我沒有用NGen或優化編譯器檢查它們是否存在。
您的循環實現產生與Contains
相同的輸出,但您不能在通用情況下使用它。 即你必須最終使用Equals
比較更復雜的對象。 Contains
實現執行的工作比實現更多 ,所以我不明白為什么在這種情況下你應該期望它更快。
如果您有一個自定義Person
對象的列表,並且覆蓋Equals
方法以比較它們的Address
Name
SSNumber
和DateOfBirth
,則循環將以幾乎相同的性能成本執行。
我希望原始值,然后是循環迭代將勝過通用Contains
,但這是一個不成熟的優化,你不會(基本上)比Contains
更好的對象進行更復雜的對象比較。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.