Оптимизация размера плагина при использовании gcc.

Вадим Егоров [zg@matrica.apollo.lv]zg@matrica.apollo.lv

Введение.

В данной статье рассматривается вопрос об уменьшении размера плагинов, скомпилированных gcc.
Условия, при которых возможен существенный выигрыш в размере:
1. Вы явно или неявно используете в своем плагине оператор new.
2. Вы не используете обработку исключений и механизм rtti.

Для компиляции использовались gcc 2.95.3-4 и ld 2.11.90 (20010704).

Тестовый плагин.

test.cpp:

#include "plugin.hpp"

void WINAPI _export SetStartupInfo(const struct PluginStartupInfo *Info)
{
  int *a;
  a=new int;
  delete a;
}

test.def:

EXPORTS
SetStartupInfo=SetStartupInfo@4

makefile:

CFLAGS=-c -mcpu=pentiumpro -O3 -Wall
LFLAGS=-s -mdll

RM=rm
CC=gcc
DLLTOOL=dlltool

DEF=test.def
OBJS=test.o
TARGET=test.dll

all: $(TARGET)

$(TARGET): $(OBJS)
	$(CC) $(LFLAGS) -o nul -Xlinker --base-file -Xlinker $@.base $^ $(LIBS)
	$(DLLTOOL) --dllname $@ --base-file $@.base --output-exp $@.exp --def $(DEF)
	$(CC) $(LFLAGS) -o $@ $^ $@.exp $(LIBS)
	$(RM) $@.base
	$(RM) $@.exp

test.o: test.cpp
	$(CC) $(CFLAGS) $< -o $@

Примечание: -mcpu=pentiumpro означает всего лишь оптимизацию для P6+ процессоров, без использования команд отсутствующих в i386.

Оптимизация.

Запускаем make. Получаем test.dll размером 23552 байт. Незаслуженно много для такого простого плагина. Начинаем оптимизацию.

Шаг 1. На этом шаге из плагина исключается код для поддержки исключений и rtti. Добавляем в makefile:

CFLAGS=-c -mcpu=pentiumpro -O3 -Wall -fno-rtti -fno-exceptions

Но этого недостаточно. Дело в том, что в реализации глобальных операторов new и delete используются и исключения и rtti. Поэтому добавим в плагин свои версии этих операторов:

void * operator new(size_t size)
{
  return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
}

void operator delete(void *block)
{
  if(block) HeapFree(GetProcessHeap(),0,block);
}

Запускаем make. Получаем test.dll размером 3584 байт.
Если вы используете операторы new[] и delete[], то их тоже надо переписать:

void * operator new[](size_t size)
{
  return HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,size);
}

void operator delete[](void *block)
{
  if(block) HeapFree(GetProcessHeap(),0,block);
}

Шаг 2. Исключение startup code. Его размер в версии gcc, указанной выше, составляет 268 байт. Не так уж много, но все же. Добавляем в makefile:

LFLAGS=-s -mdll -nostartfiles

...и в test.cpp:

#ifdef __cplusplus
extern "C"{
#endif
  BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved);
#ifdef __cplusplus
};
#endif

BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved)
{
  return TRUE;
}

Запускаем make. Получаем test.dll размером 3072 байт.
Для этого плагина на этом можно и остановится. Но для реальных плагинов можно еще сэкономить килобайт, другой.

Шаг 3. Дополнительные флаги оптимизации.
Далее идет описание флагов, влияющих на оптимизацию, но не включенных в -O3. Ими надо пользоваться с осторожностью. Иногда может быть получен неожидаемый результат.

-fomit-frame-pointer - без этого флага для каждой функции генерируется пролог и эпилог примерно следующего вида:

push ebp
mov ebp,esp
...
mov esp,ebp
pop ebp

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

-fstrict-aliasing - включает оптимизацию, основываясь на типе выражения. Считается что указатели разных типов указываю на разные участки памяти. Исключение составляют поля в объединениях.

-funroll-loops - оптимизация по раскрытию циклов с заранее известным количеством итераций. Может приводить к увеличению размера кода.

-funroll-all-loops - оптимизация по раскрытию всех циклов. Может приводить к увеличению размера и увеличению времени выполнения кода.

-fno-builtin - для таких функций, как abort, abs, alloca, cos, exit, fabs, ffs, labs, memcmp, memcpy, sin, sqrt, strcmp, strcpy и strlen gcc по умолчанию вместо вызова библиотечных функций генерирует inline код. Данный флаг предотвращает это, уменьшая размер кода, но при этом увеличивая время выполнения.


 
16.07.2001
наверх