Подтвердить что ты не робот

Компилятор Google Closure когда-либо снижает производительность?

Я пишу расширение Google Chrome. Поскольку файлы JavaScript загружаются с диска, их размер едва ли имеет значение.

В любом случае я использую Google Closure Compiler, потому что, по-видимому, он может оптимизировать производительность, а также уменьшить размер кода.

Но я заметил это в верхней части моего вывода из Closure Compiler:

var i = true, m = null, r = false;

Точка этого, очевидно, сводится к уменьшению размера файла (все последующие использования true/null/false на протяжении всего script могут быть заменены одиночными символами).

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

Неужели эта работа поражает? И есть ли что-нибудь еще в компиляторе Google Closure, которое может фактически замедлить выполнение?

4b9b3361

Ответ 1

Ответ возможно.

Давайте посмотрим, что говорит команда закрытия.

Из ЧаВо:

Компилятор делает компромисс между скоростью выполнения моего приложения и размером кода загрузки?

Да. Любой оптимизирующий компилятор делает компромиссы. Оптимизация некоторых размеров приводит к небольшим накладным расходам.. Однако разработчики Closure Compiler старались не вводить значительное дополнительное время выполнения. Некоторые из оптимизаций компилятора даже уменьшают время выполнения (см. следующий вопрос).

Оптимизирует ли компилятор скорость?

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

Я прямо бросаю первое предположение, которое они сделали здесь. Размер используемых имен варов напрямую не влияет на то, как различные механизмы JavaScript обрабатывают код - на самом деле, JS-движка не волнует, называете ли вы свои переменные supercalifragilisticexpialidocious или x (но я, как программист, уверен), Время загрузки - самая важная часть, если вы беспокоитесь о доставке - медленный запуск script может быть вызван миллионами вещей, которые, как я подозреваю, просто не могут быть учтены.

Чтобы правильно понять, почему ваш вопрос, возможно, первое, что вам нужно задать, это "Что делает JavaScript быстрым или медленным?"

Тогда, конечно, мы сталкиваемся с вопросом, "О каком движке JavaScript мы говорим?"

Имеем:

  • Каракан (Опера)
  • Чакра (IE9 +)
  • SpiderMonkey (Mozilla/FireFox)
  • SquirrelFish (Apple webkit)
  • V8 (Chrome)
  • Futhark (Opera)
  • JScript (все версии IE до 9)
  • JavaScriptCore (Konqueror, Safari)
  • Я пропустил несколько.

Кто-нибудь здесь действительно думает, что все они работают одинаково? Особенно JScript и V8? Heck no!

Итак, когда google замыкает компилирует код, для чего движком он строит материал? Вам повезло?

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

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

Старые двигатели JS

  • Код интерпретируется и скомпилируется непосредственно в байтовый код
  • Нет оптимизации: вы получаете то, что получаете
  • Коду сложно быстро запускать из-за слабо типизированного языка

Новые двигатели JS

  • Внедрить компиляторы Just-In-Time (JIT) для быстрого выполнения
  • Ввести компиляторы JIT с оптимизацией по типу для очень быстрого кода (подумайте о скорости кода C)

Ключевое различие в том, что новые двигатели представляют компиляторы JIT.

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

Вы можете делать такие вещи, имея следующие две функции:

var FunctionForIntegersOnly = function(int1, int2){
    return int1 + int2;
}

var FunctionForStringsOnly = function(str1, str2){
    return str1 + str2;
}

alert(FunctionForIntegersOnly(1, 2) + FunctionForStringsOnly("a", "b"));

Выполнение этого с помощью закрытия Google фактически упрощает весь код до:

alert("3ab");

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

Предположим, что у нас есть y-combinator в нашем коде, компилятор превращает его в нечто вроде этого:

(function(a) {
 return function(b) {
    return a(a)(b)
  }
})(function(a) {
  return function(b) {
    if(b > 0) {
      return console.log(b), a(a)(b - 1)
    }
  }
})(5);

Не очень быстро, просто уменьшил код.

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

Итак, что мы узнали?

  • У вас может быть JIT-оптимизированный код, но компилятор повторно организует ваш код во что-то еще.
  • У старых браузеров нет JIT, но все еще выполняется ваш код.
  • Закрытие скомпилированного JS вызывает меньше вызовов функций, выполняя частичное выполнение вашего кода для простых функций.

Итак, что вы делаете?

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

В общем, ваш код работает быстрее. Вы можете представить, что разные компиляторы JIT не любят, но они будут редки, если ваш код использует меньшие функции и правильную прототипную объектно-ориентированную конструкцию. Если вы думаете о полном объеме того, что делает компилятор (более короткая загрузка и более быстрое выполнение), то странные вещи, такие как var i = true, m = null, r = false;, могут стоить того, что компилятор сделал, даже если они работают медленнее, общая продолжительность жизни был быстрее.

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

Ответ 2

Похоже, что в современных браузерах, использующих литерал true или null vs, переменная делает абсолютно никакой разницы почти во всех случаях (как в ноль, они точно такие же). В очень немногих случаях переменная на самом деле быстрее.

Итак, эти лишние байты в экономии стоят того и ничего не стоят.

true vs variable (http://jsperf.com/true-vs-variable):

true vs variable

null vs variable (http://jsperf.com/null-vs-variable):

null vs variable

Ответ 3

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

Обратите внимание, что стандартные переменные псевдонима Closure Compiler являются глобальными переменными. Это означает, что в старом браузере, где движок JavaScript занимает линейное время для навигации по функциональным областям (например, IE < 9), чем глубже вы находитесь внутри вложенных вызовов функций, тем больше времени требуется, чтобы найти эту переменную, которая содержит "true" или "false" и т.д. Почти все современные движки JavaScript оптимизируют доступ к глобальным переменным, поэтому этот штраф больше не будет выполняться во многих случаях.

Кроме того, на самом деле не должно быть много мест, где вы могли бы видеть "истинный" или "ложный" или "нулевой" непосредственно в скомпилированном коде, за исключением назначений или аргументов. Например: if (someFlag == true) ... в основном просто написан if (someFlag) ..., который компилируется компилятором в a && .... В основном вы видите их только в назначениях (someFlag = true;) и аргументах (someFunc(true);), которые действительно не происходят очень часто.

Заключение: хотя многие люди сомневаются в полезности стандартных псевдонимов Closure Compiler (включая меня), вы не должны ожидать, чтобы какая-либо материальная производительность удалась. Вы также не должны ожидать каких-либо существенных преимуществ в размерах gzip.