alexeyfv

Опубликовано

- 2 мин чтения

Запускаем бенчмарки всего с одним C# файлом

C# .NET BenchmarkDotNet Performance Benchmarks
img of Запускаем бенчмарки всего с одним C# файлом

Если вы когда-нибудь задумывались, можно ли запустить бенчмарк, используя всего один C#-файл, то ответ: да, можно. Начиная с .NET 10, существует возможность создавать C#-приложения в одном *.cs‑файле. Проблема в том, что BenchmarkDotNet (BDN) не поддерживает такие бенчмарки с настройками по умолчанию. В этой статье я покажу, как обойти это ограничение, используя режим in-process.

Что такое однофайловые C#-приложения?

Однофайловые C#-приложения — это функционал, который появился в .NET 10 и позволяет поместить весь код в один *.cs-файл и запускать его напрямую:

  // HelloWorld.cs
Console.WriteLine("Hello, world!");

Запуск:

  dotnet HelloWorld.cs
# или
dotnet run HelloWorld.cs

Почему BenchmarkDotNet не работает «из коробки»?

По умолчанию BDN использует изоляцию на уровне процесса (process-level isolation): он генерирует, собирает и запускает отдельное консольное приложение для каждого бенчмарка. Такая изоляция обеспечивает более точные и стабильные измерения. В однофайловых приложениях .NET генерирует проект и собирает артефакты во временную папку. У BDN пока нет функционала для работы с такими проектами.

Обходим ограничение, используя in-process режим

Чтобы запустить бенчмарк в том же процессе, нужно использовать атрибут InProcess. Учтите, что такие измерения менее изолированы и на результаты может влиять хост-процесс.

Минимальный рабочий пример:

  #:package BenchmarkDotNet@0.15.8
#:property Optimize=true
#:property Configuration=Release
#:property PublishAot=false

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<Benchmarks>();

[InProcess]
public class Benchmarks
{
    [Benchmark]
    public Task Example() => Task.Delay(100);
}

Возможно вы заметили, что указаны флаги Optimize=true, Configuration=Release и PublishAot=false.

Флаги Optimize и Configuration нужны потому, что иначе .NET запустит бенчмарк в режиме Debug и без оптимизаций. А, как известно, бенчмарки в режиме Debug менее точные.

Флаг PublishAot отключает AOT, который для файловых приложений включён по умолчанию. AOT может быть полезен для уменьшения времени запуска вашего однофайлового приложения, и BDN поддерживает NativeAOT, но для этого требуется дополнительная настройка BDN.

Запуск бенчмарка

Чтобы запустить бенчмарк, выполните:

  dotnet run Benchmark.cs

Если у вас Linux или macOS, то можно использовать shebang. Сначала найдите путь до dotnet:

  which dotnet

Затем добавьте shebang в начало Benchmark.cs. В моём случае путь был /usr/bin/dotnet:

  #!/usr/bin/dotnet run
#:package BenchmarkDotNet@0.15.8
// ... остальной код

Сделайте файл исполняемым:

  chmod +x Benchmark.cs

И запустите:

  ./Benchmark.cs

Вне зависимости от способа запуска вы увидите примерно такие результаты:

  | Method  |     Mean |   Error |  StdDev |
| ------- | -------: | ------: | ------: |
| Example | 100.4 ms | 0.31 ms | 0.26 ms |

Вывод

Однофайловые C#-приложения можно использовать для бенчмаркинга, но для этого нужно запускать их в режиме in-process. Используя такой подход, помните, что такие бенчмарки менее изолированы и на результаты может влиять хост-процесс.