2 回答

TA貢獻1998條經(jīng)驗 獲得超6個贊
如果您使用產(chǎn)量測量版本而不具體化列表,它將比其他版本具有優(yōu)勢,因為它不必分配和調(diào)整大列表的大小(以及觸發(fā) GC)。
根據(jù)您的編輯,我想添加以下內(nèi)容:
但是,請記住,從語義上講,您正在查看兩種不同的方法。一個產(chǎn)生一個集合。它的大小是有限的,您可以存儲對集合的引用、更改其元素并共享它。
另一個產(chǎn)生一個序列。它可能是無限的,每次迭代它都會得到一個新副本,并且它后面可能有也可能沒有集合。
它們不是同一件事。編譯器不會創(chuàng)建集合來實現(xiàn)序列。如果您通過在幕后實現(xiàn)集合來實現(xiàn)序列,您將看到與使用列表的版本相似的性能。
BenchmarkDotNet 默認(rèn)情況下不允許您對延遲執(zhí)行進行計時,因此您必須構(gòu)建一個使用我在下面所做的方法的測試。我通過 BenchmarkDotNet 運行它并得到以下結(jié)果。
Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
------------- |---------:|---------:|---------:|------------:|------------:|------------:|--------------------:|
ConsumeYield | 475.5 us | 7.010 us | 6.214 us | - | - | - | 40 B |
ConsumeList | 958.9 us | 7.271 us | 6.801 us | 285.1563 | 285.1563 | 285.1563 | 1049024 B |
注意分配。在某些情況下,這可能會有所不同。
我們可以通過分配正確的大小列表來抵消一些分配,但最終這不是蘋果對蘋果的比較。下面的數(shù)字。
Method | Mean | Error | StdDev | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
------------- |---------:|----------:|----------:|------------:|------------:|------------:|--------------------:|
ConsumeYield | 470.8 us | 2.508 us | 2.346 us | - | - | - | 40 B |
ConsumeList | 836.2 us | 13.456 us | 12.587 us | 124.0234 | 124.0234 | 124.0234 | 400104 B |
代碼如下。
[MemoryDiagnoser]
public class Test
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Test>();
}
public int Size = 100000;
[Benchmark]
public int ConsumeYield()
{
var sum = 0;
foreach (var x in CreateNumbersYield()) sum += x;
return sum;
}
[Benchmark]
public int ConsumeList()
{
var sum = 0;
foreach (var x in CreateNumbersList()) sum += x;
return sum;
}
public IEnumerable<int> CreateNumbersYield() //for yield
{
for (int i = 0; i < Size; i++) yield return i;
}
public IEnumerable<int> CreateNumbersList() //for list
{
var list = new List<int>();
for (int i = 0; i < Size; i++) list.Add(i);
return list;
}
}

TA貢獻1836條經(jīng)驗 獲得超4個贊
您必須考慮以下幾點:
List<T>
消耗內(nèi)存,但您可以一次又一次地迭代它而無需任何額外資源。為了達到同樣的效果yield
,您需要通過 實現(xiàn)序列ToList()
。生產(chǎn)時最好設(shè)置容量
List<T>
。這將避免內(nèi)部數(shù)組調(diào)整大小。
這是我所擁有的:
class Program
{
static void Main(string[] args)
{
// warming up
CreateNumbersYield(1);
CreateNumbersList(1, true);
Measure(null, () => { });
// testing
var size = 1000000;
Measure("Yield", () => CreateNumbersYield(size));
Measure("Yield + ToList", () => CreateNumbersYield(size).ToList());
Measure("List", () => CreateNumbersList(size, false));
Measure("List + Set initial capacity", () => CreateNumbersList(size, true));
Console.ReadLine();
}
static void Measure(string testName, Action action)
{
var sw = new Stopwatch();
sw.Start();
action();
sw.Stop();
Console.WriteLine($"{testName} completed in {sw.Elapsed}");
}
static IEnumerable<int> CreateNumbersYield(int size) //for yield
{
for (int i = 0; i < size; i++)
{
yield return i;
}
}
static IEnumerable<int> CreateNumbersList(int size, bool setInitialCapacity) //for list
{
var list = setInitialCapacity ? new List<int>(size) : new List<int>();
for (int i = 0; i < size; i++)
{
list.Add(i);
}
return list;
}
}
結(jié)果(發(fā)布版本):
Yield completed in 00:00:00.0001683
Yield + ToList completed in 00:00:00.0121015
List completed in 00:00:00.0060071
List + Set initial capacity completed in 00:00:00.0033668
如果我們比較可比較的情況(Yield + ToList& List + Set initial capacity),yield速度要慢得多。
- 2 回答
- 0 關(guān)注
- 123 瀏覽
添加回答
舉報