只有累積,沒有奇蹟

2022年11月15日 星期二

[NETCore] 動態 String 字串相加效能比較

前言
在前一篇 [.NETCore] String 字串相加效能比較 對於 C# string 的應用做了一些測試,得到在使用固定字串相加時使用 string 效能反而比 stringBuilder 來的好,在 string  有多種應用情境因此這篇就在針對另一種使用情境針對 string 動態文字相加做比較若有問題或是錯誤的地方歡迎各位給予指導及討論

測試代碼 
測試方式同樣以  BenchmarkDotNet  套件協助進行測試,對此套件有興趣可以參考介紹的文章 [.NETCore] 使用 BenchmarkDotNet 測試程式碼效能,上一篇介紹是使用固定字串相加做測試,這一篇則是在每一個測試方法中跑一個 for 迴圈將結果相加起來,同樣的測試 C# 中 string、stringBuilder、string.Format、Interpolation、string.Concat、string.Join 等字串相加的方法,BenchmarkDotnet 使用上相當容易,直接透過 BenchmarkRunner.Run<T> 靜態方法將要測試的類別帶入 T 即可,測試 Framework 希望得知動態字串在 .NET Framework 與 .NET Core 效能表現,因此在測試類別上加上  ClrJob,CoreJob  ,除了速度之外對於 Memory 以及 GC 的使用量也是使用上要考量的重點之一,可以加上  MemoryDiagnoser  觀察記憶體與 GC 的數據,代碼如下 
namespace benchmarkLab
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<stringTestBenchmark>();
            Console.ReadKey();
        }
    }

    [MemoryDiagnoser]
    [ClrJob,CoreJob]
    public class stringTestBenchmark
    {       
        StringBuilder _sb = new StringBuilder();
        // 測試代碼
    }
}
使用 Benchmark 測試數據需要在方法上加上   Benchmark  attribute,使用 string 的相加測試的代碼如下
[Benchmark]
public void Normal()
{
    string s = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        s += i.ToString();
    }
}
使用 stringBuilder 的相加測試的代碼如下
[Benchmark]
public void StringBuilder()
{
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10; i++)
    {
        sb.Append(i.ToString());
    }
    string s = sb.ToString();
}
另外一種使用 stringBuilder 的方式,將其宣告為區域變數使用前先 clear (很少看到有人這樣使用,但想測試看看在此應用情境速度是否會提升),測試的代碼如下
[Benchmark]
public void StringBuilderClear()
{
    _sb.Clear();
    for (int i = 0; i < 10; i++)
    {
        _sb.Append(i.ToString());
    }
    string s = _sb.ToString();
}
使用 string.Format,測試的代碼如下
[Benchmark]
public void Format()
{
    string s = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        s = string.Format("{0}{1}", s, i.ToString());
    }
}
使用 C# 6.0 提供的新語法 Interpolation,測試的代碼如下
[Benchmark]
public void Interpolation()
{
    string s = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        s = $"{s}{i.ToString()}";
    }
}
使用 string.Concat,測試的代碼如下
[Benchmark]
public void Concat()
{
    string s = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        s = string.Concat(s, i.ToString());
    }
}
使用 string.Join,測試的代碼如下
[Benchmark]
public void Join()
{
    string s = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        s += string.Join(s, i.ToString());
    }
}
好的,完成了測試代碼接著將專案設定為 Release mode,按下 F5 進行測試,可以看到執行 Console 一開始會說明 BenchmarkDotnet 將會進行 14 項任務 (2 Framework * 7 種方法) 的測試,測試時間會根據你要測試的項目多寡而不同,以本次測試來說在小弟電腦大約12-15 分鐘左右
測試完成後,會產生測試報各路徑在  release\BenchmarkDotNet.Artifacts\results  目錄底下
測試報告最上方為測試主機的硬體資訊,小弟筆電為 Intel Core i7-8550、16 G,Windows 10 環境,也會列出測試的 Framework 相關資訊,像是測試時是在 .NET Framework 4.7 與 .NET Core 2.2 進行測試,下方為在 string 中測試動態字串相加 的測試結果


  • .NET Framework : 就像大家所知的,動態字串相加表現最好的為 stringBuilder,在執行時間與使用記憶體用量數據都是優於其他方法;string.Format 則是最後一名
  • .NET Core : 與 .NET Framework 相同表現最好的為 stringBuilder,相同代碼在 .NET Core 執行速度提升一倍 (除了 string.Format ),速度上最慢的依舊為 string.Format,恭喜 string.Format 在各項測試蟬聯墊底

  • 感想
    多年前曾經拜讀過黑暗大文章 StringBuilder串接字串的迷思,因此進行測試前心理大致有個答案,從以上測試結果來看,stringBuilder 在動態字串相加時效能比 string 好同樣可套用到 .NET Core 上面,另外 string.Format 不管是在動態或是靜態字串相加效能都是墊底,但在可讀性上是比較好的 (大家比較習慣?),日後在 Code Review 時並依據當下情境做適合的應用,Happy Coding :)

    0 意見:

    張貼留言

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com