Статьи

Оптимизация JavaScript по профилю

 

Оптимизация по профилю (PGO) — это известная методика компилятора для создания оптимизированного кода. Сгенерированный код смещен в сторону общего набора данных, который будет подан в код. Определенно возможно применить ту же технику к веб-приложениям, написанным на JavaScript.

Различные компиляторы, от Microsoft Visual C ++ до Intel Fortran, используют данные профиля, чтобы принимать решения о том, как найти правильный компромисс. Например, небольшие функции, которые выполняются довольно часто, должны находиться в непосредственной близости, чтобы локальность помогала кэш-памяти ЦП. Статистика выполнения также может помочь решить, какие функции следует встроить, какие ветви следует перенести на вершину, а также множество других компромиссов.

Ключом к успешной оптимизации по профилю является набор репрезентативных данных. Это может быть образец, полученный во время пробного запуска. Однако нам нужно убедиться, что данные действительно в некоторой степени напоминают фактическое распределение в реальном мире. Например, анализатор JSON должен быть протестирован с набором допустимых (хотя и синтетических) строковых объектов, а не только с потоком случайных символов.

Давайте посмотрим на следующий пример. Поддерживаемый JSON-анализатор, упомянутый выше (или любой другой вид функции синтаксического анализа), должен проверить, представляет ли символ десятичную цифру (0-9) или нет. Возможная реализация из миллиарда возможных вариантов так же проста, как:

function isDigit(ch) {
  return '0123456789'.indexOf(ch) >= 0;
}

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

Если 65% времени isDigitполучит пробел, должны ли мы заняться этим в первую очередь? Мы можем сэкономить некоторое время, поскольку нет необходимости проверять эти 10 различных возможностей цифр, если окажется, что это простой пробел. Имея это в виду, мы можем настроить реализацию так:

function isDigit(ch) {
  return (ch !== ' ') && '0123456789'.indexOf(ch) >= 0;
}

Благодаря короткому замыканию логического выражения AND (см. Раздел 11.11 Двоичные логические операторы), каждый раз, когда chэто пробел, функция немедленно выйдет из строя. Это формирует специальный быстрый путь для кода. Если это условие не выполняется, мы снова возвращаемся к медленному пути .

Обобщая этот подход, пара медленных и быстрых путей проиллюстрирована следующим образом:

сокращенный

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

В общей ситуации, когда вы можете избежать медленного пути при определенных обстоятельствах, дополнительная производительность должна быть заметна. Например, если общий медленный путь занимает 2 мс для обработки данных, и в наборе данных содержится 100 элементов, общая обработка составит 200 мс . Теперь предположим, что быстрый путь в два раза быстрее, т.е. 1 мс. В соответствии с данным распределением, быстрый путь выполняется только для 65 элементов этого набора данных. Кроме того, поскольку медленный путь теперь находится в резерве и не выполняется немедленно, это приведет к дополнительным 20% накладным расходам. Это означает, что общая обработка составит 142 мс (65 * 1 мс + 35 * 2,2 мс). Это примерно на 40% больше.

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

Как они привыкли говорить: профиль ответственно .