В более ранний пост Мы смотрели на производительность указателей функций, представленных в .NET 5/C# 9.0. Для этого мы использовали BenchmarkDotnet ( Документы ), Facto .net Bendcharking Framework. Это дробовик, чтобы заставить вас писать свои собственные тесты- в основном для тестирования регрессии производительности.
Основы
# Install benchmark template dotnet new -i BenchmarkDotNet.Templates # Create new benchmark project `benchmarks` dotnet new benchmark -o benchmarks --console-app -v 0.12.1
- Смотрите Параметры шаблона BenchmarkDotnet
В замешательстве вы найдете ссылки на установку BenchmarkDotnet. Инструмент
Глобальный инструмент, который устарел (см. Выпуск 1670 и PR 1572 ). Аналогично, шаблон проекта Dotnet BenchmarkDotnet. Шаблоны
по умолчанию: Net Core App 3.0 (которое является из поддержки ), и версия 0.12.0 BenchmarkDotnet (что не поддерживает Net 5.0 и может сбой с системой . NotsupportedException: неизвестно .NET Время выполнения ) Open
Benchmarks/Benchmarks.csproj и заменить:
-netcoreapp3.0 +net5.0
Шаблон довольно прост, так что его так же легко запустить dotnet new classlib -o тесты
и заполненный Benchmarks.csproj
с:
net5.0 Exe AnyCPU portable true true true Release
В Benchmarks/Program.cs
, замените основное тело следующим образом, чтобы получить такую же функциональность, как и контрольный CLI (от the Docs ):
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; namespace benchmarks { class Program { static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); } }
Создайте эталон в Benchmarks.cs
:
using BenchmarkDotNet; using BenchmarkDotNet.Attributes; namespace benchmarks { public class Delegate { [Benchmark] public void CallDelegate() { // Do nothing for now } } }
Попробуйте запустить тесты:
dotnet run --project ./Benchmarks/benchmarks.csproj -- --filter "benchmarks.*"
Если вы уйдете -Фильтр
Он будет ждать ввода от Stdin.
Чтобы увидеть список вариантов CLI:
dotnet run --project ./Benchmarks/benchmarks.csproj -- -h
Более
Неизменно, вы тестируете какую-то другую библиотеку, в отличие от написания автономных тестов. А затем в какой -то момент это может потерпеть неудачу с Ассамблея XXX, которая определяет контрольные показатели, не оптимизированные yyy
Анкет Все должно быть собрано для выпуска:
# Add a project reference to assembly we're benchmarking dotnet add benchmarks reference ./nng.NET.Shared/nng.NET.Shared.csproj # Note --configuration dotnet run --project ./Benchmarks/benchmarks.csproj --configuration release -- --filter "benchmarks.*"
Подобно другим структурам тестирования, вы можете иметь инициализацию и очистку (которая исключена из контрольных времен) с [GlobalSetUp]
/ [GlobalCleanup]
и [IterationSetup]
/ [IteersCleanup]
Для поведения для каждого класса и для для перспективы соответственно ( Docs ):
public class Delegate { delegate int nng_aio_set_output_delegate(nng_aio aio, int index, IntPtr arg); nng_aio_set_output_delegate nng_aio_set_output; [GlobalSetup] public void GlobalSetup() { var handle = DllImport.Load(); var ptr = NativeLibrary.GetExport(handle, "nng_aio_set_output"); nng_aio_set_output = Marshal.GetDelegateForFunctionPointer(ptr); } [Benchmark] public void CallDelegate() { var _ = nng_aio_set_output(nng_aio.Null, 9, IntPtr.Zero); } }
Есть два основных подхода к определению критериев:
- Конфигурации (в стиле свободного или объекта)
- Класс/функция атрибуты
Который позволяет настроить все виды интересных вещей: эталонные параметры, время выполнения рабочие места (например, .NET Версия , JIT/GC) и т. Д.
Если у вас есть несколько классов, результаты CLI распределены. Чтобы получить один, аккуратный стол в конце:
var config = ManualConfig.Create(DefaultConfig.Instance) .WithOptions(ConfigOptions.JoinSummary); BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
ManualConfig имеет еще много вариантов. Все в Конфигурация
может быть указан как CLI аргументы после -
Анкет Например, Inbonsummary
соответствует -JOIN
Анкет Ссылка --h
для других.
Быстро итерация за счет использования точности: --работа Сухой
, -Runonceperituration
, или какая -то комбинация -warmupcount 1-iterationcount 1-InvocationCount 1-UnrollFactor 1
Анкет
Отчеты
По умолчанию результаты CSV, HTML и Markdown записаны в BenchmarkDotnet. Артефакты/результаты/
Анкет Другие форматы возможны с -e
и базовый выходной путь может быть указан с помощью -a
/ -Артафакты
Анкет Отметка подходит для таких сообщений, как это, веб -интерфейсы CI и т. Д.:
BenchmarkDotNet=v0.12.1, OS=macOS 11.3.1 (20E241) [Darwin 20.4.0] Intel Core i5-8279U CPU 2.40GHz (Coffee Lake), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.101 [Host] : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT DefaultJob : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
16.934 нс | Делегат | 0.1687 нс | 16.23 | Calldelegate | 0,1578 нс | 0.23 |
6,713 нс | Dllimport | 0,0666 нс | 6.44 | Calldllimport | 0,0590 нс | 0.09 |
9.884 нс | Интерфейс | 0,1892 нс | 9.55 | Callinterfacetodllimport | 0.2652 нс | 0.35 |
6,764 нс | Указатель | 0.1519 нс | 6.49 | CallfunctionPointer | 0.1347 нс | 0.18 |
1.042 нс | Dllimport | 0.0125 нс | 1.00 | Callmanaged | 0.0110 нс | 0.00 |
Чтобы получить графики, установите R языка программирования и использовать -e rplot
Анкет Если rscript
в твоем Путь
Переменная среда, она автоматически выведет несколько файлов PNG, в противном случае вы можете вручную запустить Rscript benchmarkdotnet. Артефакты/результаты/сборки. R
:
Сравнения
Что «быстро» (или «медленно»)? Иногда критерии бесполезны без чего -либо, чтобы сравнить их.
Вы можете определить базовая линия :
[Benchmark(Baseline = true)] public void CallManaged() { var _ = Managed(IntPtr.Zero, 9, IntPtr.Zero); } // Prevent inline-ing [MethodImpl(MethodImplOptions.NoInlining)] static nint Managed(nint _unused0, int _unused1, nint _unused2) => 0;
Наличие базовой линии также дает столбцы «соотношение» и «соотношение» (stddev) на выходе.
BenchmarkDotnet не поддерживает и поддерживает Сравнивая результаты из разных прогонов (то есть до и после изменений кода), но вы можете использовать Результаты от .NET Project:
# Clone repo and build ResultsComparer git clone https://github.com/dotnet/performance dotnet_perf pushd dotnet_perf/src/tools/ResultsComparer dotnet build -c release popd # View CLI options dotnet ./dotnet_perf/artifacts/bin/ResultsComparer/Release/net5.0/ResultsComparer.dll -h # Run benchmarks with full JSON reports dotnet run --project ./Benchmarks/benchmarks.csproj -c release -- -f "benchmarks.*" -e fulljson # Compare with results in ./base dotnet ./dotnet_perf/artifacts/bin/ResultsComparer/Release/net5.0/ResultsComparer.dll --base ./base --diff ./BenchmarkDotNet.Artifacts --threshold '5%'
ResultsComparer требует «полных» отчетов JSON. Итак, вам придется сделать один из:
- Беги с
-e Fulljson
- Позвоните
AddExporter (BenchmarkDotnet. Экспортеры. Json. Jsonexporter. Полный)
на конфигурации - Применить
[BenchmarkDotnet. Атрибуты. Jsonexporterattribute. Полный]
атрибут
-throshold
Устанавливает разрешенное изменение между результатами. Это может быть процент или продолжительность времени, как 1 мс
Анкет
Stdout содержит результаты уценки:
тесты. Интерфейс. Callinterfacetodllimport | 112104.1 | 11.05 | 1239001.46 |
Ой, похоже, мой Thread.sleep (1)
имел измеримое воздействие.
Результаты Comprerer обладают неожиданным поведением. Он бросает исключение, когда в одной папке есть несколько отчетов, поэтому вам может потребоваться очистить результаты повторяющихся прогонов. И это не работает с результатами «сухой» рабочих мест.
CI
Мы стремимся добавить это как часть наших рабочих процессов Action GitHub, но это одинаково хорошо относится к любой другой платформе CI/CD.
Заманчиво провалить сборку/PR, если какой -либо эталон медленнее. Но иногда это неизбежно с исправлением ошибки или «правильной» реализацией. Если Мы все равно хотели сделать это, проверьте, является ли какой -либо тест «медленнее»:
# Output comparison as CSV `./csv` dotnet ResultsComparer.dll --base ./base --diff ./BenchmarkDotNet.Artifacts --threshold '5%' --csv ./csv # Parse csv, give it a header (it doesn't have one), and get 3rd column $column = Import-csv -Path ./csv -Delimiter ';' -Header 'Benchmark','Source','Result' ` | Select-Object 'Result' # Check for "Slower" ($column | % { $_.Result }).Contains("Slower")
Что нам действительно понравится, так это краткое изложение в комментариях о запросе на привлечение- аналогично нашим отчетам о покрытии кода. Есть несколько способов добавить комментарий из рабочего процесса:
- GitHub-Script Block
- скручивание/отдых
- Действие на рынке сообщества ( опция1 , опция2 )
Используя HTTP REST, PowerShell работает на всех платформах и не требует внешних зависимостей:
# Run ResultsComparer and capture markdown output $output = dotnet ResultsComparer.dll --base ./BenchmarkDotNet.Artifacts/ --diff ./diff --threshold '5%' # Convert string[] output to single string with line-breaks $body = $output -join '`n' $secure_token = ConvertTo-SecureString -String $GITHUB_TOKEN -AsPlainText # POST comment to PR Invoke-WebRequest ` -Method Post ` -Headers @{accept= 'application/vnd.github.v3+json'} ` -Uri https://api.github.com/repos/$USER/$REPO/issues/$PR_ID/comments ` -Body (ConvertTo-Json @{body=$body}) ` -Authentication OAuth -Token $secure_token
Где $ Pr_id
можно получить из $
в рабочем процессе.
Предполагая, что все правильно:
Теперь у нас есть куча, но не очень не остановились на том, как мы хотим, чтобы процесс работал. Похоже на ранее , мы можем прикрепить любой из сгенерированных отчетов с upload-artifact
/ Скачать-артифакт
или Загрузка-смягчение в качестве артефактов построения или выпуска активов соответственно. А затем генерируйте отчеты о сравнении. Но лучше всего использовать широкую гамму мелкозернистых критериев, чтобы поймать регрессии производительности в PRS, а затем меньшее количество более широких, обобщенных тестов, чтобы выделить изменения между выпусками. Это требует дополнительных экспериментов.
Оригинал: «https://dev.to/jeikabu/benchmarking-in-net-or-how-i-learned-to-stop-worrying-and-love-perf-regression-testing-55pn»