День 1360. #Testing #Benchmark
Руководство по Бенчмарку в .NET. Окончание
Начало
Продолжение
Настройка и очистка
Иногда нужно написать какую-то логику, которая должна выполняться до или после бенчмарка, но мы не хотим, чтобы она участвовала в бенчмарке.
- Метод, помеченный атрибутом [GlobalSetup], будет выполняться только один раз для тестируемого метода после инициализации параметров бенчмарка и до всех вызовов тестового метода.
- Метод, помеченный атрибутом [GlobalCleanup], будет выполняться только один раз для тестируемого метода после всех вызовов тестового метода.
- Метод, помеченный атрибутом [IterationSetup], будет выполняться ровно один раз перед каждым вызовом теста. Не рекомендуется использовать его в микробенчмарках, так как это может испортить результаты. Однако, он может быть полезен, если тест занимает не менее 100 мс, и вы хотите подготовить некоторые данные перед каждым вызовом.
- Метод, отмеченный атрибутом [IterationCleanup], будет выполняться ровно один раз после каждого вызова. Этот атрибут также не рекомендуется использовать в микробенчмарках.
Валидация
Итак, бенчмарк позволяет сравнить между собой производительность методов, которые, по идее, должны делать одно и то же. Однако, если внимательно присмотреться к коду из первой части, можно заметить, что это не так. Метод StringBuilder() выводит лишнюю запятую в конце, в отличие от метода StringJoin(). Эта конкретная ошибка вряд ли сильно повлияла на результаты бенчмарка, но нам может и не повезти с этим в следующий раз.
Хотелось бы иметь какой-то автоматизированный тест, подтверждающий, что результат работы методов одинаковый.
BenchmarkDotNet умеет и это. Всё, что нам нужно сделать, это добавить ReturnValueValidator в наш тестовый класс, и все готово.
[ReturnValueValidator(failOnError: true)]
public class Benchmarks
{
// остальной код скрыт для краткости
// см. предыдущий пост
}
Теперь при попытке запуска нашего бенчмарка, если методы не возвращают одинаковый результат, мы получим ошибку:
// Validating benchmarks (Проверка бенчмарков):
Inconsistent benchmark return values in Benchmarks (Несогласованные возвращаемые значения в бенчмарках): StringJoin: 0, 1, 2, 3, 4, StringBuilder: 0, 1, 2, 3, 4,
* Здесь значения кажутся одинаковыми, но на самом деле это из-за того, что BenchmarkDotNet ставит запятую в сообщении об ошибке после результата первого метода. На самом деле это следует читать как:
StringJoin: "0, 1, 2, 3, 4", StringBuilder: "0, 1, 2, 3, 4,"
И бенчмарки не выполнятся. Нам нужно исправить ошибку и убедиться, что результаты обоих методов одинаковы.
Также существует BaselineValidator, который проверяет, что параметр Baseline=true атрибута Benchmark добавлен только в одном методе. Этот валидатор обязательный.
JitOptimizationsValidator проверяет, все ли зависимости проекта оптимизированы. По умолчанию отключен.
ExecutionValidator проверяет, могут ли исполниться все бенчмарки, исполняя все методы по одному разу перед запуском тестов. Если в каком-то из методов возникает ошибка, бенчмарки не запускаются. По умолчанию отключен.
Источники:
- https://blog.nimblepros.com/blogs/validating-benchmarks/
- https://benchmarkdotnet.org/articles/features/setup-and-cleanup.html
- https://benchmarkdotnet.org/articles/configs/validators.html