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

Почему Closure Compiler настаивает на добавлении большего количества байтов?

Если я даю Closure Compiler примерно следующее:

window.array = '0123456789'.split('');

Он "скомпилирует" это:

window.array="0,1,2,3,4,5,6,7,8,9".split(",");

Теперь, как вы можете сказать, это больше. Есть ли причина, по которой Closure Compiler делает это?

4b9b3361

Ответ 1

Я думаю, что это то, что происходит, но я отнюдь не уверен...

Код, который вызывает вставку запятых, tryMinimizeStringArrayLiteral в PeepholeSubstituteAlternateSyntax.java.

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

"a b c d e f g".split(" "); //Uncompiled, split on spaces
"a,b,c,d,e,f,g".split(","); //Compiled, split on commas (same size)

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

// These delimiters are chars that appears a lot in the program therefore
// probably have a small Huffman encoding.
NEXT_DELIMITER: for (char delimiter : new char[]{',', ' ', ';', '{', '}'}) {
  for (String cur : strings) {
    if (cur.indexOf(delimiter) != -1) {
      continue NEXT_DELIMITER;
    }
  }
  String template = Joiner.on(delimiter).join(strings);
  //...
}

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

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


Другой пример того, как компилятор имеет дело с методом split:

"a,;b;c;d;e;f;g".split(";"); //Uncompiled, split on semi-colons
"a, b c d e f g".split(" "); //Compiled, split on spaces

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


Обновление

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

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

А так как алгоритм Хаффмана является большой частью алгоритма DEFLATE, используемого gzip. Поэтому, если ваш веб-сервер настроен на использование gzip, ваши пользователи получат выгоду от этой умной оптимизации.

Ответ 3

Как ни странно, split в скомпилированном коде не имеет ничего общего с split в источнике. Рассмотрим:

Source  : a = ["0","1","2","3","4","5"]
Compiled: a="0,1,2,3,4,5".split(",")

Здесь split - это просто способ представления длинных массивов (достаточно долго, чтобы сумма всех котировок + запятых была длиннее split(","")). Итак, что происходит в вашем примере? Во-первых, компилятор видит строчную функцию, применяемую к константе, и сразу ее оценивает:

'0123456789'.split('') => ["0","1","2","3","4","5","6","7","8","9"]

В какой-то более поздний момент при генерации вывода компилятор считает этот массив "длинным" и записывает его в приведенную выше "split" форму:

["0","1","2","3","4","5","6","7","8","9"] => "0,1,2,3,4,5,6,7,8,9".split(",")

Обратите внимание, что вся информация о split('') в источнике уже потеряна в этой точке.

Если исходная строка была короче, она будет сгенерирована в форме массива массивов без дополнительного разбиения:

Source  : a = '0123'.split('')
Compiled: a=["0","1","2","3"]