Я собираюсь ответить на третью часть вашего вопроса, так как я делал это с некоторым успехом несколько раз.
как бы вы применили красный-> зеленый-> рефактор, когда производительность составляет критическое требование?
- Написать прижав тесты, чтобы поймать регрессии, за то, что вы планируете изменить и другие методы, которые могут замедлить в результате изменений.
- Напишите тест производительности, который терпит неудачу.
- Сделайте повышение производительности, часто выполняйте все тесты.
- Обновите свои тесты пиннинга, чтобы более точно приспособить производительность.
Запись прижав тесты
Создать вспомогательный метод как это на время, что вы хотите прикрепить.
private TimeSpan Time(Action toTime)
{
var timer = Stopwatch.StartNew();
toTime();
timer.Stop();
return timer.Elapsed;
}
Затем написать тест, который утверждает, ваш метод не занимает времени:
[Test]
public void FooPerformance_Pin()
{
Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0));
}
Когда он выходит из строя (с фактическим затраченным временем сообщения о неисправности), обновлять время с чем-то чуть больше, чем фактическое время. Повторите, и это пройдет. Повторите это для других функций, производительность которых может повлиять на ваши изменения, в результате чего-то вроде этого.
[Test]
public void FooPerformance_Pin()
{
Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0.8));
}
[Test]
public void BarPerformance_Pin()
{
Assert.That(Time(()=>fooer.Bar()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(6));
}
написать тест неудовлетворительной производительности
Мне нравится называть этот вид испытания на «травлю тест».Это всего лишь первый шаг теста пиннинга.
[Test]
public void FooPerformance_Bait()
{
Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0));
}
Теперь работа над улучшением производительности. Выполняйте все тесты (пиннинг и травление) после каждого предварительного улучшения. Если вы добьетесь успеха, вы увидите время, сбитое с выхода сбоя теста на изнашивание, и ни одно из ваших тестов на скрининг не завершится.
Если вас устраивают улучшения, обновите тест пиннинга для кода, который вы изменили, и удалите тест на изнашивание.
Что вы будете делать с этими тестами сейчас?
Самое меньшее, что нужно сделать, это отметить эти тесты атрибутом Explicit и сохранить их в следующий раз, когда вы захотите проверить производительность.
На противоположной стороне спектра работы создание разумно хорошо контролируемой подсистемы в CI для запуска таких тестов является действительно хорошим способом контроля регрессий производительности. По моему опыту есть много беспокойства о том, что они «случайно не работают из-за загрузки процессора из чего-то другого», чем есть фактические сбои. Успех такого рода усилий зависит в большей степени от командной культуры, чем от вашей способности осуществлять контроль над окружающей средой.
Количество выполненных инструкций не обязательно равноценно. Почему настенные часы не являются показателем интереса? –
Согласовано - но если количество команд, выполненных в ответ на определенный набор входов, должно было резко измениться в результате изменения кода, я хотел бы узнать об этом. Это то, что я ищу. –
Я бы не использовал модульные тесты для проверки этого. Если разработчики меняют рабочий, полный, высоко оптимизированный код, тогда они должны точно знать, что они делают. Не так ли вероятно, что этот код когда-нибудь будет «изменен» правильно? Так зачем беспокоиться? Производительность является нефункциональной характеристикой. –