LINUX.ORG.RU

А насколько можно «обезжирить» программу на C++?

 


1

3

Предположим, есть у меня исходники программы на С++. Если я перепишу ее так, чтобы не зависеть от стандартной библиотеки, насколько это может сэкономить размер исполняемого файла?

Раньше так особо не делал, или писал как есть на плюсах или сразу на С.

★★★★★

Последнее исправление: LongLiveUbuntu (всего исправлений: 1)

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

anonymous
()

насколько это может сэкономить размер исполняемого файла?

И elf, и PE можно упаковать меньше 4К на диске. В памяти всё равно будет больше - по странице на код, bss и стэк. Но ты, если задаёшь такие вопросы, не потянешь. Крестовую библиотеку в хэлловорде еще можно как-то убрать, но без глибс ты далеко не уедешь.

LamerOk ★★★★★
()
Ответ на: комментарий от user_x

+1000 чтобы убрать шаблоны полностью.

И еще +10 в пользу того, чтобы полностью убрать исключения. Они тоже немного сжирают место.

Вот тогда программа станет маленькой и даже шелковистой как волосы у того парня в знаменитой рекламе из начала 90-х. Только есть один нюанс. Это будет уже не С++…

anonymous
()

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

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

А я так и сделал когда-то, когда писал для Palm OS. Сначала не мог понять, почему код на C++ такой жирный получался, и он просто не влезал в память устройства. Когда полностью исключил «исключения», то код на C++ получился такой же компактный, как и на голом C.

Ну, а с шаблонами по-моему все очевидно.

anonymous
()
Ответ на: комментарий от LongLiveUbuntu

Цель - сэкономить место на накопителе.

Сначала убедись, что на накопителе нет тех библиотек, что ты используешь. Иначе ты получишь обратный результат.

LamerOk ★★★★★
()

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

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

CrX ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

ну неиспользуемые секции ты удалишь, а дальше что? Суть-то в используемых.

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

CrX ★★★★★
()
Ответ на: комментарий от LamerOk

Вот прямо сейчас сделал 64-битный .exe-шник размером 960 байт.

Он запускается и выводит «Hello World» MessageBox’ом.

Исключительно опциями компилятора C++ и линкера в MSVC 2022, без всяких извратов с ассемблером и выкидыванием ДОС’овского стаба.

bigbit ★★★★★
()

Если я перепишу ее так, чтобы не зависеть от стандартной библиотеки

При этом стандартная библиотека уже присутствует на диске и скорее всего уже отмаплена в память каким-то другим приложением.

Классно ты оптимизировал, место на диске и память сэкономил.

bigbit ★★★★★
()

Пойдёт?

$ cat tiny.cpp
extern "C" void _start() {
    __asm__ volatile (
        "movl $1, %eax\n"  // syscall: sys_exit
        "xorl %ebx, %ebx\n" // status: 0
        "int $0x80\n"       // invoke syscall
    );
}

$ g++ -nostdlib -static -Wl,--gc-sections -ffunction-sections -Os -o tiny tiny.cpp

$ ls -la tiny
-rwxr-xr-x 1 kroz kroz 9056 Feb 13 20:26 tiny

P. S.

$ cat tiny.asm
section .text
    global _start

_start:
    mov eax, 1      ; syscall: sys_exit
    xor ebx, ebx    ; status: 0
    int 0x80        ; invoke syscall

$ nasm -f elf64 -o tiny.o tiny.asm
$ ld -n -o tiny tiny.o
$ strip --strip-all tiny
$ ls -la tiny
-rwxr-xr-x 1 kroz kroz 352 Feb 13 20:36 tiny
Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)

Линкуется же эта стандартная библиотека? Ну вот посмотри, какой там размер файлов библиотек.

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

Shushundr ★★★★
()
Последнее исправление: Shushundr (всего исправлений: 1)

Если я перепишу ее так, чтобы не зависеть от стандартной библиотеки

Я перепишу стандартную библиотеку, какую-то часть на aсеме.

Удачи, братан, с персоналом психдиспансера.

Ygor ★★★★★
()
Ответ на: комментарий от user_x

в основном шаблоны раздувают. Чем больше шаблонов, тем жирнее бинарь

смешно такое слышать, если понимать что компилятор шаблоны просто разворачивает в нужные классы/функции.

не хочешь шаблоны, пиши ручками все это, а нормальные программеры в это время пиво пьют)

Экономить нужно на собственном времени!

anonymous2 ★★★★★
()
Последнее исправление: anonymous2 (всего исправлений: 2)

700 байт с возможностью использования простых функций из стандратной C-билиотеки

#include <cstdlib>
extern "C" void _start() {
    _Exit(42);
}
user@host /tmp % g++ -s -nostartfiles -static -Wl,--gc-sections -Wl,-e_start -Wl,-nmagic -Wl,--build-id=none -fno-ident -ffunction-sections -fno-exceptions -fno-asynchronous-unwind-tables -Os -o tiny tiny.cpp 
/usr/bin/ld: warning: tiny has a LOAD segment with RWX permissions
user@host /tmp % ./tiny; echo $?
42
user@host /tmp % ls -l ./tiny
-rwxr-xr-x 1 user user 696 Feb 14 04:16 ./tiny

Все большие варианты - это всякие навороты линкера типа выравнивания секций и всяких информационных секций.

GPFault ★★★
()

В контексте линукса этот вопрос бессмыслен, потому что libstdc++ (кажется такое название) лежит по умолчанию в системе. Если же собирать тем же mingw, то это нужно тащить все плюсовые библиотеки вместе с программой, у MSVC приложений же плюсовые библиотеки ставятся в c:/windows/... через инсталлер и вроде бы статиком их не линкуют к приложению.

Skullnet ★★★★★
()
Последнее исправление: Skullnet (всего исправлений: 2)

Вообще обычно делают не так. Во-первых определись зачем тебе это и где ты не влазишь по памяти, а во-вторых отказ от STL обычно сопровождают заменой STL на её аналог, более производительный вроде EASTL или чего-то более стройного, вроде такого. В общем тут нужно просто поискать замену. Касательно «жирноты» - вообще не ясно о чём вы, ибо, как уже отметил выше - не хватает аргументации касательно того каким образом STL «съела вашу память», т.е. вы реально столкнулись с тем, что бинарник перестал куда-то влазить ? На какую-нибудь STM32 или arduino ? Или это просто возня ради возьни, дабы размер бинарника уместить в абстрактную норму ? Если это микроконтроллеры, то для них тоже существуют компактные версии и их нужно только найти

anonymous
()

так это легко проверить.

Использующие стандартную библиотеку программы на C++ линкуются с libstdc++.so - либо статически, либо динамически. Динамически получается вот так:

$ du -h /usr/lib/libstdc++.so.6.0.33
2.6M	/usr/lib/libstdc++.so.6.0.33

А вот статика:

#include <iostream>

int main() { 
  std::cout << "hello\n";
}
$ g++ -static-libstdc++ main.cpp
$ strip ./a.out
$ du -h ./a.out 
1.0M	./a.out

не знаю, зачем люди затирают про ассемблер, когда тут налицо такие жирные гуси.

И да, у меня аллергия на STL из-за подобных вещей.

Lrrr ★★★★★
()
Последнее исправление: Lrrr (всего исправлений: 2)
Ответ на: комментарий от Kroz

352

«Перспективные» и малоизвестные проекты ЯП (комментарий)

$ cat tiny.wsn

// $ wisnia tiny.wsn
// $ strip не нужен :)
// $ stat a.out
// $ wisnia tiny.wsn -d ir

fn main() {
}

File: ./a.out
Size: 193

              Target           |      Op      |               Arg1            |           Arg2
-------------------------------+--------------+-------------------------------+------------------------
 __builtin_exit  %% IDENT_VOID |     call     |                 %%            |            %%
                 %%            |    label     | __builtin_exit  %% IDENT_VOID |            %%
                 %%            |     xor      |      RDI        %% REGISTER   |  RDI       %% REGISTER
      RAX        %% REGISTER   |      <-      |       60        %% LIT_INT    |            %%
                 %%            |   syscall    |                 %%            |            %%

Но язык игрушечный, конечно.

dataman ★★★★★
()

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

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

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

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

Enthusiast ★★★
()
Ответ на: комментарий от LamerOk

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

Многие современные программы читают только сигнатуру и смещение «нового» PE-заголовка, начиная с 60-го байта.

hd -n 182 bzImage; objdump -p  bzImage
00000000  4d 5a 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |MZ..............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000030  00 00 00 00 00 00 00 00  cd 23 82 81 40 00 00 00  |.........#..@...|
00000040  50 45 00 00 64 86 04 00  00 00 00 00 00 00 00 00  |PE..d...........|
00000050  01 00 00 00 a0 00 06 02  0b 02 02 14 00 50 d0 00  |.............P..|
00000060  00 e0 03 00 00 00 00 00  93 e9 cf 00 00 50 00 00  |.............P..|
00000070  00 00 00 00 00 00 00 00  00 10 00 00 00 02 00 00  |................|
00000080  00 00 00 00 03 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 80 d4 00 00 10 00 00  00 00 00 00 0a 00 00 01  |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000000b6

bzImage:     file format pei-x86-64

Characteristics 0x20e
        executable
        line numbers stripped
        symbols stripped
        debugging information removed

Time/Date               Thu Jan  1 05:00:00 1970
Magic                   020b    (PE32+)
MajorLinkerVersion      2
MinorLinkerVersion      20
SizeOfCode              00d05000
SizeOfInitializedData   0003e000
SizeOfUninitializedData 00000000
AddressOfEntryPoint     0000000000cfe993
BaseOfCode              0000000000005000
ImageBase               0000000000000000
SectionAlignment        0000000000001000
FileAlignment           0000000000000200
MajorOSystemVersion     0
MinorOSystemVersion     0
MajorImageVersion       3
MinorImageVersion       0
MajorSubsystemVersion   0
MinorSubsystemVersion   0
Win32Version            00000000
SizeOfImage             00d48000
SizeOfHeaders           00001000
CheckSum                00000000
Subsystem               0000000a        (EFI application)
DllCharacteristics      00000100
SizeOfStackReserve      0000000000000000
SizeOfStackCommit       0000000000000000
SizeOfHeapReserve       0000000000000000
SizeOfHeapCommit        0000000000000000
LoaderFlags             00000000
NumberOfRvaAndSizes     00000006

The Data Directory
Entry 0 0000000000000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 0000000000000000 00000000 Import Directory [parts of .idata]
Entry 2 0000000000000000 00000000 Resource Directory [.rsrc]
Entry 3 0000000000000000 00000000 Exception Directory [.pdata]
Entry 4 0000000000000000 00000000 Security Directory
Entry 5 0000000000000000 00000000 Base Relocation Directory [.reloc]
Entry 6 0000000000000000 00000000 Debug Directory
Entry 7 0000000000000000 00000000 Description Directory
Entry 8 0000000000000000 00000000 Special Directory
Entry 9 0000000000000000 00000000 Thread Storage Directory [.tls]
Entry a 0000000000000000 00000000 Load Configuration Directory
Entry b 0000000000000000 00000000 Bound Import Directory
Entry c 0000000000000000 00000000 Import Address Table Directory
Entry d 0000000000000000 00000000 Delay Import Directory
Entry e 0000000000000000 00000000 CLR Runtime Header
Entry f 0000000000000000 00000000 Reserved
vM ★★
()
Ответ на: комментарий от Enthusiast

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

Во многих системах со страничной виртуальной памятью страницы для стека выделяются по pagefault’ам по мере использования.

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

Классические STLные контейнеры позволяют в каждом экземпляре использовать свой аллокатор.

vM ★★
()
Последнее исправление: vM (всего исправлений: 1)
Ответ на: комментарий от vM

>exit0

Это, очевидно, не программа, а пустой файл, и возвращаемое значение устанавливает не сама программа, а запускающий её родитель.

LamerOk ★★★★★
()
Последнее исправление: LamerOk (всего исправлений: 1)
Ответ на: комментарий от LongLiveUbuntu

уменьшить существующую

Что уменьшить, только размер exe-файла? Тогда динамическая линковка с (стандартными) либами; отказ от использования тяжелых «модулей» iostreams, fstream, regex, filesystem, которые генерируют много мусора ради «удобства» использования; также отказ от string и сложных контейнеров типа map, unordered_map; отказ от исключений (а это уже не c++); -flto возможно; clang -Oz; -g0; strip.

anonymous
()