Почему на самом деле ваш бенчмарк — говно

6 декабря 2013

Ни для кого не секрет, что программисты очень любят бенчмарки. Ведь что может быть лучше, чем взглянуть на пару графиков, а потом с умным видом рассуждать о том, что Common Lisp быстрее PHP, а игры под FreeBSD идут быстрее, чем под Linux? А тем временем многие небезосновательно полагают, что бенчмаркам вообще нельзя верить. Я бы не был уж настолько категоричен, но на самом деле в этом есть существенная доля правды. Давайте разберемся, почему.

Но оговорим сначала, что в рамках данной заметки будет называться бенчмарком. Обычно под бенчмарком понимается некий тест, измеряющий скорость чего-то. Типа, «мы тут померили, и выяснили, что новая версия нашей СУБД держит до 100 000 транзакций в секунду». Или «мы тут передали гигабайтный файлик по сети и оказалось, что скорость составляет всего лишь 5 Мбит/сек». Однако с тем же успехом можно мерить не только производительность софта, железок и сетей, но и измерять объем оперативной памяти, используемый приложением, или, например, коэффициент сжатия архиватора. Возможно, вы не тестируете чью-то СУБД, а занимаетесь оптимизацией разрабатываемой вами программы. Ну или же пишите метрики, позволяющие находить в ней узкие места. Это все тоже бенчмарки.

Основная проблема с бенчмарками заключается в том, что при их проведении почти всегда совершаются одни и те же типичные ошибки. Вот некоторые из них.

1. Неповторяемость. Кто угодно должен иметь возможность повторить ваш бенчмарк. Это означает, что вы должны выложить исходный код приложения, которым производилось тестирование, архив с входными данными, привести характеристики машины, на которой проводился бенчмарк, версию используемых ОС, компиляторов, настройки приложения и так далее. Должна быть простая и понятная инструкция, описывающая, как повторить эксперимент. А теперь вспомните, как часто при проведении бенчмарка предоставляется вся эта информация? Если ее нет, бенчмарк нельзя повторить. Следовательно, его нельзя проверить. Следовательно, он не заслуживает доверия. Если видите бенчмарк, который нельзя легко и просто повторить, не верьте ему.

2. Вы измеряете не то, что думаете. Современные информационные системы сложны. Допустим, вы думаете, что добавили в программу метрику, измеряющую скорость вашего жесткого диска. На самом деле, скорее всего, вы измеряете скорость чтения из кэша файловой системы, в случае промаха — скорость чтения из кэша контроллера жесткого диска, а в случае очередного промаха — скорость последовательного чтения с жесткого диска. Эта скорость может очень, очень сильно отличаться, например, от скорости случайной записи при обходе всех кэшей. Аналогично при тестировании скорости языка программирования вы на самом деле можете тестировать скорость работы библиотеки PCRE или время инициализации виртуальной машины Java. Или вместо скорости обработки одного HTTP-запроса вы можете измерять время установления TCP-соединения. Чтобы понимать, что именно вы измеряете, нужно очень хорошо понимать устройство тестируемой системы. Много ли систем вы знаете действительно хорошо?

3. Взятие среднего. Не могу сосчитать, сколько раз на митингах я слышал что-то в стиле «давайте будем писать в Graphite среднее время выполнения запроса за минуту». Это грубейшая ошибка при измерении чего угодно. Ваше приложение может в среднем отвечать на запросы за 5 мс, но время от времени — за 10 секунд! Если запросов много, а пишется среднее, вы никогда не увидите этих пиков. Прямо как в анекдоте про среднюю температуру по больнице. Помните, среднее ни о чем не говорит, нужно смотреть на распределение. Другими словами, следует считать запросы, выполненные за 5 мс или быстрее, от 5 до 10 мс, от 10 мс до 20 мс, …., за 100 мс или дольше. Ну или хотя бы писать максимальное значение за интервал времени.

4. А кто будет бенчмаркать бенчмарки? Обязательно убедитесь, что во время проведения бенчмарка вы не уперлись в производительность самой программы для проведения бенчмарка. Например, если вы натравили на ваше приложение профайлер, причиной наблюдаемых этим профайлером тормозов может оказаться сам профайлер. Это еще одна причина, почему я не люблю fprof. Аналогично, если вы напихали в приложение слишком много метрик, производительность может упереться в сами метрики. Вот почему метрики весьма желательно слать по UDP. Если бенчмарк показывает, что ваше приложение не держит более 1000 запросов в секунду, возможно, это потому что программа, генерирующая нагрузку, работает в один поток. И так далее, и тому подобное.

5. Отсутствие анализа. Если программа работает медленно, разберитесь, почему она работает медленно. Что, если на самом деле мы уперлись в своп, и если дать программе немного больше оперативной памяти, она будет прекрасно работать? Что, если на самом деле проблема не в программе, а в пропускной способности сети? Если же вы занимаетесь оптимизацией программы, то просто обязаны понимать причину проблем с производительностью. В противном случае ваш процесс оптимизации будет проходить в стиле «хм, попробуем-ка впихнуть тут кэшик… не помогло, наверное просто JSON медленно кодируется, попробуем другую библиотеку…». Методом проб и ошибок вы будете оптимизировать вашу программу очень долго, заодно создав десяток новых бутылочных горлышек.

6. Игнорирование ошибок. Если тестируемая система держит сотни миллионов запросов в секунду, не спешите радоваться. Возможно, она просто отвечает на все ваши запросы сообщениями об ошибках. Аналогично, если система еле пашет, не спешите огорчаться. Возможно, вы просто ломитесь не на тот порт, или же порт закрыт фаерволом. Во время бенчмарка всегда считайте количество ошибок. Если это количество отлично от нуля, обязательно разберитесь, почему возникают ошибки.

7. Неправильные настройки. В реальных условиях системы эксплуатируется годами и сильно тюнится в процессе эксплуатации. При проведении бенчмарков ни у кого нет двух лишних недель на то, чтобы как следует разбираться в деталях настройки каждой из тестируемых систем. Особенно это касается, например, баз данных, настройки по умолчанию которых предполагают, что вы будете пытаться запустить их на микроволновке. Так что, если вы видите тестирование MySQL против PostgreSQL с настройками по умолчанию, тут же закрывайте страницу. И наоборот, если вы видите бенчмарк PostgreSQL, проведенный разработчиком этого PostgreSQL с двадцатилетним стажем, это вовсе не значит, что вы сможете заставить PostgreSQL работать так же быстро. Во всяком случае, не сразу.

8. Нетипичная нагрузка. Это, конечно, прекрасно, что ваша любимая СУБД держит миллион операций записи в секунду. Но проблема заключается в том, что операции чтения и записи в вашем приложении распределены в пропорции 90:10. То есть, чтение как бы намного важнее. И еще меня смущают объемы данных, на которых вы проводили тестирование. Учитывая, что они целиком помещаются в память, а ваша любимая СУБД активно использует mmap, возникает вопрос, что будет, когда рабочий набор перестанет помещаться в оперативную память? И да, быть может, эта ваша Java и умеет дробить числа в 10 раз быстрее Erlang’а, но кто в реальных приложениях на Java интенсивно считает факториалы? И почему когда я использую программы на Erlang, мне кажется, что они работают быстро, а когда я работаю с программами на Java, такого чувства ну никак не возникает? В общем, проводите бенчмарки на реальных нагрузках, на реальных объемах данных, решая реальные задачи. Никому не интересен сферический конь в вакууме.

9. Маркетинг и подгон. В реальном мире бенчмарки зачастую проводятся с пристрастием. Если производитель некого продукта захочет доказать, что его продукт лучше, чем аналог от конкурентов, он подберет подходящие для этого условия. А то и вовсе проплатит статью в подходящем журнале. Аналогично, если программист захочет доказать, что его любимый язык X лучше языка Y, он без труда найдет подходящую задачу. Намного более, чем вы могли бы подумать, распространен подгон продуктов под популярные бенчмарки. Мне, например, что называется, из первых уст, известно о том, как в свое время некий производитель компьютеров тюнил свои продукты под бенчмарки известного журнала. Нисколько не удивлюсь, если это практикуется многими производителями и нынче. Ну и если вы пишите некий софт, разумеется, довольно глупо тратить свое время, пытаясь подогнать его под бенчмарки, написанные вашей командой тестировщиков.

10. А как же другие параметры? Наконец, не стоит забывать, что на скорости, коэффициенте сжатия, или там объеме используемой памяти мир клином не сошлеся. Скорее всего, вы не захотите использовать архиватор, который жмет в 10 раз лучше gzip, если на решение задачи ему требуется около недели. Сишечка может быть намного быстрее, чем Erlang, но мне на работе без всяких там утечек памяти, переполнений буфера и проблем с переносимостью кода забот хватает. Oracle может быть во сколько угодно раз круче PostgreSQL, но у меня тупо нет денег на Oracle. Вообще, метрика «цена делить на производительность» очень полезна, советую держать ее на вооружении.

Следует также обратить внимание не следующее:

  • Как мы помним из лабораторных по физике, эксперименты нужно повторять не менее трех раз. Если программа то летает, то адово тупит, что-то здесь нечисто;
  • Бенчмарк должен быть легок в понимании и мерить что-то интересное. Мне ни о чем не говорит, что Nginx некое количество раз обратился к какой-то своей внутренней структуре, но я понимаю, что такое число запросов в секунду;
  • Хорошо, если один и тот же бенчмарк можно натравить на продукты разных версий или разных производителей;
  • Помните о различного рода побочных эффектах — качаемых торрентах, бэкапах БД, что стали внезапно писаться на диск, «шумных соседях», если ваше приложение работает в облаке, и так далее;
  • Некоторым системам нужно время на то, чтобы прогреться;
  • Не сравнивайте теплое с мягким. Если диск A работает намного быстрее диска B, но диск A тестируется в системе, где в два раза больше оперативки, возможно, вы сравниваете диск и кэш файловой системы. Если приложение нормально работает на реальных объемах данных на нормальной машине, но адово тупит на небольших объемах данных под тормозной виртуалкой, это ни о чем не говорит;
  • Уж лучше иметь хреновый бенчмарк, чем оптимизировать программу, полагаясь на «очевидно»;

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

Вот еще недавно нашел по теме: http://swizard.livejournal.com/187048.html

Метки: .

Подпишись через RSS, E-Mail, Google+, Facebook, Vk или Twitter!

Понравился пост? Поделись с другими: