Почему двоичные файлы, сгенерированные при компиляции моих С++-программ настолько велики (как в 10 раз меньше файлов исходного кода)? Какие преимущества дает это предложение на интерпретируемых языках, для которых такая компиляция не нужна (и, следовательно, размер программы - это только размер файлов кода)?
Почему сгенерированные бинарные файлы настолько велики?
Ответ 1
Современные интерпретируемые языки обычно компилируют код для некоторого представления для более быстрого выполнения... он не может быть записан на диск, но, конечно же, нет гарантии, что программа представлена в более компактной форме. Некоторые переводчики отправляются на все свиньи и генерируют машинный код в любом случае (например, Java JIT). Тогда там сам интерпретатор сидит в памяти, которая может быть большой.
Несколько точек:
- Чем сложнее команды в исходном коде, тем больше операций машинного кода может потребоваться для их выполнения. Таким образом, языковые функции более высокого уровня имеют более высокий коэффициент скомпилированного кода и исходного кода. Это не обязательно плохо: подумайте об этом как о "Мне нужно лишь немного рассказать о том, что я хочу сделать, и он раскрывает все эти необходимые шаги". Задача в программировании - обеспечить их необходимость - для этого требуется хорошая библиотека и дизайн программы.
- Компилятор часто преднамеренно решает обменять некоторый размер исполняемого файла для более быстрой ожидаемой скорости выполнения: встроенный и внекорректный код является частью этого компромисса, хотя для небольших функций они не могут быть последовательно более компактными.
- Более сложные среды выполнения (например, добавление поддержки исключений С++) могут включать в себя немного дополнительного кода, который запускается, когда программа сначала начинает создавать необходимую среду для этой языковой функции.
- Функция библиотек может быть несовместимой. Помимо типа дополнительных библиотек, которые вы, скорее всего, должны были отследить себя и быть в курсе использования (например, XML, разбора PDF файлов, OpenGL), языки часто спокойно используют вспомогательные библиотеки для того, что похоже на языковые функции и функций. Любое из них может быть удивительно большим.
- Например, многие интерпретаторы просто выставляют оператор C-библиотеки
printf()
или что-то подобное, в то время как для форматирования вывода С++ имеетostream
- более сложную, расширяемую и безопасную по типу систему с (для лучшего или худшего) постоянным состоянием через вызовы функций, процедуры для запроса и установки этого состояния, дополнительный уровень настраиваемой буферизации, настраиваемые типы символов и локализацию и, как правило, множество небольших встроенных функций, которые могут привести к меньшим или большим программам в зависимости от точных настроек использования и компилятора. Что лучше всего зависит от ваших целей приложения и памяти и производительности.
- Например, многие интерпретаторы просто выставляют оператор C-библиотеки
- Операторы встроенного языка могут быть скомпилированы по-разному: a
switch
в целочисленном выражении и 100 случайных меток распределены случайным образом между 1 и 1000: один компилятор/языки могут решить "упаковать" 100 случаев и выполнить двоичный поиск для совпадение, другое - использовать малонаселенный массив из 1000 элементов и делать прямую индексацию (что тратит пространство в исполняемом файле, но обычно делает для более быстрого кода). Поэтому трудно сделать выводы на основе размера исполняемого файла.
Как правило, использование памяти и скорость выполнения становятся все более важными, поскольку программа становится все более сложной и сложной. Вы не видите такие системы, как операционные системы, корпоративные веб-серверы или полнофункциональные коммерческие текстовые процессоры, написанные на интерпретируемых языках, потому что они не имеют масштабируемости.
Ответ 2
Интерпретированные языки предполагают, что интерпретатор доступен, в то время как скомпилированные программы в большинстве случаев автономны.
Ответ 3
Возьмем тривиальный случай: предположим, что у вас есть однострочная программа
print("hello world")
что делает эта "печать"? Неужели ясно, что вы попросите какой-нибудь другой код сделать какую-то работу? И этот код не является бесплатным, общая сумма того, что нужно запустить, намного больше, чем строки кода, который вы пишете. В более реалистичных программах вы используете множество сложных библиотек, управляющих окнами и другими функциями пользовательского интерфейса, сетями, базами данных и т.д. Теперь, входит ли этот код в ваше приложение или загружен из DLL или присутствует в интерпретаторе, он должен быть где-то.
Существует много сделок между компиляцией и интерпретацией, а также промежуточными решениями, такими как подход для компиляции Java/байтового кода. Например, вы можете рассмотреть
- затраты времени на выполнение интерпретации источника каждый раз, когда вы запускаете, за счет выполнения скомпилированного кода
- преимущества переносимости переводчиков - вам нужно скомпилировать отдельные версии приложения для разных платформ.
Ответ 4
Обычно программы записываются на языках более высокого уровня, поскольку эти программы должны выполняться процессором, программы должны быть преобразованы в машинный код. Это преобразование выполняется с помощью Компилятора или Интерпретатора.
A Компилятор выполняет преобразование только один раз, а Интерпретатор обычно преобразует его каждый раз, когда программа выполняется.
Интерпретированные программы выполняются гораздо медленнее, чем скомпилированные программы, потому что интерпретатор должен анализировать каждый оператор в программе каждый раз, когда он выполняется, а затем выполнять требуемое действие, тогда как скомпилированный код просто выполняет действие в пределах фиксированный контекст, определяемый компиляцией (что является причиной наличия двоичных файлов большого размера).
Другим недостатком интерпретаторов является то, что они должны присутствовать в окружении в качестве дополнительного программного обеспечения для запуска исходного кода.