只有累積,沒有奇蹟

2022年11月28日 星期一

[Benchmark] String 比較字串效能分析

前言
在 .NET 開發時很常會用到字串的比對,C# 中提供多種字串比對的方式,這篇文章就列出在 C# 提供的字串比方法,針對這幾種常用的方法使用  BenchmarkDotNet  做效能的測試及比對並會在 .NET Framework 與 .NET Core 分別測試結果,若有問題或是錯誤的地方歡迎各位給予指導及討論

測試代碼 
測試使用  BenchmarkDotNet  進行測試的動作,如果沒聽過此套件可以參考介紹的文章 [.NETCore] 使用 BenchmarkDotNet 測試程式碼效能,這次測試的項目是字串的比對方式效能差異,另外在 stringBenchmark 類別加上  ClrJob,CoreJob  希望分別在 .NET Framework 與 .NET Core 測試效能差異,代碼如下 
namespace benchmarkLab
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<stringCompareBenchmark>();
            Console.ReadKey();
        }
    }

    [MemoryDiagnoser]
    [ClrJob,CoreJob]
    public class stringCompareBenchmark
    {
        public string string1 = "i am iron man";
        public string string2 = "I AM IRON MAN";

        // 測試代碼
    }
}
使用 Benchmark 測試數據,需要在方法上加上 Benchmark,使用 string 的比對測試的代碼如下
[Benchmark]
public void EqualityString()
{
    if (string1 == string2) ;
}
使用 string.Equals 的測試代碼如下
[Benchmark]
public void EqualString()
{
    if (string1.Equals(string2)) ;
}
使用 string.Compare 比對 string 的方式如下
[Benchmark]
public void CompareString()
{
    if (string.Compare(string1, string2) == 0) ;
}
使用 string.CompareOrdinal,測試的代碼如下
[Benchmark]
public void CompareOrdinalString()
{
    if (string.CompareOrdinal(string1, string2) == 0) ;
}
使用 string.compareTo,測試的代碼如下
[Benchmark]
public void CompareToString()
{
    if (string1.CompareTo(string2) == 0) ;
}
使用 string.Concat,測試的代碼如下
[Benchmark]
public void Concat()
{
    string s = string.Empty;
    s = string.Concat(firstWord, secondWord);
}
也可以使用 indexOf 方法找,因此也一併列出看看測試效能測試的代碼如下
[Benchmark]
public void IndexOfString()
{
    if (string1.IndexOf(string2) == 0) ;
}
接著調整到 Release mode,按下 F5 進行測試
測試完成後,會產生測試報各路徑在  release\BenchmarkDotNet.Artifacts\results  目錄底下,如下圖所示
根據測試結果
  • .NET Framework : 表現最差的為 IndexOf,表現最好的是 string.CompareOrdinal
  • .NET Core : 同樣表現最差的為 IndexOf,表現最好的是 string.CompareOrdinal

  • 感想
    透過結果得知,在 .NET Framework 與 .NET Core 環境中在字串比較上,效能最好的是 string.CompareOrdinal,== 與 string.Equals 緊追在後,IndexOf 則是在 .NET Framework 與 .NET Core 都墊底,另外在微軟 MSDN 在 .NET 中比較字串 文章中也有介紹常見的字串比對,有特別強調 CompareOrdinal 主要是用於排序或字串排序時。 不應該使用 String.CompareOrdinal 方法來測試是否相等
    得到最後的結論 compareOrdinal 雖然快,但 若要判斷兩個字串是否相等,請使用 String.Equals ,另外文章列出的 String.StartsWith、String.EndsWith 在本次比較中並未列出,原因是個人覺得在情境上與其他出發點不同,若是想了解更多各位客官也可自行測試看看,以上就是比較字串的結果,Happy Coding :)

    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 :)

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

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com