技術(shù)頻道導(dǎo)航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運(yùn)營(yíng)

贊助商

分類目錄

贊助商

最新文章

搜索

[實(shí)踐]使用BenchmarkDotNet對(duì).NET代碼進(jìn)行基準(zhǔn)測(cè)試

作者:admin    時(shí)間:2023-5-13 10:12:25    瀏覽:

將你的代碼包裝在計(jì)時(shí)器中并運(yùn)行它幾萬(wàn)次,這種做法并不完全可靠。你可能會(huì)陷入太多的陷阱,從而完全扭曲你的結(jié)果。在這種情況下,使用 BenchmarkDotNet 進(jìn)行代碼基準(zhǔn)測(cè)試是最好的選擇。 參閱文章:

代碼基準(zhǔn)

代碼基準(zhǔn)測(cè)試是指你想要將兩段代碼/方法相互比較。這是量化代碼重寫或重構(gòu)的好方法,它將成為 BenchmarkDotNet 最常見(jiàn)的用例。

首先,創(chuàng)建一個(gè)空白的 .NET Core 控制臺(tái)應(yīng)用程序?,F(xiàn)在,大多數(shù)這些“應(yīng)該”在使用 .NET Full Framework 時(shí)也有效,但我將在 .NET Core 中完成這里的所有操作。

接下來(lái),你需要從包管理器控制臺(tái)運(yùn)行以下命令來(lái)安裝 BenchmarkDotNet nuget 包:

Install-Package BenchmarkDotNet

接下來(lái)我們需要構(gòu)建我們的代碼。為此,我們將使用經(jīng)典的“大海撈針”。我們將在 C# 中構(gòu)建一個(gè)包含隨機(jī)項(xiàng)目的大型列表,并在列表中間放置一個(gè)“針”。然后我們將比較在列表上執(zhí)行“SingleOrDefault”與“FirstOrDefault”的效果。這是我們的完整代碼:

using System;
using System.Collections.Generic;
using System.Linq;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace BenchmarkExample
{
    public class SingleVsFirst
    {
        private readonly List<string> _haystack = new List<string>();
        private readonly int _haystackSize = 1000000;
        private readonly string _needle = "needle";

        public SingleVsFirst()
        {
            //Add a large amount of items to our list. 
            Enumerable.Range(1, _haystackSize).ToList().ForEach(x => _haystack.Add(x.ToString()));
            //Insert the needle right in the middle. 
            _haystack.Insert(_haystackSize / 2, _needle);
        }

        [Benchmark]
        public string Single() => _haystack.SingleOrDefault(x => x == _needle);

        [Benchmark]
        public string First() => _haystack.FirstOrDefault(x => x == _needle);

    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<SingleVsFirst>();
            Console.ReadLine();
        }
    }
}

稍微了解一下,首先我們創(chuàng)建一個(gè)類來(lái)保存我們的基準(zhǔn)。這可以包含任意數(shù)量的私有方法,并且可以在構(gòu)造函數(shù)中包含設(shè)置代碼。構(gòu)造函數(shù)中的任何代碼都不包括在方法的計(jì)時(shí)中。然后我們可以創(chuàng)建公共方法并添加[Benchmark]的屬性, 將它們列為應(yīng)該進(jìn)行比較和基準(zhǔn)測(cè)試的項(xiàng)目。

最后,在我們控制臺(tái)應(yīng)用程序的主要方法中,我們使用“BenchmarkRunner”類來(lái)運(yùn)行我們的基準(zhǔn)測(cè)試。

運(yùn)行基準(zhǔn)測(cè)試工具時(shí)的注意事項(xiàng)。它必須以“發(fā)布”模式構(gòu)建,并從命令行運(yùn)行。你不應(yīng)該使用從 Visual Studio 運(yùn)行的基準(zhǔn)測(cè)試,因?yàn)樗€附加了一個(gè)調(diào)試器并且未編譯為“優(yōu)化”。要從命令行運(yùn)行,請(qǐng)前往你的應(yīng)用程序 bin/Release/netcoreappxx/ 文件夾,然后運(yùn)行 ??dotnet {你的dll名}.dll

結(jié)果呢?

 Method |      Mean |    StdDev |    Median |
------- |----------:|----------:|----------:|
 Single | 15.591 ms | 0.4429 ms | 15.507 ms |
  First |  7.638 ms | 0.4399 ms |  7.475 ms |

所以看起來(lái) SingleFirst 慢兩倍!如果你了解 Single 在幕后做了什么,這是可以預(yù)料的。當(dāng) First 找到一個(gè)項(xiàng)目時(shí),它會(huì)立即返回(畢竟,它只想要“第一個(gè)”項(xiàng)目)。然而,當(dāng) Single 找到一個(gè)項(xiàng)目時(shí),它仍然需要遍歷整個(gè)列表的其余部分,因?yàn)槿绻卸鄠€(gè),它需要拋出異常。當(dāng)我們將項(xiàng)目放在列表中間時(shí),這是有道理的!

輸入基準(zhǔn)

假設(shè)我們發(fā)現(xiàn) SingleFirst 慢。我們有一個(gè)關(guān)于為什么會(huì)這樣的理論(那個(gè) Single 需要繼續(xù)通過(guò)列表),然后我們可能需要一種方法來(lái)嘗試不同的“配置”,而不必在更改次要細(xì)節(jié)的情況下重新運(yùn)行測(cè)試。為此,我們可以使用 BenchmarkDotNet 的“輸入”功能。

讓我們稍微修改一下我們的代碼:

using System;
using System.Collections.Generic;
using System.Linq;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace BenchmarkExample
{
    public class SingleVsFirst
    {
        private readonly List<string> _haystack = new List<string>();
        private readonly int _haystackSize = 1000000;

        public List<string> _needles => new List<string> { "StartNeedle", "MiddleNeedle", "EndNeedle" };

        public SingleVsFirst()
        {
            //Add a large amount of items to our list. 
            Enumerable.Range(1, _haystackSize).ToList().ForEach(x => _haystack.Add(x.ToString()));

            //One at the start. 
            _haystack.Insert(0, _needles[0]);
            //One right in the middle. 
            _haystack.Insert(_haystackSize / 2, _needles[1]);
            //One at the end. 
            _haystack.Insert(_haystack.Count - 1, _needles[2]);
        }

        [ParamsSource(nameof(_needles))]
        public string Needle { get; set; }

        [Benchmark]
        public string Single() => _haystack.SingleOrDefault(x => x == Needle);

        [Benchmark]
        public string First() => _haystack.FirstOrDefault(x => x == Needle);

    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<SingleVsFirst>();
            Console.ReadLine();
        }
    }
}

我們?cè)谶@里所做的是創(chuàng)建一個(gè)“_needles”屬性來(lái)保存我們可能希望找到的不同針頭。我們已將它們插入列表中的不同索引處。然后,我們創(chuàng)建一個(gè)具有 ParamsSource 屬性的“Needle”屬性。這告訴 BenchmarkDotNet 循環(huán)遍歷這些值并為每個(gè)可能的值運(yùn)行不同的測(cè)試。

一個(gè)重要的提示是 ParamsSource 必須是公共的。

運(yùn)行這個(gè),我們的報(bào)告現(xiàn)在看起來(lái)像這樣:

 Method |       Needle |             Mean |           StdDev |
------- |------------- |-----------------:|-----------------:|
 Single |    EndNeedle | 19,741,752.75 ns | 1,078,431.672 ns |
  First |    EndNeedle | 18,422,088.07 ns |   998,023.064 ns |
 Single | MiddleNeedle | 19,326,424.98 ns | 1,356,796.153 ns |
  First | MiddleNeedle |  9,586,518.55 ns |   649,534.186 ns |
 Single |  StartNeedle | 18,509,550.74 ns | 1,113,976.063 ns |
  First |  StartNeedle |         77.90 ns |         7.782 ns |

這有點(diǎn)難看,因?yàn)槲覀儸F(xiàn)在根據(jù)“First”返回 StartNeedle 所需的時(shí)間減少到納秒。但是結(jié)果很明顯。

運(yùn)行 Single 時(shí),返回針?biāo)璧臅r(shí)間是相同的,無(wú)論它在列表中的哪個(gè)位置。而 First 的響應(yīng)時(shí)間完全取決于項(xiàng)目在列表中的位置。

輸入功能可以極大地幫助理解應(yīng)用程序在給定不同輸入的情況下如何或?yàn)槭裁磿?huì)變慢。例如,當(dāng)密碼較長(zhǎng)時(shí),你的密碼散列函數(shù)會(huì)變慢嗎?或者它根本不是一個(gè)因素?

創(chuàng)建基線

最后一個(gè)有用的提示只是在報(bào)告上創(chuàng)建一個(gè)漂亮的小“乘數(shù)”,就是將你的基準(zhǔn)之一標(biāo)記為“基線”。如果我們回到我們的第一個(gè)例子(沒(méi)有輸入),我們只需要像這樣將我們的一個(gè)基準(zhǔn)標(biāo)記為基線: [Benchmark(Baseline = true)]

現(xiàn)在,當(dāng)我們以標(biāo)記為基線的“First”運(yùn)行測(cè)試時(shí),輸出現(xiàn)在如下所示:

 Method |     Mean | Scaled |
------- |---------:|-------:|
 Single | 22.77 ms |   1.99 |
  First | 11.42 ms |   1.00 |

所以現(xiàn)在更容易看出我們的其他方法變慢(或變快)的“因素”。在這種情況下,我們的 Single 調(diào)用幾乎是 First 調(diào)用的兩倍。

總結(jié)

作為程序員,我們喜歡看到微小的變化如何突飛猛進(jìn)地提高性能,本文介紹了使用 BenchmarkDotNet 對(duì)代碼進(jìn)行基準(zhǔn)測(cè)試的方法,希望本文對(duì)你在對(duì)C#的代碼性能測(cè)試方面有所幫助。

相關(guān)文章

x
  • 站長(zhǎng)推薦
/* 左側(cè)顯示文章內(nèi)容目錄 */