簡體   English   中英

List.Contains()的循環實現看起來比內置的快。 是嗎? 如果是這樣,為什么?

[英]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[]數組,這避免了將用於我的循環實現的索引范圍檢查。

我有三個問題:

  1. 任何人都可以復制我看到的結果嗎? (請記住在調試器外部運行發布版本。)
  2. 如果是這樣,有人可以解釋我的循環如何比List.Contains()快得多嗎?
  3. 如果沒有,任何人都可以解釋為什么我看到我的循環更快?

這不僅僅是學術上的興趣,因為我編寫的代碼可以處理大量的數字數據,並且需要盡可能快,這是我需要了解的事情。 (注意:是的,我描述了一些東西,只是嘗試優化需要優化的東西......我知道過早優化的問題。)

[編輯]

我發現這可能與處理器有關。 我嘗試過的所有系統都配備了英特爾處理器,雖然型號從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.sbrnull.s是相同的指令。

L_0000: ldarg.1 
L_0001: box !T
L_0006: brnull.s L_0021
...

基線JIT編譯器沒有優化盒子操作,但是我沒有用NGen或優化編譯器檢查它們是否存在。

您的循環實現產生與Contains相同的輸出,但您不能在通用情況下使用它。 即你必須最終使用Equals比較更復雜的對象。 Contains實現執行的工作比實現更多 ,所以我不明白為什么在這種情況下你應該期望它更快。

如果您有一個自定義Person對象的列表,並且覆蓋Equals方法以比較它們的Address Name SSNumberDateOfBirth ,則循環將以幾乎相同的性能成本執行。

我希望原始值,然后是循環迭代將勝過通用Contains ,但這是一個不成熟的優化,你不會(基本上)比Contains更好的對象進行更復雜的對象比較。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM