JIT компилятор против автономных компиляторов - PullRequest
37 голосов
/ 11 февраля 2009

Существуют ли сценарии, в которых JIT-компилятор работает быстрее, чем другие компиляторы, такие как C ++?

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

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

Есть идеи?

Ответы [ 10 ]

52 голосов
/ 11 февраля 2009

Да, конечно, есть такие сценарии.

  • JIT-компиляция может использовать профилирование во время выполнения для оптимизации конкретных случаев на основе измерения характеристик того, что код фактически делает в данный момент, и может при необходимости перекомпилировать «горячий» код. Это не теоретически; HotSpot Java фактически делает это.
  • JITters могут оптимизировать для конкретной конфигурации процессора и памяти, используемой на реальном оборудовании, на котором выполняется программа. Например, многие .NET-приложения будут работать в 32-битном или 64-битном коде, в зависимости от того, где они JITted. На 64-битном оборудовании они будут использовать больше регистров, памяти и лучший набор команд.
  • Виртуальные вызовы методов внутри замкнутого цикла можно заменить статическими вызовами, основанными на знании времени выполнения типа ссылки.

Я думаю, что в будущем будут прорывы. В частности, я думаю, что сочетание JIT-компиляции и динамической типизации будет значительно улучшено. Мы уже видим это в пространстве JavaScript с Chrome V8 и TraceMonkey. Я ожидаю увидеть другие улучшения такого же масштаба в не слишком отдаленном будущем. Это важно, потому что даже так называемые «статически типизированные» языки, как правило, имеют ряд динамических особенностей.

11 голосов
/ 11 февраля 2009

Да, JIT-компиляторы могут создавать более быстрый машинный код, оптимизированный для текущей среды. Но практически программы VM работают медленнее, чем программы Native, поскольку сама JITing требует времени (больше оптимизации == больше времени), и для многих методов JIT их может занять больше времени, чем их выполнение. И именно поэтому GAC представлен в .NET

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

Извините за мой плохой английский.

7 голосов
/ 11 февраля 2009

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

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

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

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

1 голос
/ 04 декабря 2013

Еще один момент, который предпочитает «автономные» компиляторы, заключается в том, что такие компиляторы могут с пользой ориентироваться на платформы с небольшим объемом ОЗУ - даже всего шестнадцатью байтами. Конечно, все, что совместимо даже с удаленным ПК, может иметь (буквально) в миллионы раз больше оперативной памяти, чем это, но я думаю, что пройдет некоторое время, прежде чем можно будет найти машину со многими мегабайтами оперативной памяти, которая стоит менее $ 0,50 и потребляет менее чем милливатт мощности во время работы.

Обратите внимание, что 16 байтов ОЗУ не так слабы, как кажется, поскольку микросхемы с такой небольшой ОЗУ не хранят код в ОЗУ - имеют отдельную энергонезависимую область памяти для хранения кода (384 байт самый маленький из известных мне). Конечно, это немного, но этого достаточно, чтобы процессор за 0,25 доллара мог выполнять функции, которые в противном случае потребовали бы дискретных компонентов стоимостью 1 доллар.

1 голос
/ 03 декабря 2013

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

interface IGenericAction { bool Act<T>(); }

struct Blah<T>
{
  public static void ActUpon(IGenericAction action)
  {
     if (action.Act<T>())
       Blah<Blah<T>>.ActUpon(action);
  }
}

Вызов Blah<Int32>.ActUpon(act) вызовет act.Act<Int32>(). Если этот метод возвращает true, он вызовет Blah<Blah<Int32>>.ActUpon(act), что, в свою очередь, вызовет act.Act<Blah<Int32>>(). Если это возвращает true, будет выполняться больше вызовов с еще более глубоко вложенным типом. Генерация кода для всех методов ActUpon, которые могут быть вызваны , была бы невозможна, но, к счастью, в этом нет необходимости. Типы не нужно генерировать, пока они не будут использованы. Если action<Blah<...50 levels deep...>>.Act() возвращает false, то Blah<Blah<...50 levels deep...>>.ActUpon не будет вызывать Blah<Blah<...51 levels deep...>>.ActUpon, и последний тип создавать не нужно.

1 голос
/ 08 ноября 2010

Множество людей ответили, может быть, я скиммирую (возможно, у меня неправильный конец клюшки), но для меня это две разные вещи:

AFAIK ничто не мешает вам JIT'скомпилировать c ++, например, машинный код проекта Dynamo JIT:

http://arstechnica.com/reviews/1q00/dynamo/dynamo-1.html

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

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

например. компиляция с ++ в сборку (я думаю ;-) или c # компиляция в IL или компиляция Java в байт-код

JIT - это процесс, который происходит во время выполнения. Машина, выполняющая код, анализирует его, чтобы увидеть, сможет ли он его улучшить. Java и C # могут использовать и то и другое, поскольку компилятор готовит команды для виртуальной машины, и тогда у виртуальной машины есть возможность, по крайней мере, еще раз оптимизировать ее.

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

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

ОДНАКО, я действительно думаю, что JIT может стать большей частью истории, так как среда выполнения Java и среда .net развиваются. Я уверен, что JIT улучшится и с учетом таких вещей, как проект Dynamo, я предполагаю, что есть место для аппаратного обеспечения и JIT, так что все, что делает ваш процессор, оптимизируется в зависимости от среды выполнения.

1 голос
/ 20 октября 2010

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

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

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

1 голос
/ 10 ноября 2009

Еще одна вещь, которая была пропущена в этом разговоре, это то, что когда вы JIT-код, он может быть скомпилирован в свободное место в памяти. На языке, подобном C ++, если библиотека DLL основана на том, что эта часть памяти недоступна, ей придется пройти дорогостоящий процесс перебазирования. Это быстрее JIT-код в неиспользуемый адрес, а затем перекомпилировать скомпилированную DLL в свободное пространство памяти. Что еще хуже, перебазированная DLL больше не может быть общей. (см. http://msdn.microsoft.com/en-us/magazine/cc163610.aspx)

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

Лично я думаю, что хорошим решением будет уровень оптимизации (1-100), который вы можете указать JIT-компилятору, сколько времени, по вашему мнению, должно быть потрачено на оптимизацию кода. Единственным другим решением будет компилятор AOT (Ahead of Time), и тогда вы потеряете многие из преимуществ JIT-кода.

1 голос
/ 11 февраля 2009

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

Почему это важно?

//code before
if(errorCondition)
{
  //error handling
}
//code after

Превращается в нечто вроде:

//code before
Branch if not error to Code After
//error handling
Code After:
//Code After

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

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

0 голосов
/ 11 февраля 2009

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

...