[英]Why is my IEnumerable<String> using yield return slower to iterate then List<String>
我已經用我編寫的一些代碼測試了yield return
語句。 我有兩種方法:
public static IEnumerable<String> MyYieldCollection {
get
{
wrapper.RunCommand("Fetch First From Water_Mains");
for (int row = 0; row < tabinfo.GetNumberOfRows() ; row++) //GetNumberOfRows
//will return 1000+ most of the time.
{
yield return wrapper.Evaluate("Water_Mains.col1");
wrapper.RunCommand("Fetch Next From Water_Mains");
}
}
}
和
public static List<String> MyListCollection
{
get
{
List<String> innerlist = new List<String>();
wrapper.RunCommand("Fetch First From Water_Mains");
for (int row = 0; row < tabinfo.GetNumberOfRows(); row++)
{
innerlist.Add(wrapper.Evaluate("Water_Mains.col1"));
wrapper.RunCommand("Fetch Next From Water_Mains");
}
return innerlist;
}
}
然后我對每個集合使用一個foreach
循環:
foreach (var item in MyYieldCollection) //Same thing for MyListCollection.
{
Console.WriteLine(item);
}
有趣的是,由於某種原因,我似乎可以循環並比MyListCollection
更快地打印出完整的MyListCollection。
結果:
我真的看不出有什么原因,我是否缺少某些東西,或者這是正常現象嗎?
您如何安排時間? 您在調試器中嗎? 在調試模式下? 看起來您正在使用DataTable
,因此我將您的代碼用作測試裝備的模板(每次創建1000行),並在命令行的發布模式下使用了如下所述的工具; 結果如下(括號中的數字是檢查它們是否做相同的工作):
Yield: 2000 (5000000)
List: 2100 (5000000)
測試線束:
static void Main()
{
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
int count1 = 0;
var watch1 = Stopwatch.StartNew();
for(int i = 0 ; i < 5000 ; i++) {
foreach (var row in MyYieldCollection)
{
count1++;
}
}
watch1.Stop();
GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
int count2 = 0;
var watch2 = Stopwatch.StartNew();
for (int i = 0; i < 5000; i++)
{
foreach (var row in MyListCollection)
{
count2++;
}
}
watch1.Stop();
Console.WriteLine("Yield: {0} ({1})", watch1.ElapsedMilliseconds, count1);
Console.WriteLine("List: {0} ({1})", watch2.ElapsedMilliseconds, count2);
}
(請注意,您通常不應該使用GC.Collect
,但是它可以用於平整性能測試的字段)
為了避免重復,我所做的唯一更改是對for
循環:
int rows = tabinfo.Rows.Count;
for (int row = 0; row < rows; row++) {...}
所以我不會復制您的電話號碼...
如果循環的一次迭代很昂貴,而您只需要迭代集合中的幾個項目,會發生什么?
有了收益,您只需要為獲得的收益付費;)
public IEnumerable<int> YieldInts()
{
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(1000) // or do some other work
yield return i;
}
}
public void Main()
{
foreach(int i in YieldInts())
{
Console.WriteLine(i);
if(i == 42)
{
break;
}
}
}
我的猜測是,JIT可以更好地優化返回列表的版本中的for循環。 在返回IEnumerable的版本中,在for循環中使用的行變量現在實際上是所生成類的成員,而不是僅是方法本地的變量。
速度差異只有10%左右,因此除非這是性能關鍵代碼,否則我不會擔心。
據我了解,“收益回報”將一直循環運行,直到它運行我們要做的事情並且功能/屬性退出,並返回填充的IEnumarable。 換句話說,不是在foreach循環中為每個項目調用該函數,而是在執行一次foreach循環內的任何操作之前調用該函數一次。
可以根據返回的集合的類型。 列表的迭代速度可能比IEnumerable的任何數據結構都要快。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.