2013-03-03 5 views
23

Я использую оптимизированную версию алгоритма Левенштейна в некотором поисковом коде, который я строю. У меня есть функциональные модульные тесты для проверки того, что алгоритм возвращает правильные результаты, но в этом контексте производительность алгоритма также чрезвычайно важна.Как я могу оптимизировать производительность тестирования на C#?

Я ищу, чтобы добавить некоторое покрытие для проекта в проект, чтобы, если какие-либо будущие изменения влияют на оптимизацию, они будут отображаться как неудачные тесты - поскольку алгоритм детерминирован и работает с известными тестовыми данными, это может быть как подсчитано количество команд, выполненных для заданного набора тестовых входов. Другими словами, я не хочу измерять производительность алгоритма с помощью таймеров. Меня интересует фактическое тестирование внутреннего поведения алгоритма, а не только выход.

Любые идеи, как я мог бы это сделать в C# /. NET 4?

EDIT: Причина, по которой я не хочу просто использовать настенные часы, заключается в том, что она будет отличаться от нагрузки процессора и других факторов вне контроля теста. Это может привести к ошибкам, которые возникают, например, когда сервер сборки находится под нагрузкой. Там будет быть настенным монитором как часть развернутой системы.

EDIT 2: Подумайте об этом так: как бы вы применили red-> green-> refactor, когда производительность является критическим требованием?

+1

Количество выполненных инструкций не обязательно равноценно. Почему настенные часы не являются показателем интереса? –

+0

Согласовано - но если количество команд, выполненных в ответ на определенный набор входов, должно было резко измениться в результате изменения кода, я хотел бы узнать об этом. Это то, что я ищу. –

+2

Я бы не использовал модульные тесты для проверки этого. Если разработчики меняют рабочий, полный, высоко оптимизированный код, тогда они должны точно знать, что они делают. Не так ли вероятно, что этот код когда-нибудь будет «изменен» правильно? Так зачем беспокоиться? Производительность является нефункциональной характеристикой. –

ответ

30

Я собираюсь ответить на третью часть вашего вопроса, так как я делал это с некоторым успехом несколько раз.

как бы вы применили красный-> зеленый-> рефактор, когда производительность составляет критическое требование?

  1. Написать прижав тесты, чтобы поймать регрессии, за то, что вы планируете изменить и другие методы, которые могут замедлить в результате изменений.
  2. Напишите тест производительности, который терпит неудачу.
  3. Сделайте повышение производительности, часто выполняйте все тесты.
  4. Обновите свои тесты пиннинга, чтобы более точно приспособить производительность.

Запись прижав тесты

Создать вспомогательный метод как это на время, что вы хотите прикрепить.

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 для запуска таких тестов является действительно хорошим способом контроля регрессий производительности. По моему опыту есть много беспокойства о том, что они «случайно не работают из-за загрузки процессора из чего-то другого», чем есть фактические сбои. Успех такого рода усилий зависит в большей степени от командной культуры, чем от вашей способности осуществлять контроль над окружающей средой.

+0

Я не уверен, что понимаю ваш последний комментарий. Разумеется, все ** связано с наличием хорошо контролируемого CI, который гарантирует, что тесты производительности выполняются на определенной машине, по одному за раз, когда столько фоновых процессов и т. Д. Отключено, насколько это возможно? И, возможно, также обеспечение того, что, например, кеши очищаются до запуска. Если вы не выполняете большинство/все вышеперечисленное, я не вижу, как вы можете сделать значимую регрессию производительности (если вы не установили очень свободный порог). –

+0

Я думаю, что вы, вероятно, правы - особенно в отношении необходимости хорошо контролируемой подсистемы CI для мониторинга регрессий производительности. Я думаю, что специальный агент сборки TeamCity, вероятно, является хорошим подходом здесь - мы уже делали подобные вещи для таких вещей, как тесты на длительный период Selenium. Спасибо за такой всеобъемлющий ответ. –

+0

@OliCharlesworth, вы пробовали тесты производительности, подобные этому в CI? В первый раз, когда я это сделал, было ** много ** pushback. Дело в том, что, хотя он и работал, чтобы работать, было не так много работы, чтобы добиться их стабильности. Было гораздо больше работы, чтобы спорить с скептиками, прежде чем мы это попробовали. YMMV, большинство из этих тестов измеряют что-то порядка секунд, а не миллисекунд. Я ожидаю, что очень быстрые перфекционные тесты будут более хрупкими для внешних условий. – tallseth

Смежные вопросы