Как можно интерпретировать код даже мало эффективно? (Теоретический) - PullRequest
4 голосов
/ 03 сентября 2010

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

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

Давайте вернемся к тем дням, когда не было JIT-компиляторов.У Java есть виртуальная машина, которая в основном является ее аппаратным обеспечением.Вы пишете код, после чего он компилируется в байт-код для выполнения хотя бы некоторой работы с виртуальной машины, это нормально.Но, учитывая, насколько сложным может быть даже набор команд RISC в аппаратном обеспечении, я даже не могу придумать, как это сделать на программном эмулируемом оборудовании.

У меня нет опыта написания виртуальных машин, поэтому я не знаю, как именно это делаетсяэффективный уровень, но я не могу придумать ничего более эффективного, чем тестирование каждой инструкции на соответствие и выполнение соответствующих действий.Вы знаете, что-то вроде: if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) } и т.д ....

Но это должно быть очень медленно.И все же, даже если есть статьи о том, что java был медленнее, чем JIT-компиляторы, они все еще говорят, что это не так медленно.Но для эмуляции требуется много тактов реального HW для выполнения одной инструкции байт-кода.

И все же, даже целые платформы основаны на Java.Например, Android.И первые версии Android не имели JIT-компилятора.Они были истолкованы.Но не должен ли Android быть ужасно медленным?И все же это не так.Я знаю, что когда вы вызываете какую-то функцию API из библиотеки Android, они пишутся в машинном коде, поэтому они эффективны, и это очень помогает.

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

А теперь несколько примеров, как я и обещал.Поскольку я в основном работаю с микроконтроллерами, я нашел JVM для микроконтроллера Atmel AVR.Скажи, что 8 МГц MCU может делать 20 000 опткодов Java в секунду.Но поскольку AVR может выполнять большинство инструкций за один или два цикла, скажем, в среднем 6000000 инструкций.Это дает нам то, что JVM без JIT-компилятора медленнее машинного кода в 300 раз.Так почему же Java стала настолько популярной без JIT-компилятора?Не слишком ли это плохая потеря производительности?Я просто не могу этого понять.Спасибо.

Ответы [ 4 ]

3 голосов
/ 03 сентября 2010

У нас был байт-код в течение долгого времени. На старом Apple II p-система USCD была очень популярна, которая компилировала Паскаль в байт-код, который интерпретировался бы 8-битным 6502, который мог бы работать на частоте 2 МГц. Эти программы работали достаточно быстро.

Интерпретатор байт-кода обычно основывается на таблице переходов, а не на цепочке if/then/else операторов. В C или C ++ это может включать оператор switch. По сути, интерпретатор должен иметь эквивалент массива кода обработки и использовать код операции в инструкции байтового кода в качестве индекса массива.

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

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

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

0 голосов
/ 03 сентября 2010

12 МГц - это ATtiny, 8-битный микропроцессор.Это означает (например), что собственная инструкция «Добавить» может добавить только два 8-разрядных числа вместе, чтобы получить 9-разрядный результат. JVM - это, по сути, виртуальный 32-разрядный процессор. Это означает, что его инструкция добавления добавляет два 32-разрядныхбитовые числа вместе, чтобы получить 33-битный результат.

Таким образом, при сравнении скоростей команд следует ожидать уменьшения скорости команд на 4: 1 как абсолютного минимума. В действительности, хотя это легкочтобы имитировать 32-разрядное добавление с 4 8-разрядными добавлениями (с переносами), некоторые вещи масштабируются не совсем так. Например, согласно собственному замечанию приложения Atmel , умножение 16x16 дает32-битный результат выполняется за ~ 218 тактов. В той же заметке приложения показано 16/16 битное деление (с 8-битным результатом), работающее за 255 тактов.

При условии линейного масштабирования мы можем ожидать 32версии умножения должны занимать ~ 425-450 тактов, а деление ~ 510 тактов. В действительности, мы, вероятно, ожидаем небольшую дополнительную нагрузку, которая уменьшитСкорость еще выше - добавление не менее 10% к этим оценкам, вероятно, делает их более реалистичными.

Итог: когда вы сравниваете яблоки с яблоками, становится очевидным, что большая разница в скоростиразговор вообще не реален (или не связан с издержками JVM в любом случае).

0 голосов
/ 03 сентября 2010

Есть два разных подхода к этому вопросу.

(i) «почему нормально запускать медленный код»

Как уже упоминал Джеймс, иногда скорость выполнения невсе, что вас интересует. Для многих приложений, работающих в интерпретируемом режиме, может быть «достаточно быстро».Вы должны учитывать, как будет использоваться код, который вы пишете.

(ii) «почему интерпретируемый код неэффективен»

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

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

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

0 голосов
/ 03 сентября 2010

Но разве Android не должен быть ужасно медленным?

Определите «ужасно медленный». Это телефон. Он должен обработать «Набрать первую цифру», прежде чем набрать вторую цифру.

В любом интерактивном приложении ограничивающим фактором всегда является время реакции человека. Это может быть в 100 раз медленнее и все же быстрее, чем пользователь.

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

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

Интерпретаторы байт-кода могут быть быстрее строки if () с использованием таблицы переходов:

 void (*jmp_tbl)[256] = ...;  /* array of function pointers */
 byte op = *program_counter++;
 jmp_tbl[op]();
...