Сам принцип работы нейросетей подразумевает, что они будут давать наиболее удобный, а не наиболее честный ответ. Буквально “предсказатель наиболее вероятного следующего слова”.
Поэтому к ИИ всегда стоит относиться критически и всегда все по 7 раз уточнять и переспрашивать. И конечно сверяться с другими источниками.
Проблема в том что компилятор не может сам адаптировать, какой размер зависимостей для какого размера проекта подключать. Вот и выходит что человек написавший свою первую программу, видит 2 мегабайта, думает что язык перегружен и уходит обратно на Python или на чем он там писал раньше.
Практически всегда во всех примерах и учебниках навязывают именно iostream, и совершенно не поясняют что это далеко не единственный способ вывода.
Для данного примера -О3 не дает эффекта. Это флаг оптимизации отвечает за скорость программы в рантайме, а не за размер бинарника. Хотя есть флаг и для этого -Os но обычно он выигрыша не дает.
Можно сделать неразделяемый взаимосвязанный монолит, а можно попытаться сделать все эти зависимости менее взаимосвязанными, чтобы линкер смог выкинуть неиспользуемое.
Чтобы все эти паре-тройке вариантов можно было выкинуть ели они явно не нужны.
Винда грешит тем, что даже при -static, она может неявно подключать динамические зависимости. Хотя для того же msvcrt.dll и kernel32.dll это не столь большая проблема. Так как вендор единый, они 100% будут в системе начиная с Win2000.
А вот ucrtbase.dll относительно новый, и уже могут быть системы где его еще не существовало.
Статическая линковка пихает в бинарник то, что вам бы пришлось тащить в виде .dll/.so в папку вашего проекта. Динамическая библиотека грузится полностью в память, а вот при статической линковке потенциально уже может вырезать только нужные для работы объектные файлы.
И, собственно всё. Иные флаги оптимизаций не влияют на размер при текущей кодовой базе.
То есть ни -flto/-flto=thin, ни -fno-exceptions ни даже специальный флаг GCC -fwhole-program не помогли выкинуть лишнее в случае включения iostream. Видимо линкер считает все его зависимости используемыми.
С printf также, линкер даже со всеми навешенными оптимизациями может просто не найти неиспользуемые символы чтобы их выкинуть.
Оптимизации линкера очень хороши, но они бессильны если из конкретной сборки стандартной либы физически выкидывать нечего.
Что на счет отказа от CRT - я это использовал в качестве сравнительного примера именно для Hello World и только под Windows.
Однако для проектов посложнее, однозначно что этот путь будет крайне тернист и использование стандартного CRT будет куда выгоднее.
Я даже не стал приводить приводить примеров самопального CRT для Linux, т.к. там уже понадобились бы прямые ассемблерные вставки, что уже требует специализаций под конкретную архитектуру, а там уже есть чему ломаться. У части пользователей это могло бы просто не запуститься или что еще хуже выполнить это некорректно. Тут рациональнее использовать статическую линковку с musl заместо glibc.
В вашем примере вы полагаетесь на то, что в системе будут и libstdc++ и libc и libm и еще от компилятора libgcc_s.
Если хоть чего-то из этого не будет, или же будет не той версии которой нужно (привет от GLIBC) - ваш файл превращается в тыкву. Он не запуститься в среде с musl или же где нет компилятора с его libgcc_s.so.
Я пытался сделать обзор проблемы именно с точки зрения разработчика С++, а не как ревер-инженер на ASM. Целью этого мини-исследования было именно практически добиться минимально возможного размера компилятором С++ (в данном случае GCC) и показать развитие тенденции вместе с обновлением компилятора, конкретное содержание PE заголовков для этого мне показалось излишним. Хотя это вполне имеет место быть, и спасибо вам за дополнение!
Проблема в том, что более высокоуровневые языки почему-то не выкидывают лишнее в очевидных ситуациях, когда исключения попросту не требуются. Например компилятор GCC догадывается не прикручивать исключения для Hello Worldно как только в коде появляется malloc()/free() компилятор уже может их включить, и тут уже нужно явно указывать флаг что стоит отключить добавление механизма исключений.
В вашем примере для того чтобы добиться размера меньше, вам уже пришлось использовать ASM, что уже не совсем разработка на С++ :)
Компилятор по идее для того и нужен чтобы упростить жизнь разработчику и не заставлять его писать на ASM из-за того что компилятор не справился со своей задачей лучше программиста.
Ловите зеркало :)
Сам принцип работы нейросетей подразумевает, что они будут давать наиболее удобный, а не наиболее честный ответ. Буквально “предсказатель наиболее вероятного следующего слова”.
Поэтому к ИИ всегда стоит относиться критически и всегда все по 7 раз уточнять и переспрашивать. И конечно сверяться с другими источниками.
Проблема в том что компилятор не может сам адаптировать, какой размер зависимостей для какого размера проекта подключать. Вот и выходит что человек написавший свою первую программу, видит 2 мегабайта, думает что язык перегружен и уходит обратно на Python или на чем он там писал раньше.
Практически всегда во всех примерах и учебниках навязывают именно
iostream, и совершенно не поясняют что это далеко не единственный способ вывода.Таргет вроде бы явный -
hello.exeа не простоhello. Да системные вызовы чисто виндовые.В третьем же абзаце было упоминание
-staticВыбрать удачное название статьи так, чтобы было и эффектно, и просто, и чтобы все всё правильно поняли - тот еще квест.
Для данного примера
-О3не дает эффекта. Это флаг оптимизации отвечает за скорость программы в рантайме, а не за размер бинарника. Хотя есть флаг и для этого-Osно обычно он выигрыша не дает.Можно сделать неразделяемый взаимосвязанный монолит, а можно попытаться сделать все эти зависимости менее взаимосвязанными, чтобы линкер смог выкинуть неиспользуемое.
Чтобы все эти
паре-тройке вариантовможно было выкинуть ели они явно не нужны.Так вроде бы и называл это "системным вызовом" :)
Винда грешит тем, что даже при
-static, она может неявно подключать динамические зависимости. Хотя для того жеmsvcrt.dllиkernel32.dllэто не столь большая проблема. Так как вендор единый, они 100% будут в системе начиная с Win2000.А вот
ucrtbase.dllотносительно новый, и уже могут быть системы где его еще не существовало.Вы забыли указать
-static- вам откроется портал кроличей норы :)Статическая линковка пихает в бинарник то, что вам бы пришлось тащить в виде
.dll/.soв папку вашего проекта. Динамическая библиотека грузится полностью в память, а вот при статической линковке потенциально уже может вырезать только нужные для работы объектные файлы.Так у вас же
dynamically linked:)Использовали ли вы флаг
-static? На линухе оно неизбежно будет тащить либоglibcлибоmusl.Я упоминал, что:
То есть ни
-flto/-flto=thin, ни-fno-exceptionsни даже специальный флаг GCC-fwhole-programне помогли выкинуть лишнее в случае включенияiostream. Видимо линкер считает все его зависимости используемыми.С
printfтакже, линкер даже со всеми навешенными оптимизациями может просто не найти неиспользуемые символы чтобы их выкинуть.Оптимизации линкера очень хороши, но они бессильны если из конкретной сборки стандартной либы физически выкидывать нечего.
Что на счет отказа от
CRT- я это использовал в качестве сравнительного примера именно дляHello Worldи только подWindows.Однако для проектов посложнее, однозначно что этот путь будет крайне тернист и использование стандартного
CRTбудет куда выгоднее.Я даже не стал приводить приводить примеров самопального
CRTдляLinux, т.к. там уже понадобились бы прямые ассемблерные вставки, что уже требует специализаций под конкретную архитектуру, а там уже есть чему ломаться. У части пользователей это могло бы просто не запуститься или что еще хуже выполнить это некорректно. Тут рациональнее использовать статическую линковку сmuslзаместоglibc.Это же касается только RAM, кризис HDD пока еще не наступил, так что экономия места там мало кого волнует.
Как только вы начинаете полагаться на то что
libstdc++будет предустановлена в системе, есть риск, что на другом компьютере вы можете столкнуться cИли какой-либо другой внешней библиотеки на которую вы полагаетесь. Это уменьшает переносимость и вынуждает качать внешние зависимости.
В вашем примере вы полагаетесь на то, что в системе будут и
libstdc++иlibcиlibmи еще от компилятораlibgcc_s.Если хоть чего-то из этого не будет, или же будет не той версии которой нужно (привет от
GLIBC) - ваш файл превращается в тыкву. Он не запуститься в среде сmuslили же где нет компилятора с егоlibgcc_s.so.Я пытался сделать обзор проблемы именно с точки зрения разработчика С++, а не как ревер-инженер на ASM. Целью этого мини-исследования было именно практически добиться минимально возможного размера компилятором С++ (в данном случае GCC) и показать развитие тенденции вместе с обновлением компилятора, конкретное содержание PE заголовков для этого мне показалось излишним. Хотя это вполне имеет место быть, и спасибо вам за дополнение!
Проблема в том, что более высокоуровневые языки почему-то не выкидывают лишнее в очевидных ситуациях, когда исключения попросту не требуются. Например компилятор GCC догадывается не прикручивать исключения для
Hello Worldно как только в коде появляетсяmalloc()/free()компилятор уже может их включить, и тут уже нужно явно указывать флаг что стоит отключить добавление механизма исключений.В вашем примере для того чтобы добиться размера меньше, вам уже пришлось использовать ASM, что уже не совсем разработка на С++ :)
Компилятор по идее для того и нужен чтобы упростить жизнь разработчику и не заставлять его писать на ASM из-за того что компилятор не справился со своей задачей лучше программиста.