.NET 的 List 中提供了 ConvertAll
和 Select
兩個方法,在開發中實際上應該使用哪一個?
接下來透過基準測試指令碼來對比效能。
先編寫基準測試指令碼:
[MemoryDiagnoser]
public class BenchmarksTerrible
{
private readonly List<Order> _orders;
public BenchmarksTerrible()
{
var random = new Random(420);
_orders = Enumerable.Range(1, 100000).Select(_ => new Order { Status = random.Next().ToString() })
.ToList();;
}
public static OrderBasicInfo ConvertOrder(Order order) => new() { Status = order.Status };
public List<OrderBasicInfo> GetOrderBasicInfos() => _orders.ConvertAll(new Converter<Order, OrderBasicInfo>(ConvertOrder));
[Benchmark]
public List<OrderBasicInfo> ConvertAll()
{
return GetOrderBasicInfos();
}
[Benchmark]
public void Select()
{
var _ = _orders.Select(x => new OrderBasicInfo { Status = x.Status });
}
[Benchmark]
public List<OrderBasicInfo> SelectToList()
{
return _orders.Select(x => new OrderBasicInfo { Status = x.Status })
.ToList();
}
}
測試結果如下:
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
ConvertAll | 4,118,657.86 ns | 77,004.920 ns | 79,078.383 ns | 382.8125 | 375.0000 | 132.8125 | 3200166 B |
Select | 14.70 ns | 0.287 ns | 0.330 ns | 0.0076 | - | - | 72 B |
SelectToList | 4,115,770.49 ns | 68,067.640 ns | 63,670.513 ns | 382.8125 | 375.0000 | 132.8125 | 3200174 B |
然後將程式碼的 GetOrderBasicInfos
方法進行如下調整,重新測試。
public List<OrderBasicInfo> GetOrderBasicInfos() => _orders.ConvertAll(ConvertOrder);
測試結果如下:
Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
---|---|---|---|---|---|---|---|
ConvertAll | 4,160,022.71 ns | 57,100.202 ns | 50,617.842 ns | 382.8125 | 375.0000 | 132.8125 | 3200166 B |
Select | 14.49 ns | 0.209 ns | 0.174 ns | 0.0076 | - | - | 72 B |
SelectToList | 4,118,527.16 ns | 58,763.369 ns | 49,070.075 ns | 382.8125 | 375.0000 | 132.8125 | 3200174 B |
經過兩次測試可以發現,Select
方法的效能最好,因為它的執行時間最短,分配的記憶體最少,並且幾乎不需要垃圾回收。ConvertAll
和 SelectToList
方法的執行時間非常接近,SelectToList
稍好一些,但都比 Select 方法慢得多,且分配的記憶體較多,垃圾回收次數也較高。
因此,對於開頭的問題,我的結論是兩個方法的效能差不多,都可以使用。