Времена, когда программисты пытались выжать максимум из размера своего приложения, безвозвратно ушли. Основной причиной является существенное увеличение объемов оперативной памяти и дискового пространства на современных компьютерах. Немногие помнят, как при загрузке приложения с кассеты можно было пойти покушать. Или как можно было считать моргания дисковода, косвенно определяя размер приложения. Пожалуй, только разработчики програмного обеспечения под встраиваемые системы до сих пор заботятся о размере кода и потребляемой памяти. Могут ли таблетки и смартфоны вернуть разработчиков «назад в будущее»?
Данная статья призвана помочь разработчикам програмного обеспечения, использующим GCC компилятор, уменьшить размер кода своих приложений. Все данные в статье получены при помощи x86 GCC компилятора версии 4.7.2 на операционной система Fedora 17 для архитектуры Intel Atom.
Довольно существенный выигрыш с точки зрения размера GCC дает динамическая линковка (включенная по умолчанию). То, насколько динамическая линковка выигрывает у статической, сильно зависит от используемых библиотек.
Чаще всего, когда речь заходит об оптимизации размера, используется опция “-Os”. Ниже приведена табличка со средними геометрическими размера кода по набору приложений для смартфонов и таблеток.
Результаты в табличке показаны относительно “-Os”. Меньший результат говорит о меньшем размере кода. “-m32, -mfpmath=sse, -march=atom” включены по-умолчанию во всех случаях.
“-Ofast” (или “-O3”) и “-funroll-loops”, очевидно, увеличивают размер кода. Опция “-flto”, за счет более агрессивной подстановки функций (inline), также должна увеличивать размер кода. Однако, результат противоположный. Почему?
“-flto” делает возможным удаление неиспользуемых функций. Функция может стать таковой, если в конкретной конфигурации приложения она не вызывается или была полностью и во всех местах вызова подставлена в код. Для того, чтобы удалить неиспользуемые функции без “-flto” можно воспользоваться “-ffunction-sections -Wl,--gc-sections”. Данная техника дает хороший результат, если в приложении используются внутренние статические библиотеки.
Приложение все еще слишком большое? Есть еще несколько техник для уменьшения размера. По умолчанию GCC использует опцию “-fasynchronous-unwind-tables”, что увеличивает размер EH (exception handling) секции, даже при компиляции приложений на языке “C”. Это облегчает процесс отладки, однако может существенно увеличить размер кода. Для отключения надо добавить “-fno-asynchronous-unwind-tables” к опциям компиляции.
“-Wl,--strip-all” сообщит линкеру, что надо удалить всю символьную информацию. Это сделает процесс отладки еще сложнее. И все же, если размер кода критичен, опция приемлема.
Ниже приведена табличка отражающая эффект от добавления:
к различным опциям оптимизации.
Результаты в табличке показаны относительно “-Os”. Меньший результат говорит о меньшем размере кода. “-m32, -mfpmath=sse, -march=atom” включены по-умолчанию во всех случаях.
Ниже представлено описание используемых в статье опций компилятора. Полное описание (на английском): gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Optimize-Options.html
Ниже представлено описание используемых в статье опций линковщика. Полное описание (на английском): sourceware.org/binutils/docs/ld/Options.html
Данная статья призвана помочь разработчикам програмного обеспечения, использующим GCC компилятор, уменьшить размер кода своих приложений. Все данные в статье получены при помощи x86 GCC компилятора версии 4.7.2 на операционной система Fedora 17 для архитектуры Intel Atom.
Довольно существенный выигрыш с точки зрения размера GCC дает динамическая линковка (включенная по умолчанию). То, насколько динамическая линковка выигрывает у статической, сильно зависит от используемых библиотек.
Чаще всего, когда речь заходит об оптимизации размера, используется опция “-Os”. Ниже приведена табличка со средними геометрическими размера кода по набору приложений для смартфонов и таблеток.
Результаты в табличке показаны относительно “-Os”. Меньший результат говорит о меньшем размере кода. “-m32, -mfpmath=sse, -march=atom” включены по-умолчанию во всех случаях.
-O2 | 6% |
-O2 -flto | -5% |
-Ofast | 11,5% |
-Ofast -flto | 3% |
-Ofast -funroll-loops | 19% |
-Ofast -funroll-loops -flto | 10,5% |
“-Ofast” (или “-O3”) и “-funroll-loops”, очевидно, увеличивают размер кода. Опция “-flto”, за счет более агрессивной подстановки функций (inline), также должна увеличивать размер кода. Однако, результат противоположный. Почему?
“-flto” делает возможным удаление неиспользуемых функций. Функция может стать таковой, если в конкретной конфигурации приложения она не вызывается или была полностью и во всех местах вызова подставлена в код. Для того, чтобы удалить неиспользуемые функции без “-flto” можно воспользоваться “-ffunction-sections -Wl,--gc-sections”. Данная техника дает хороший результат, если в приложении используются внутренние статические библиотеки.
Приложение все еще слишком большое? Есть еще несколько техник для уменьшения размера. По умолчанию GCC использует опцию “-fasynchronous-unwind-tables”, что увеличивает размер EH (exception handling) секции, даже при компиляции приложений на языке “C”. Это облегчает процесс отладки, однако может существенно увеличить размер кода. Для отключения надо добавить “-fno-asynchronous-unwind-tables” к опциям компиляции.
“-Wl,--strip-all” сообщит линкеру, что надо удалить всю символьную информацию. Это сделает процесс отладки еще сложнее. И все же, если размер кода критичен, опция приемлема.
Ниже приведена табличка отражающая эффект от добавления:
- “-ffunction-sections -Wl,--gc-sections” (+ сборщик мусора)
- “-ffunction-sections -Wl,--gc-sections -fno-asynchronous-unwind-tables” (+ без таблиц раскрутки)
- “-ffunction-sections -Wl,--gc-sections -fno-asynchronous-unwind-tables -Wl,--strip-all” (+ удаление символов)
к различным опциям оптимизации.
Результаты в табличке показаны относительно “-Os”. Меньший результат говорит о меньшем размере кода. “-m32, -mfpmath=sse, -march=atom” включены по-умолчанию во всех случаях.
по умолчанию | + сборщик мусора | + без таблиц раскрутки | + удаление символов | |
-Os | - | -5% | -10,5% | -22,5% |
-O2 | 6% | 0,5% | -3,5% | -13,5% |
-O2 -flto | -5% | -5% | -8% | -17% |
-Ofast | 11,5% | 6% | 2% | -6,5% |
-Ofast -flto | 3% | 2,5% | 0,5% | -6,5% |
-Ofast -funroll-loops | 19% | 12,5% | 9,5% | 3% |
-Ofast -funroll-loops -flto | 10,5% | 10% | 8,5% | 2,5% |
Ниже представлено описание используемых в статье опций компилятора. Полное описание (на английском): gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Optimize-Options.html
- "-Ofast" аналогично "-O3 -ffast-math" включает более высокий уровень оптимизаций и более агрессивные оптимизации для арифметических вычислений (например, вещественную реассоциацию)
- "-flto" межмодульные оптимизации
- "-m32" 32 битный режим
- "-mfpmath=sse" включает использование XMM регистров в вещественной арифметике (вместо вещественного стека в x87 режиме)
- "-funroll-loops" включает развертывание циклов
- "-ffunction-sections" размещает каждую функцию в отдельной секции
- "-Os" оптимизирует производительность и размер
- "-fno-asynchronous-unwind-tables" гарантирует точность таблиц раскрутки только в пределах функции
Ниже представлено описание используемых в статье опций линковщика. Полное описание (на английском): sourceware.org/binutils/docs/ld/Options.html
- “--gc-sections” включает удаление неиспользуемых секций
- “--strip-all” удаляет символьную информацию