Почему сгенерированные бинарные файлы настолько велики?

Почему двоичные файлы, сгенерированные при компиляции моих С++-программ настолько велики (как в 10 раз меньше файлов исходного кода)? Какие преимущества дает это предложение на интерпретируемых языках, для которых такая компиляция не нужна (и, следовательно, размер программы - это только размер файлов кода)?

Ответ 1

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

Несколько точек:

  • Чем сложнее команды в исходном коде, тем больше операций машинного кода может потребоваться для их выполнения. Таким образом, языковые функции более высокого уровня имеют более высокий коэффициент скомпилированного кода и исходного кода. Это не обязательно плохо: подумайте об этом как о "Мне нужно лишь немного рассказать о том, что я хочу сделать, и он раскрывает все эти необходимые шаги". Задача в программировании - обеспечить их необходимость - для этого требуется хорошая библиотека и дизайн программы.
  • Компилятор часто преднамеренно решает обменять некоторый размер исполняемого файла для более быстрой ожидаемой скорости выполнения: встроенный и внекорректный код является частью этого компромисса, хотя для небольших функций они не могут быть последовательно более компактными.
  • Более сложные среды выполнения (например, добавление поддержки исключений С++) могут включать в себя немного дополнительного кода, который запускается, когда программа сначала начинает создавать необходимую среду для этой языковой функции.
  • Функция библиотек может быть несовместимой. Помимо типа дополнительных библиотек, которые вы, скорее всего, должны были отследить себя и быть в курсе использования (например, XML, разбора PDF файлов, OpenGL), языки часто спокойно используют вспомогательные библиотеки для того, что похоже на языковые функции и функций. Любое из них может быть удивительно большим.
    • Например, многие интерпретаторы просто выставляют оператор C-библиотеки printf() или что-то подобное, в то время как для форматирования вывода С++ имеет ostream - более сложную, расширяемую и безопасную по типу систему с (для лучшего или худшего) постоянным состоянием через вызовы функций, процедуры для запроса и установки этого состояния, дополнительный уровень настраиваемой буферизации, настраиваемые типы символов и локализацию и, как правило, множество небольших встроенных функций, которые могут привести к меньшим или большим программам в зависимости от точных настроек использования и компилятора. Что лучше всего зависит от ваших целей приложения и памяти и производительности.
  • Операторы встроенного языка могут быть скомпилированы по-разному: a switch в целочисленном выражении и 100 случайных меток распределены случайным образом между 1 и 1000: один компилятор/языки могут решить "упаковать" 100 случаев и выполнить двоичный поиск для совпадение, другое - использовать малонаселенный массив из 1000 элементов и делать прямую индексацию (что тратит пространство в исполняемом файле, но обычно делает для более быстрого кода). Поэтому трудно сделать выводы на основе размера исполняемого файла.

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

Ответ 2

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

Ответ 3

Возьмем тривиальный случай: предположим, что у вас есть однострочная программа

print("hello world")

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

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

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

Ответ 4

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

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

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

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