2013-04-09 2 views
2

이 열거 형 중 하나가 다른 것보다 빠르거나 거의 같은 것입니까? (C에서 # 예)foreach Dictionary <>. Values ​​또는 List <>를 열거하는 .NET 수집이 더 빠릅니다.

사례 1 :

Dictionary<string, object> valuesDict; 

// valuesDict loaded with thousands of objects 

foreach (object value in valuesDict.Values) { /* process */ } 

케이스 2 :

List<object> valuesList; 

// valuesList loaded with thousands of objects 

foreach (object value in valuesList) { /* process */ } 

UPDATE :

배경 :

사전은 다른 키잉 검색 유리할 (목록을 반복하는 것과는 대조적으로) iterat라면 이점은 줄어들 것입니다 사전을 통하는 것은 목록을 통과하는 것보다 훨씬 느립니다.

업데이트 : 많은 조언을 듣고 직접 테스트했습니다.

첫째, 결과입니다. 다음은 프로그램입니다.

대하여 반복 전체 컬렉션 딕셔너리 78 Keyd 131 목록 : 76

키 입력 됨 컬렉션 딕셔너리 : 178 Keyd : 194 목록 : 142,800

using System; 
using System.Linq; 

namespace IterateCollections 
{ 
    public class Data 
    { 
     public string Id; 
     public string Text; 
    } 

    public class KeyedData : System.Collections.ObjectModel.KeyedCollection<string, Data> 
    { 
     protected override string GetKeyForItem(Data item) 
     { 
      return item.Id; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var dict = new System.Collections.Generic.Dictionary<string, Data>(); 
      var list = new System.Collections.Generic.List<Data>(); 
      var keyd = new KeyedData(); 

      for (int i = 0; i < 10000; i++) 
      { 
       string s = i.ToString(); 
       var d = new Data { Id = s, Text = s }; 
       dict.Add(d.Id, d); 
       list.Add(d); 
       keyd.Add(d); 
      } 

      var sw = new System.Diagnostics.Stopwatch(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in dict.Values) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var dictTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in keyd) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var keydTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       foreach (Data d in list) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      var listTime = sw.ElapsedMilliseconds; 

      Console.WriteLine("Iterate whole collection"); 
      Console.WriteLine("Dict: " + dictTime); 
      Console.WriteLine("Keyd: " + keydTime); 
      Console.WriteLine("List: " + listTime); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = dict[s]; 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      dictTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 1000; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = keyd[s]; 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      keydTime = sw.ElapsedMilliseconds; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < 10; r++) 
      { 
       for (int i = 0; i < 10000; i += 10) 
       { 
        string s = i.ToString(); 
        Data d = list.FirstOrDefault(item => item.Id == s); 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      listTime = sw.ElapsedMilliseconds * 100; 

      Console.WriteLine("Keyed search collection"); 
      Console.WriteLine("Dict: " + dictTime); 
      Console.WriteLine("Keyd: " + keydTime); 
      Console.WriteLine("List: " + listTime); 

     } 
    } 

}

업데이트 :

@Blam이 제안한 KeyedCollection과 Dictionary의 비교.

가장 빠른 방법은 KeyedCollection 항목의 배열을 반복하는 것입니다.

그러나 사전 값을 반복하는 것이 배열로 변환하지 않고 KeyedCollection보다 더 빠릅니다.

사전 값을 반복하는 것은 사전 컬렉션보다 훨씬 빠르고 빠릅니다.

Iterate 1,000 times over collection of 10,000 items 
    Dictionary Pair: 519 ms 
Dictionary Values: 95 ms 
    Dict Val ToArray: 92 ms 
    KeyedCollection: 141 ms 
    KeyedC. ToArray: 17 ms 

타이밍은 Windows 콘솔 응용 프로그램 (릴리스 빌드)에서입니다. 다음은 소스 코드입니다.

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace IterateCollections 
{ 
    public class GUIDkeyCollection : System.Collections.ObjectModel.KeyedCollection<Guid, GUIDkey> 
    { 
     // This parameterless constructor calls the base class constructor 
     // that specifies a dictionary threshold of 0, so that the internal 
     // dictionary is created as soon as an item is added to the 
     // collection. 
     // 
     public GUIDkeyCollection() : base() { } 

     // This is the only method that absolutely must be overridden, 
     // because without it the KeyedCollection cannot extract the 
     // keys from the items. 
     // 
     protected override Guid GetKeyForItem(GUIDkey item) 
     { 
      // In this example, the key is the part number. 
      return item.Key; 
     } 

     public GUIDkey[] ToArray() 
     { 
      return Items.ToArray(); 
     } 

     //[Obsolete("Iterate using .ToArray()", true)] 
     //public new IEnumerator GetEnumerator() 
     //{ 
     // throw new NotImplementedException("Iterate using .ToArray()"); 
     //} 
    } 
    public class GUIDkey : Object 
    { 
     private Guid key; 
     public Guid Key 
     { 
      get 
      { 
       return key; 
      } 
     } 
     public override bool Equals(Object obj) 
     { 
      //Check for null and compare run-time types. 
      if (obj == null || !(obj is GUIDkey)) return false; 
      GUIDkey item = (GUIDkey)obj; 
      return (Key == item.Key); 
     } 
     public override int GetHashCode() { return Key.GetHashCode(); } 
     public GUIDkey(Guid guid) 
     { 
      key = guid; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      const int itemCount = 10000; 
      const int repetitions = 1000; 
      const string resultFormat = "{0,18}: {1,5:D} ms"; 

      Console.WriteLine("Iterate {0:N0} times over collection of {1:N0} items", repetitions, itemCount); 

      var dict = new Dictionary<Guid, GUIDkey>(); 
      var keyd = new GUIDkeyCollection(); 

      for (int i = 0; i < itemCount; i++) 
      { 
       var d = new GUIDkey(Guid.NewGuid()); 
       dict.Add(d.Key, d); 
       keyd.Add(d); 
      } 

      var sw = new System.Diagnostics.Stopwatch(); 
      long time; 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (KeyValuePair<Guid, GUIDkey> w in dict) 
       { 
        if (null == w.Value) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dictionary Pair", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in dict.Values) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dictionary Values", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in dict.Values.ToArray()) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "Dict Val ToArray", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in keyd) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "KeyedCollection", time); 

      sw.Reset(); 
      sw.Start(); 
      for (int r = 0; r < repetitions; r++) 
      { 
       foreach (GUIDkey d in keyd.ToArray()) 
       { 
        if (null == d) throw new ApplicationException(); 
       } 
      } 
      sw.Stop(); 
      time = sw.ElapsedMilliseconds; 
      Console.WriteLine(resultFormat, "KeyedC. ToArray", time); 
     } 
    } 

} 
+0

두 번째는 더 빨리, 가장 가능성이, 같은 사전 값이 드문 드문 것 같습니다 여기

는 코드입니다. 구현에 따라 목록 값이 더 좋을 것입니다. – Lucas

+0

최적화 된 Linq 쿼리를 사용하지 않는 특별한 이유가 있거나 응용 프로그램의 타이밍을 밀리 세컨드로 줄일 수 있습니까? – Jasper

+8

조기 미세 최적화는 악합니다. – JustAnotherUserYouMayKnow

답변

8

이 스톱워치 확인하는 것이 상대적으로 쉽다 :

var d = new Dictionary<string, object>(); 
var s = new List<object>(); 
for (int i =0 ; i != 10000000 ; i++) { 
    d.Add(""+i, i); 
    s.Add(i); 
} 
var sw = new Stopwatch(); 
sw.Start(); 
foreach(object o in d.Values) { 
    if (o == null) throw new ApplicationException(); 
} 
sw.Stop(); 
Console.WriteLine(sw.ElapsedMilliseconds); 
sw.Reset(); 
sw.Start(); 
foreach (object o in s) { 
    if (o == null) throw new ApplicationException(); 
} 
sw.Stop(); 
Console.WriteLine(sw.ElapsedMilliseconds); 

서로 합리적으로 가까운이 인쇄 번호 :

Dict List 
---- ---- 
136 107 
139 108 
136 108 

List 항상이기는하지만, 여백 두 데이터 구조의 상대적 복잡성을 감안할 때 기대만큼 크지는 않습니다.

5

거의 같은 시간입니다. 물론 프로세스에 코드가 포함 된 경우에는 눈에 띄지 않습니다.

하지만 인터넷에서 무작위로 들리는 이유는 무엇입니까? 그것을 시험해 보라!

Stopwatch 클래스가 유용 할 수 있습니다.

4

키순으로 검색하려면 사전을 누릅니다.
키에 의한 사전 검색은 매우 빠르게 수행됩니다.

foreach의 차이는 미미합니다.

키는 속성 인 경우

KeyedCollection Class

누구의 키 값을 을 포함하는 컬렉션에 대한 추상 기본 클래스를 제공합니다 KeyedCollection
을 고려하십시오.

Servy이 기능 컬렉션을 선택 주석로서 당신은
.NET 많은 컬렉션을 가지고해야합니다.
System.Collections Namespaces

개체에 Int32로 표시 할 수있는 자연 키가있는 경우 OverRide GetHashCode()를 고려하십시오.

당신의 개체 GUID의 자연 키가 다음 KeyedCollection을 고려하고 재정의 GetHashCode 및

그리고 키가 아닌 특성에 seach에 대한

오히려 휴식과를 ForEach보다 LINQ를 고려 같음이있는 경우;

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Collections.ObjectModel; 
using System.Diagnostics; 

namespace IntIntKeyedCollection 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 

      Guid findGUID = Guid.NewGuid(); 
      GUIDkeyCollection gUIDkeyCollection = new GUIDkeyCollection(); 
      gUIDkeyCollection.Add(new GUIDkey(findGUID)); 
      gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid())); 
      gUIDkeyCollection.Add(new GUIDkey(Guid.NewGuid())); 
      GUIDkey findGUIDkey = gUIDkeyCollection[findGUID]; // lookup by key (behaves like a dict) 
      Console.WriteLine(findGUIDkey.Key); 
      Console.WriteLine(findGUIDkey.GetHashCode()); 
      Console.WriteLine(findGUID); 
      Console.WriteLine(findGUID.GetHashCode()); 
      Console.ReadLine(); 
     } 
     public class GUIDkeyCollection : KeyedCollection<Guid, GUIDkey> 
     { 
      // This parameterless constructor calls the base class constructor 
      // that specifies a dictionary threshold of 0, so that the internal 
      // dictionary is created as soon as an item is added to the 
      // collection. 
      // 
      public GUIDkeyCollection() : base() { } 

      // This is the only method that absolutely must be overridden, 
      // because without it the KeyedCollection cannot extract the 
      // keys from the items. 
      // 
      protected override Guid GetKeyForItem(GUIDkey item) 
      { 
       // In this example, the key is the part number. 
       return item.Key; 
      } 
     } 
     public class GUIDkey : Object 
     { 
      private Guid key; 
      public Guid Key 
      { 
       get 
       { 
        return key; 
       } 
      } 
      public override bool Equals(Object obj) 
      { 
       //Check for null and compare run-time types. 
       if (obj == null || !(obj is GUIDkey)) return false; 
       GUIDkey item = (GUIDkey)obj; 
       return (Key == item.Key); 
      } 
      public override int GetHashCode() { return Key.GetHashCode(); } 
      public GUIDkey(Guid guid) 
      { 
       key = guid; 
      } 
     } 
    } 
} 
+0

왜 투표가 늦습니까? – Paparazzi

+0

@JustAnotherUserYouMayKnow 그가 끝에 만든 편집을보세요. – Paparazzi

+0

@JustAnotherUserYouMayKnow이 답변의 요점은 당신이 필요로하는 컬렉션 유형을 사용해야한다는 것입니다. 그리고 둘 다 당신의 요구에 정확하게 부합하는 위치에 있지 않기 때문에 속도를 비교할 필요가 없습니다. 더 빠르지 만 문제를 해결하지 못하는 코드가 아니라 올바른 코드를 사용합니다. – Servy

2

여기 테스트입니다 : 당신이 당신의 사전에서 충돌을 해싱 한 경우 당신은 또한 테스트해야하지만

class speedtest 
{ 
    static void Main(string[] args) 
    { 
     int size = 10000000; 
     Dictionary<string, object> valuesDict = new Dictionary<string, object>(); 
     List<object> valuesList = new List<object>(); 

     for (int i = 0; i < size; i++) 
     { 
      valuesDict.Add(i.ToString(), i); 
      valuesList.Add(i); 
     } 
     // valuesDict loaded with thousands of objects 

     Stopwatch s = new Stopwatch(); 
     s.Start(); 
     foreach (object value in valuesDict.Values) { /* process */ } 
     s.Stop(); 

     Stopwatch s2 = new Stopwatch(); 
     s2.Start(); 
     foreach (object value in valuesList) { /* process */ } 
     s.Stop(); 
     Console.WriteLine("Size {0}, Dictonary {1}ms, List {2}ms",size,s.ElapsedMilliseconds,s2.ElapsedMilliseconds); 

     Console.ReadLine(); 
    } 
} 

Outputs: 
Size 10000000, Dictonary 73ms, List 63ms 

. 그들은 당신에게 다른 결과를 줄 수 있습니다.

실제 응용 프로그램의 경우 구조 생성, 액세스 및 메모리 발자국 지출을 고려해야합니다.

1

다른 사람들이 말했듯이, 조기 최적화 yadda yadda. 올바른 시나리오에 적합한 컬렉션을 사용하고 문제가되는 경우 속도에 대해서만 염려하십시오.

어쨌든 측정하는 것만 알면됩니다. 나는 30,000 개의 객체를 가진 사전과 목록을 채우고 10,000 번 반복하는 신속하고 지저분한 테스트를 만들었습니다. 결과는 다음과 같습니다

사전 : 2683ms
목록 : 1955ms

그래서, Dictionary.Values은 어떤 이유로 열거 약간 느린 것으로 보인다.

void Main() 
{ 
    int i = 0; 

    var dict = new Dictionary<string, object>(); 
    var list = new List<object>(); 

    for (i = 0; i < 30000; i++) 
    { 
     var foo = new Foo(); 
     dict.Add(i.ToString(), foo); 
     list.Add(foo); 
    } 

    var dictWatch = new Stopwatch(); 

    dictWatch.Start(); 

    for (i = 0; i < 10000; i++) 
    { 
     foreach (var foo in dict.Values) {} 
    } 

    dictWatch.Stop(); 
    Console.WriteLine("Dictionary: " + dictWatch.ElapsedMilliseconds); 

    var listWatch = new Stopwatch(); 
    listWatch.Start(); 

    for (i = 0; i < 10000; i++) 
    { 
     foreach (var foo in list) {} 
    } 

    listWatch.Stop(); 

    Console.WriteLine("List: " + listWatch.ElapsedMilliseconds); 
} 

class Foo {} 
관련 문제