Файлы режима бенчмарка что это

Обновлено: 04.07.2024

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

AnTuTu Benchmark

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

3DMark Benchmark

Бенчмарк с тестами, которые показывают скорость устройства под большой нагрузкой. Еще дает статистику, как менялась скорость смартфона с каждым обновлением ОС

Geekbench 5

Комплексное тестирование производительности во время разных процессов — вроде рендеринга видео и обработки HDR. Есть тесты в одноядерном и многоядерном режимах

PCMark for Android Benchmark

Тестирует расход заряда и памяти устройства. Разряжает телефон до 20%, чтобы проверить производительность с запущенными приложениями в фоне. Бенчмарк критикуют за нестабильную работу

Basemark GPU

Совмещает универсальность AnTuTu и игровую направленность 3DMark. Тесты проводятся в окне с требовательной 3Д-графикой . Из минусов: приложение нестабильное и запрашивает личные данные

Linpack

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

Больше материалов о гаджетах

В нашем потоке «Технологии». Какие устройства стоит купить и как выжать максимум из вашего телефона и компьютера

Цены действительны на момент публикации

Теперь вы знаете, как проверить производительность своего телефона. Осталось узнать:



Мало толку от них. Только для тестирования железа. Но по факту оно может разгоняться под бенчмарк и тормозить в реальных приложениях. И не забываем, что запускать надо раз 10 подряд, чтобы убедиться в отсутствии просадок от перегрева.

Тот же старенький Пиксель или 1+ на старом процессоре может работать отзывчевее и приятнее, чем новый флагманский Сяоми. А дешёвый Виво приятнее среднего Самсунга.
Просто потому, что железо правильно охлаждается, а оболочка и андроид хорошо оптимизированы. И пофиг, что в том же антуту будет отставание в 2-3 раза.

Производители оптимизируют код ОС под бенчмарки. Т.е. при запуске АнТуТу включают буст процессора и т.д. Поэтому проверять лучше на реальных тяжелых приложениях и играх.

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

В любой конторе, в любом коллективе, да даже, когда вы наедине со своим Тайлером Дерденом (но только, если ваш Тайлер — программист), обязательно, хотя бы раз, зайдет спор о том, как реализовать ту или иную функцию, чтобы она работала быстро. Но быстро, как характеристика обычно не котируется, поэтому предлагаю поговорить о том, как абстрактное быстро превращать в неабстрактное число.

Инструментарий

Замерять производительность можно разными инструментами, давайте поговорим о некоторых из тех, с которыми мне приходилось сталкиваться.

Нативная структура данных описывающая дату/время.
Все замеры сводятся к тому, что мы замеряем дату перед функцией, затем дату после функции, и берем разницу.

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

Системные часы инициализируются от аппаратных при загрузке операционной системы, и далее системное время поддерживаются с помощью регулярных прерываний от таймера. (Wikipedia)

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

Единственный случай, где Date может пригодиться, это если вы заменяете скрипты, которые выполняются по несколько секунд, и разница в ± 100 мс для вас не играет никакой роли. Я вообще не рекомендую пользоваться Date для замеров.

Performance.now()

Возвращает временную метку измеряемую в миллисекундах с точностью до одной тысячной миллисекунды.

Для Node.js измерение идет с отсчетом от начала выполнения текущего потока выполнения, а для браузеров от события PerformanceTiming.navigationStart.

Замер времени выполнения функции выглядит вот так:

Мне приятнее сравнивать числа в формате op/sec, нежели в виде 0.00000546654.

Performance.now() не только точнее чем Date, но и куда удобнее. Вам не придется проводить каких-либо дополнительных манипуляций с переводом даты в timestamp и обратно, вы сразу получаете число в удобных единицах измерения.

Benchmark.js

Библиотека для точных измерений работы кода и сбора статистики выполнения. На мой взгляд она несколько тяжела для быстрого вникания, но предоставляет весь необходимый набор инструментов для замера производительности кода.

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

Распространенные Ошибки

Замеры производительности только кажутся простым делом. На самом деле есть много подводных камней, которые могут испортить вам всю малину: от компилятора и самого js, до операционной системы.

Оптимизация компилятора

Ошибка характерная только для микробенчмарков и можно ее выразить во фразе:

Хочешь рассмешить компилятор — покажи ему микробенчмарки, которые собираешься сделать.

Давайте посмотрим на функцию для измерения цены получения значения длинны массива.

Функция для замера

*- Этот и все нижеприведенные результаты являются средним значением для тысячи вызовов.

Это количество вызовов функции за секунду, если вас интересует результат по получении поля то надо это время домножить на миллион.

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

На самом деле в итоговом значении еще учитывается стоимость вызова функции и стоимость итерации, но это, пожалуй, мелочи.

Так вот, казалось бы в чем проблема Hrodvitnir? А в том, что это все наглая ложь и не правда. Давайте-ка, запустим вот эту функцию:

А теперь внимание вопрос: мы один раз обратились к полю и выполнили на две операции в секунду меньше.

Ну давайте честно: две операции разницы это разница в 0.28%, и мы можем этим смело пренебречь, и считать оба этих результата эквивалентными. А вот это, в свою очередь должно нас озаботить.

Дело в том, что этот способ замерять время обращения к полю уже устарел. И устарел он примерно тогда же, когда js перестал быть интерпретируемым.

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

И это не единственный вариант, как такое может произойти.

Компилятор умеет манипулировать кодом в циклах, умеет встраивать функции, и т. д. Конкретно эта оптимизация называется LICM.

Вместо этого, мы можем проверить обращение к полю вот так:

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

Но давайте честно, замерять скорость получения длинны массива занятие абсолютно неблагодарное.
Пример здесь приведен сугубо для того, чтобы продемонстрировать, что может быть и такая ситуация.

Замер одних и тех же данных

Это та ошибка, которая водила меня некоторое время за нос.

В качестве аргумента выступает массив от 1 до 1,000,000.

Ниже приведен метод из моей библиотеки, которым мы отфильтровываем все нечетные значения:

В общем-то, неплохо, быстрее даже чем нативный filter и быстрее, чем lodash.
Мы с вами, даже это проверили в статье Нативный — не значит быстрый

Но если мы замерим эту функцию на еще раз на неотсортированном массиве, то получим уже вот такой результат:

На неотсортированном массиве фильтрация идет в два раза дольше. При чем и у меня, и у lodash, и у нативной реализации.

А казалось, всего-то проверили на другом массиве.

Однократный замер

Давайте снова обратимся к замеру отсортированного массива из предыдущего примера и сделаем несколько разовых замеров:

No Результат Средний накопленный
1 30 30
2 27 28.5
3 18 25
4 24 24.75
5 13 22.4

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

Давайте отметим, это не 10 замеров подряд, это 10 запусков скрипта с одним замером.

Разница так велика, банально, оттого, что замеры происходят при разной загрузке ЦП.
Кроме того, мы замеряем не боевую функцию, а "холодную", ту, что выполняется интерпретируемо, а не ту, что уже скомпилирована в байт-код. В общем, все говорит о том, что разовые замеры не говорят ни чего о том, как эта функция будет вести себя в бою.

Замер в разных условиях

Эта ошибка косвенно связана с предыдущей: делайте замеры выключив все лишние программы.
Чем чище диспетчер задач тем лучше.

Просто, если вы делаете замеры, а параллельно открываете/закрываете браузер, играете в игры и т. д., то, я готов поспорить, что некоторые бенчмарки наверняка покажут причудливые результаты.

Желательно вообще никак не трогать машину. Чем меньше взаимодействия с ней, тем точнее результаты.

Хорошие практики

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

Профилактика оптимизаций компилятора

(О боже, я сказал это вслух).

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

Чтобы такого не происходило, вам надо как можно сильнее "наследить": в последнем примере мы записывали результат в массив и поэтому компилятор не вынес общий код за цикл. Только таким образом вы можете замерить именно то, что хотели.

Старайтесь, чтобы код не отрабатывал в холостую. И чтобы функция в бенчмарке не просто выполнялась, а чтобы ее результат возвращался.

Лучше делать так:

Конечно, код во втором примере слишком сложный, чтобы компилятор смог определить, что в нем нет сайдэффектов, которые выходят за пределы функции, но уж лучше перестраховаться.

Замеры на разных данных

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

Я поясню, проверяйте на лучших из возможных, на худших из возможных, проверяйте на наборе из средних данных. У вас должна быть полноценная картина о том, насколько ваш код хорош.
В этом плане бенчмарки, как юнит-тесты — покрытие должно быть полным.

В моей библиотеке используется паттерн стратегия и при определенных длинах массивов выбираются определенные алгоритмы и вследствие этого я делаю проверки на массивах длинными от 10,000,000 до 75 элементов.

Возможно вы назовете это избыточным, но я хочу видеть динамику времени работы методов от количества элементов.

Замеры на боевых данных

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

Если лучшие и худшие варианты априори не возможны, то можно их пропустить.

Если мы проверяем производительность функции, которая обрабатывает нажатие клавиш в текстовое поле из расчета, что клавиши будут нажиматься не чаще, чем 10 символов в секунду, то нет смысла проверять на частоте в 200 символов в секунду.

Да и вполне вероятно, если пользователь вбивает 200 символов в секунду, ему скорее нужен врач, чем отзывчивый интерфейс.

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

Ведение статистики

Мало собрать данные по производительности кода. Надо их еще правильно обработать, чтобы сделать корректные выводы.

Я, лично, работаю с межквартильным средним.

Формула проста — удаляем 25% самых медленных значений, удаляем 25% самых быстрых значений, а для оставшихся высчитываем среднее. Это позволяет удалить выбросы из выборки и оценивать результаты по средним значениям.

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

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

Интерпретация значений

У нас есть функция:

У нее есть результат:

А теперь давайте подумаем: это много или мало? Вот на машине помощнее будет в два раза быстрее, а на машине послабее в два раза медленнее. И что нам делать?

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

Возможно это будет ранее написанный вами функционал, и тогда вы сможете оценивать прогресс/регресс вашего кода.

Итоги

Бенчмаркинг это очень увлекательное занятие, которое помогает вовремя заметить проблемные места в коде, и сделать его лучше. Но это кроме того еще и занятие сопряженное со своими трудностями.

В этой статье я старался ответить на вопросы, которые у меня возникали в свое время, старался осветить некоторые интересные моменты.

Мой вопрос может показаться глупым, но все же спрошу. В статьях про инвестиции я очень часто встречаю упоминание бенчмарков. Как понимаю, речь идет об индексах. Но для чего они нужны инвестору, как их использовать в портфеле, где купить?

Бенчмарк — это эталон, с которым можно сравнивать поведение отдельных активов или портфеля в целом. При помощи бенчмарка инвестор может оценить, насколько хорош его портфель.

В роли бенчмарков чаще всего выступают биржевые индексы — наборы рыночных инструментов, объединенных по определенному признаку. Например, это может быть индекс акций крупнейших компаний США или индекс государственных облигаций Московской биржи.

Например, портфель акций российских компаний принес за год 12% в рублях. Вроде бы неплохой результат. Но если в то же время индекс Мосбиржи упал на 5%, то результат портфеля становится отличным. И наоборот, если за тот же год индекс Мосбиржи вырос на 25%, портфель с доходностью 12% уже не впечатляет.

Расскажу подробно, какие бывают бенчмарки и как они устроены.

Какими должны быть характеристики бенчмарка

Бенчмарк не выбирают произвольно — он должен обладать определенными характеристиками.

Конкретный. Бенчмарк должен быть понятным и четко определенным. Например, нельзя сравнить поведение облигационного портфеля с поведением облигаций вообще, потому что долговые бумаги бывают разными и сильно отличаются друг от друга. Однако можно сравнить результаты портфеля с результатами конкретного вида облигаций или какого-то облигационного фонда.

Доступный для вложений. В то, что считается бенчмарком, можно вложить деньги напрямую или согласно ему. Например, если речь об индексах, деньги обычно вкладывают через фонды ETF. Если вложиться нельзя, смысла сравнивать портфель с таким бенчмарком может не быть.

Измеримый. Результаты бенчмарка можно посчитать самостоятельно или найти готовые данные. Если данные о бенчмарке получить нельзя, он бесполезен.

Подходящий. Бенчмарк должен соответствовать портфелю. Например, инвестор собрал портфель акций российских компаний и хочет оценить, хороший ли он. Поведение такого портфеля разумно сравнивать с индексом Московской биржи, а не с индексом акций китайских компаний или американским технологическим индексом Nasdaq 100.

Конечно, при желании результат портфеля можно сравнить и с китайскими акциями, и с российскими облигациями, и с золотом. Но это не даст адекватного ответа на вопрос, правильно ли инвестор подобрал свои акции.

Заранее выбранный. Бенчмарк нужно выбрать до того, как оценивать результаты портфеля. Профессионалы определяют бенчмарк, когда только составляют портфель, и затем регулярно сравнивают показатели.

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

Как победить выгорание

Курс для тех, кто много работает и устает. Цена открыта — назначаете ее сами

Индексы в роли бенчмарков

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

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

Среди популярных американских бенчмарков можно выделить индексы Dow Jones, Nasdaq и S&P 500 , которые служат индикатором экономики США. Чаще инвесторы ориентируются на S&P 500 : у него более широкий охват компаний и отраслей. При этом капитализация 500 компаний, чьи акции входят в S&P 500 , составляет около 80% от капитализации всех компаний на биржах США.

Из российских бенчмарков основной — индекс Мосбиржи, а также его долларовый аналог — РТС. В них на данный момент представлены акции 41 компании. На Московской бирже есть и другие индексы, например индекс голубых фишек или индекс компаний средней и малой капитализации. Есть и облигационные индексы, которые подходят как бенчмарк облигационных портфелей.

Каждый индекс может быть представлен в нескольких вариантах: помимо индексов, которые отражают ценовой рост активов, также существуют индексы полной доходности — брутто и нетто. Первый отслеживает доходность активов с учетом дивидендов или купонов, а второй — с учетом дивидендов или купонов и налоговой ставки.

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

Можно ли инвестировать в бенчмарк

Я не знаю, какие именно активы вам нужны. Это зависит от цели инвестиций, горизонта инвестирования, пожеланий по доходности и риску и т. д.

Если вы решили, что хотите инвестировать на основе какого-то биржевого индекса, то вам подойдут биржевые фонды — БПИФ и ETF. Большинство таких фондов повторяют состав индекса с минимальными отклонениями. Если купите долю в таком фонде, вы, по сути, будете инвестировать в бенчмарк этого фонда.

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

Если говорить о фондах, которые отслеживают индекс S&P 500 , то на Мосбирже есть, например, SBSP и TSPX. Чтобы инвестировать в них, достаточно иметь брокерский счет или ИИС.

Самыми популярными зарубежными фондами на индекс S&P 500 считаются Vanguard S&P 500 ETF — VOO, SPDR S&P 500 ETF — SPY, iShares Core S&P 500 ETF — IVV. Существует также равновзвешенная версия S&P 500 , инвестировать в которую позволяет ETF Invesco S&P 500 Equal Weight — RSP. Чтобы получить доступ к этим фондам, нужен статус квалифицированного инвестора либо счет у зарубежного брокера.

Фонд — это упаковка для десятков и сотен бумаг. Иными словами, это диверсифицированный набор активов. Поэтому многие пассивные инвесторы предпочитают формировать портфель из нескольких ETF на нужные классы активов. Такие портфели еще называют ленивыми.

В то же время есть инвесторы с индексной стратегией инвестирования, которые предпочитают самостоятельно повторять индекс — покупать отдельные бумаги в соответствующих пропорциях. Это позволяет не использовать ETF и экономить на комиссии фонда. О том, как самостоятельно инвестировать в индексы, я писал в отдельной статье.

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

Чтобы сравнить свой портфель с выбранным бенчмарком, надо рассчитать интересующие вас параметры для портфеля и бенчмарка. Вот что это может быть:

  • доходность с начала года;
  • доходность за прошедший год;
  • среднегеометрическая доходность за несколько лет в процентах годовых;
  • стандартное отклонение доходности;
  • лучший и худший месяц или год;
  • максимальная просадка;
  • коэффициент Шарпа.

Это не исчерпывающий список параметров для сравнения. Например, кого-то также будет интересовать коэффициент бета, то есть степень волатильности портфеля по сравнению с бенчмарком. Кому-то важно, чтобы дивидендная доходность его портфеля американских акций была выше, чем у индекса S&P 500 , и т. д.

Чтобы все это рассчитать, потребуется знать формулы. Кое-что мы уже разобрали в журнале. Например, про коэффициент бета я подробно писал в статье про современную теорию портфеля.

Также, чтобы изучить параметры портфеля, необходимо найти нужные данные. Для этого могут пригодиться данные в личном кабинете или приложении вашего брокера, но многое придется считать самостоятельно. Так, чтобы правильно определить доходность портфеля, надо учесть даты пополнений и вывода средств из него. Результат бенчмарка тоже следует рассчитать с учетом этих денежных потоков.

Информацию о поведении индексов, которые могут быть бенчмарком вашего портфеля, можно найти на сайтах бирж. Также пригодятся сервисы с возможностью бэктеста портфелей, например Portfolio Visualizer, Capital Gain, Rusetfs. Какие-то расчеты, возможно, опять же придется делать самостоятельно.

Как часто стоит сравнивать портфель и бенчмарк, решать вам. Можно делать это раз в год. Данные за более короткий период, например 2—3 месяца , обычно не показательны. Если же не следить за портфелем несколько лет, могут случиться неприятные сюрпризы, а менять что-то будет уже поздно.

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

Вообще, бенчмарки нужны в первую очередь профессионалам, например управляющим фондами, под чьим контролем находится чужой капитал. Обычному инвестору бенчмарки могут вообще не требоваться.

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

Что в итоге

Бенчмарк — это эталон, на который ориентируются инвесторы. Поведение портфеля можно сравнить с поведением бенчмарка, чтобы лучше понимать, как ведет себя портфель.

Важно правильно выбрать бенчмарк. Он должен быть конкретным и понятным, измеримым, доступным для вложений и подходящим. Хорошая идея — выбрать бенчмарк еще на этапе формирования портфеля.

Чаще всего в качестве бенчмарка используют какой-то биржевой индекс — набор ценных бумаг, сформированный по какому-то принципу. Индексы могут отражать ценовой рост, а могут быть полной доходности — с учетом дивидендов и купонов, в том числе налогов с них.

Если портфель состоит из одного фонда, который качественно отслеживает какой-то индекс, результат портфеля будет равен результату индекса минус расходы фонда. По сути, это будет вложением в бенчмарк. У более сложных портфелей результат может сильно отличаться от бенчмарка, и может потребоваться более сложный бенчмарк, чем индекс акций или облигаций.

Сравнить портфель и бенчмарк можно по многим показателям: доходности с начала года, среднегодовой доходности за несколько лет, величине просадок, коэффициенту Шарпа и т. д. Почти наверняка хотя бы часть расчетов придется провести самостоятельно. При этом мало сравнить — важно еще сделать правильные выводы, чтобы понять, что делать дальше.


Честно вам признаюсь — бенчмаркинг (тестирование производительности) не входит в число моих самых сильных сторон, к тому же и провожу я его не так часто, как хотелось бы. Но с момента программирования на Go в качестве основного моего языка случаи его применения намного участились. А все потому, что Go предоставляет отличную встроенную поддержку для бенчмаркинга.

Go позволяет разработчикам тестировать производительность с помощью пакета testing, содержащего для этого все необходимое.

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

Итак, в п рограммировании под бенчмаркингом понимают тестирование производительности написанного кода.

“Бенчмарк — это выполнение компьютерной программы (набора программ) или других операций для оценки относительной производительности объекта”.

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

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

“Преждевременная оптимизация — это корень всех зол”. — Дональд Кнут, “Искусство программирования”.

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

Теперь научимся проводить бенчмарк в Go. Нам предстоит ответить на следующие вопросы:

  • Что быстрее: срезы (slice) или карты (map)?
  • Влияет ли размер на скорость срезов и карт?
  • Имеет ли значение тип ключа в картах?

Пишем самый простой бенчмарк

Прежде чем ответить на эти вопросы, создадим простой бенчмарк и покажем, как его осуществить в Golang. После этого будем его дорабатывать, чтобы получить ответы на поставленные вопросы.

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

Потребуется также создать файл, оканчивающийся на _test.go . В нашем случае им будет benching_test.go .

Подобно обычным модульным тестам, бенчмарки в Go выполняются с помощью пакета testing и запускаются теми же инструментами тестирования.

Инструмент Go определяет, какие методы являются бенчмарками по их именам. Любой метод, начинающийся с Benchmark и принимающий указатель на testing.B , будет выполняться как бенчмарк. Данный фрагмент иллюстрирует пример такого минимального метода:

Испытаем его, выполнив команду go test с флагом -bench=.


Результат выполнения команды ‘ go test -bench= .’ говорит о том, что бенчмарк работает.

Остановимся на этом моменте подробнее и проанализируем вывод. По завершении каждый бенчмарк выводит 3 значения: имя, количество выполнений и ns/op .

Первое значение говорит само за себя — это имя, которое мы задаем в тестовом файле.

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

ns/op означает число наносекунд, затраченных на операцию. Это время, которое потребовалось на вызов метода.

Если из большого числа бенчмарков вы хотите выполнить только один или несколько, можно заменить точку на строку в соответствии с такими именами, как -bench=BenchmarkSimplest . Напоминаю, что -bench=Benchmark по-прежнему будет запускать бенчмарк, поскольку строка соответствует началу метода.


Замена значения ‘-bench=’ нужна для указания бенчмарка, подлежащего выполнению.

Теперь мы можем измерить скорость и не только. К счастью, заглянув в пакет testing, мы обнаружим, что добавление флага -benchmem позволит получить дополнительную информацию о том, сколько было выделено байт за операцию (B/op), а также сколько раз за операцию выделялась память (allocs/op).


При добавлении флага ‘-benchmem’ получаем B/op и allocs/op

Мы уже почти готовы тестировать реальные процессы, но уделим внимание еще ряду моментов. Что происходит с входным параметром *testing.B в нашем бенчмарке? Чтобы понять, с чем мы имеем дело, ознакомимся с его определением, предложенным в стандартной библиотеке (golang/src/testing/benchmark.go).

Testing.B содержит любые данные, относящиеся к выполняемому бенчмарку, а также структуру BenchmarkResult для форматирования вывода. Если какая-то часть вывода вам непонятна, то настоятельно рекомендую открыть benchmark.go и прочитать код.

Особое внимание заслуживает переменная N . Как вы помните, бенчмарки выполняются по многу раз. Так вот, переменная N в testing.B как раз и указывает на количество выполнений.

Согласно документации в бенчмарках это необходимо иметь в виду, поэтому обновляем BenchmarkSimplest с учетом N .

Мы обновили функцию, создав цикл for , который перебирает N раз. При проведении тестирования я устанавливаю конкретные значения N , обеспечивая адекватность бенчмарков. В противном случае один из них выполнится 100 000 раз, а другой — дважды.


На старт, внимание, бенчмарк!

Приступаем к тестированию и ответам на ранее заданные вопросы о производительности.

  • Что быстрее: срезы или карты?
  • Влияет ли размер на скорость срезов и карт?
  • Имеет ли значение тип ключа в картах?

Начнем реализацию одного бенчмарка для оценки записи данных в карты и срезы, а другого — для оценки их обратного считывания. У разработчика Дейва Чини я позаимствовал один прием — создание метода, принимающего входные параметры, которые мы хотим протестировать. С его помощью можно запросто проверять много различных значений.

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

Ниже представлены методы бенчмарков, которые выполняются N раз и записывают X элементов в карту.

Заметили, как я повторно задействовал один и тот же метод в каждом бенчмарке, лишь меняя число вставляемых элементов? Весьма ловкий прием, позволяющий легко тестировать большие и маленькие размеры.


Количество затраченного времени растет, что закономерно в связи с увеличением количества вставок. Пока эти результаты особо ни о чем не говорят, поскольку их не с чем сравнивать. Однако можно выделить время для ответа на один из вопросов: “Имеет ли значение тип ключа в картах?”.

Скопируем все методы и заменим используемый тип ключа интерфейсом. В этот раз у нас будет всего 2 файла: benching_map_interface_test.go и benching_map_int_test.go . Методы бенчмарков будут соответствовать имени для поддержания легко управляемой структуры при добавлении дополнительных бенчмарков.

Ниже следует пример тестирования записи в карту с интерфейсом в качестве ключа:


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

Ответ на один из вопросов получен. Судя по результатам, тип ключа немаловажен. В этом тесте Int в качестве ключа вместо Interface в 2.23 раза быстрее, учитывая показатели бенчмарка 1000000 . Хотя я не припомню, чтобы интерфейс когда-либо применялся в качестве ключа.

  • Что быстрее: срезы или карты?
  • Влияет ли размер на скорость срезов и карт?
  • Имеет ли значение тип ключа в картах? Да, имеет.

Перед тем, как двигаться дальше, добавим ради интереса новый бенчмарк. В предыдущих тестах размер карт не был предустановлен заранее. Теперь мы это изменим и проверим, в чем же отличие.

Но прежде нам предстоит изменить метод insertXIntMap , а также инициализацию карты на использование длины X. Был создан новый файл benching_map_prealloc_int_test.go , в котором я изменил метод insertXIntMap для предварительной инициализации размера. ‘make(map[int]int, 0)’ был заменен на ‘make(map[int]int,x)’.

Напоминаю, что мы можем управлять выполнением тех или иных бенчмарков с помощью флага -bench= . Теперь самое время вспомнить этот прием, учитывая их немалое количество. Но задача именно этого бенчмарка — сравнить производительность карт с предустановленным размером и без него.

Новые бенчмарки я назвал BenchmarkInsertIntMapPrealloc , чтобы их имена совпадали с BenchmarkInsertIntMap , что можно будет использовать в качестве триггера. Этот новый файл является точной копией другого бенчмарка IntMap — изменились лишь имена и метод, подлежащий выполнению.

Выполняем бенчмарк и меняем флаг -bench= .


Отличия между предустановленным и непредустановленным размером в карте

Согласно результатам бенчмарка, предустановка размера карты играет значимую роль — достаточно лишь увидеть, что производительность теста 1000000 в 1.92 раза больше, а показатели B/op еще лучше.

Далее перейдем к реализации бенчмарка записи в срезы. Ее принцип аналогичен реализации карты с той лишь разницей, что срез используется с append .

Опять-таки ради интереса напишем бенчмарки для предустановленных и непредустановленных размеров срезов. Заново создаем метод insertX , все копируем и затем заменяем Map на Slice . Итак, записываем в срез X элементов для тестирования вставки.

Бенчмарк добавления срезов:

Для среза, под который выделена память, инструкция append не применяется, поскольку она добавляет ему индекс, в связи с чем срез вынужден изменяться для использования нового верного индекса.

Полный вариант кода для тестирования среза в предварительно выделенной памяти:

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


Разница между предустановленными и динамическими срезами просто огромная. Показатели бенчмарка 1000000 составляют соответственно 7246 ns/op против 75388 ns/op , т. е. скорость выполнения операции в первом случае в 10.4 раз больше, чем во втором. Однако в некоторых ситуациях работать со срезами фиксированного размера бывает проблематично. Что касается моих приложений, то обычно я не знаю размер срезов, поскольку они, как правило, динамические.

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

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

Бенчмарк выборки из карты:

Бенчмарк выборки из среза:


Как вы заметили, код для selectXIntSlice и selectXIntMap один и тот же, единственное отличие — команда make . Про разницу в производительности и говорить нечего — все очевидно.

Итак, теперь у нас есть результаты тестирования. Для более простого сравнительного анализа оформим в таблицу показатели всех бенчмарков с 1000000 элементов.

Насколько сильно отличаются результаты срезов и карт?

Срезы быстрее в 21.65 раз (1321196/75388), если сравнивать производительность записи в их динамическую форму.

Срезы быстрее в 118.35 раз (857588/7246 ) при сравнении производительности записи в их форму с предустановленным размером.

Срезы быстрее в 117.19 раз (507843/2866) при сравнении производительности считывания.

Что быстрее: срезы или карты?

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

Но зато карты проще в использовании. В данных бенчмарках предполагалось, что индексы в срезах известны. Однако есть много случаев, когда мы их не знаем, так что приходится перебирать весь срез, например map[userID]User вместо цикла for для []User .

Влияет ли размер на скорость срезов и карт?

Размер в этих случаях не имеет значения.

Имеет ли значение тип ключа в картах?

Имеет. Применение целого числа по сравнению с интерфейсом оказалось в 2.23 раза быстрее.

Рассмотрим более реалистичный случай

Похоже, что срезы отличаются намного более высокой производительностью, но буду с вами честен — верный индекс для них мне практически никогда неизвестен. Чаще всего приходится перебирать весь срез для поиска нужного элемента. Именно по этой главной причине я, как правило, предпочитаю карты.

Создадим бенчмарк как раз для такого случая. У нас есть map[userID]User и []User . Тест нацелен на сравнение скоростных показателей нахождения конкретного пользователя в картах и срезах.

Я создал новый файл, содержащий код для генерации случайных пользователей, количество которых в срезе и карте составило 10 000, 100 000 и 1 миллион. Допустим, что у нас есть API, и нам был отправлен ID пользователя, которого нужно найти. Далее следует сценарий, подлежащий тестированию. Я также перемешиваю срез для имитации реальной ситуацией, когда данные добавляются динамически.

Этот бенчмарк я назвал “Спасти рядового Райана”. Вот его мы и будем искать по ID пользователя 7777 . Ниже представлен бенчмарк более реалистичного случая использования срезов и карт:


Сравнение результатов тестирования по поиску пользователя в карте и срезе

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

На этот раз производительность карты превосходит срез в 6 678.53 раза.

Что быстрее: срезы или карты?

В плане производительности срезы намного превосходят карты, но при этом работать с ними сложнее, судя по результатам теста “Спасти рядового Райана”. Так что высокая производительность не всегда оказывается решающим фактором.

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

Влияет ли размер на скорость срезов и карт?

Судя по результатам теста — влияет. Если нужный номер индекса известен, то, конечно, размер не важен. Но если вы не знаете, под каким индексом хранится значение, то размер играет важную роль.

Имеет ли значение тип ключа в картах?

Да. Работа с целым числом в 2.23 раза быстрее, чем с интерфейсом.

Ответы на все вопросы получены, и я надеюсь, что вы узнали что-то для себя полезное о бенчмарках. С полным вариантом кода можно ознакомиться здесь.

Читайте также: