.NET Core 中的效能
大家好! 本文是我和我的同事在處理不同專案時長期使用的最佳實踐的集合。
有關執行計算的機器的資訊:BenchmarkDotNet=v0.11.5,作業系統=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R),1 個 CPU,8 個邏輯核心和 4 個實體核心
.NET核心SDK=3.0.100
[主機]:.NET Core 2.2.7(CoreCLR 4.6.28008.02、CoreFX 4.6.28008.03)、64位RyuJIT
核心:.NET Core 2.2.7(CoreCLR 4.6.28008.02、CoreFX 4.6.28008.03)、64 位元 RyuJIT
[主機]:.NET Core 3.0.0(CoreCLR 4.700.19.46205、CoreFX 4.700.19.46214)、64位RyuJIT
核心:.NET Core 3.0.0(CoreCLR 4.700.19.46205、CoreFX 4.700.19.46214)、64 位元 RyuJIT
作業=核心執行時間=核心
ToList 與 ToArray 和循環
我本來打算在.NET Core 3.0發佈時準備這些信息,但他們搶先了我,我不想竊取別人的榮耀並複製別人的信息,所以我只是指出
就我個人而言,我只想向您展示我的測量結果和結果;我為「C++ 風格」編寫循環的愛好者添加了反向循環。
代碼:
public class Bench
{
private List<int> _list;
private int[] _array;
[Params(100000, 10000000)] public int N;
[GlobalSetup]
public void Setup()
{
const int MIN = 1;
const int MAX = 10;
Random random = new Random();
_list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
_array = _list.ToArray();
}
[Benchmark]
public int ForList()
{
int total = 0;
for (int i = 0; i < _list.Count; i++)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForListFromEnd()
{
int total = 0;t
for (int i = _list.Count-1; i > 0; i--)
{
total += _list[i];
}
return total;
}
[Benchmark]
public int ForeachList()
{
int total = 0;
foreach (int i in _list)
{
total += i;
}
return total;
}
[Benchmark]
public int ForeachArray()
{
int total = 0;
foreach (int i in _array)
{
total += i;
}
return total;
}
[Benchmark]
public int ForArray()
{
int total = 0;
for (int i = 0; i < _array.Length; i++)
{
total += _array[i];
}
return total;
}
[Benchmark]
public int ForArrayFromEnd()
{
int total = 0;
for (int i = _array.Length-1; i > 0; i--)
{
total += _array[i];
}
return total;
}
}
.NET Core 2.2 和 3.0 中的效能速度幾乎相同。 以下是我在 .NET Core 3.0 中能夠獲得的內容:
我們可以得出結論,由於其內部最佳化和明確集合大小分配,數組集合的迭代處理速度更快。 另外值得記住的是,List 集合有其自身的優點,您應該根據所需的計算使用正確的集合。 即使您編寫了使用循環的邏輯,也不要忘記這是一個普通的循環,並且它也可能受到循環最佳化的影響。 habr上很久以前就發表過一篇文章:
扔
一年前,我在一家公司的一個遺留專案中工作,在該專案中,透過 try-catch- throw 結構來處理欄位驗證是正常的。 當時我已經明白這對專案來說是不健康的業務邏輯,所以只要有可能我就盡量不使用這樣的設計。 但讓我們弄清楚為什麼用這種結構來處理錯誤的方法是不好的。 我編寫了一個小程式碼來比較這兩種方法,並為每個選項制定了基準。
代碼:
public bool ContainsHash()
{
bool result = false;
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
return result;
}
public bool ContainsHashTryCatch()
{
bool result = false;
try
{
foreach (var file in _files)
{
var extension = Path.GetExtension(file);
if (_hash.Contains(extension))
result = true;
}
if(!result)
throw new Exception("false");
}
catch (Exception e)
{
result = false;
}
return result;
}
.NET Core 3.0 和 Core 2.2 的結果有類似的結果(.NET Core 3.0):
Try catch 會使程式碼更難理解並增加程式的執行時間。 但如果您需要這種構造,則不應插入那些預計不會處理錯誤的程式碼行 - 這將使程式碼更易於理解。 事實上,載入系統的並不是對異常的處理,而是透過 throw new Exception 構造拋出錯誤本身。
拋出異常比某些以所需格式收集錯誤的類別要慢。 如果您正在處理表單或某些數據,並且您清楚地知道錯誤應該是什麼,為什麼不處理它呢?
如果這種情況並非異常,則不應編寫 throw new Exception() 構造。 處理和拋出異常是非常昂貴的!
ToLower、ToLowerInvariant、ToUpper、ToUpperInvariant
在我 5 年的 .NET 平台工作經驗中,我遇到許多使用字串匹配的專案。 我還看到了下面的圖片:有一個企業解決方案有很多項目,每個項目執行字串比較的方式都不同。 但應該使用什麼以及如何統一呢? 在Richter的《CLR via C#》一書中,我讀到ToUpperInvariant()方法比ToLowerInvariant()方法更快的資訊。
書中摘錄:
當然,我不相信,決定在 .NET Framework 上執行一些測試,結果讓我震驚——效能提升了 15% 以上。 然後,第二天早上上班時,我向我的上級展示了這些測量結果,並允許他們存取原始程式碼。 此後,2 個項目中有 14 個進行了更改以適應新的測量,考慮到這兩個項目的存在是為了處理巨大的 Excel 表,結果對於產品來說意義重大。
我還向大家展示了不同版本的 .NET Core 的測量結果,以便大家可以選擇最佳的解決方案。 我只想補充一點,在我工作的公司中,我們使用 ToUpper() 來比較字串。
代碼:
public const string defaultString = "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";
[Benchmark]
public bool ToLower()
{
return defaultString.ToLower() == defaultString.ToLower();
}
[Benchmark]
public bool ToLowerInvariant()
{
return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
}
[Benchmark]
public bool ToUpper()
{
return defaultString.ToUpper() == defaultString.ToUpper();
}
[Benchmark]
public bool ToUpperInvariant()
{
return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
}
在 .NET Core 3.0 中,每個方法的增加約為 x2,並平衡了它們之間的實作。
層級編譯
在我的上一篇文章中,我簡要地描述了這個功能,我想糾正和補充我的話。 多層編譯可以加快解決方案的啟動時間,但您會犧牲部分程式碼將在背景編譯成更最佳化的版本,這可能會帶來少量開銷。 隨著 NET Core 3.0 的出現,啟用了層編譯的專案的建置時間已減少,並且與該技術相關的錯誤已修復。 先前,該技術會導致 ASP.NET Core 中的首次請求出錯,並在多層編譯模式下的首次建置期間凍結。 目前,它在 .NET Core 3.0 中預設為啟用,但您可以根據需要停用它。 如果您處於團隊領導、高階、中階或部門負責人的位置,那麼您必須了解快速的專案開發可以增加團隊的價值,而這項技術可以讓您為開發人員節省時間以及專案本身的時間。
.NET 升級
升級您的 .NET Framework/.NET Core 版本。 通常,每個新版本都會提供額外的效能提升並添加新功能。
但具體有什麼好處呢? 讓我們看看其中的一些:
- .NET Core 3.0 引入了 R2R 映像,這將減少 .NET Core 應用程式的啟動時間。
- 在 2.2 版本中,出現了 Tier Compilation,因此程式設計師將花費更少的時間啟動專案。
- 支援新的 .NET 標準。
- 支援新版本的程式語言。
- 優化,每個新版本都會對基礎庫 Collection/Struct/Stream/String/Regex 等進行最佳化,並進行更多改進。 如果您從 .NET Framework 遷移到 .NET Core,您將獲得開箱即用的巨大效能提升。 作為範例,我附加了 .NET Core 3.0 中新增的一些最佳化的連結:
https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/
結論
在編寫程式碼時,值得關注專案的不同方面,並使用程式語言和平台的功能來實現最佳結果。 如果您分享與 .NET 優化相關的知識,我將非常高興。