Ao contrário do que muitos pensam, um aplicativo feito em C/C++ não tem obrigatoriamente que ser mais rápido que outro feito em Java ou C#, pois essas plataformas, por já serem concebidas para serem mais *lentas*, normalmente já trazem embutidos os melhores métodos para a maioria das tarefas comuns dos aplicativos. Entre esses métodos, um dos mais importantes a destacar é o gerenciamento de memória através de heap space.
English version of this article: Memory management in Windows applications.
Chamadas intensivas de alocação de memória feitas diretamente para o sistema operacional podem ser considerávelmente lentas a medida que em sistemas multitarefa você concorre por essas chamadas com o restante dos aplicativos como em uma fila de espera, onde o sistema tenta localizar blocos contínuos disponíveis para cada aplicativo que precisa armazenar alguma informação, um de cada vez. Não é um tempo humanamente considerável, porém pode fazer toda diferença em um aplicativo que precisa receber e processar um grande volume de mensagens da rede, por exemplo.
Low-fragmentation heap, ou simplesmente LFH, é uma política de alocação de memória disponível na Win32API que se utiliza de blocos de tamanhos pré-determinados, onde ele sempre procura o menor bloco possível que seja grande o suficiente para comportar o tamanho solicitado no método HeapAlloc. Toda aplicação Windows possui um espaço de memória heap próprio que tenta absorver a carga de alocações, e esse espaço de memória também pode ser configurado para utilizar LFH ao invés da política padrão. Mesmo chamadas para malloc/free em aplicativos feitos com o Visual C++ utilizam HeapAlloc, de fato malloc/free existem somente para compatibilidade com o ANSI C.
Existem outros médotos conhecidos para otimizar memória, como o Circular Buffer disponível na biblioteca Boost, que não é um método de alocação, mas uma espécie de algoritimo bastante rápido de processamento de filas, porém esse método funciona melhor quando os blocos a serem alocados são do mesmo tamanho e lidos em sequência, além de que, como o próprio nome sugere, o buffer circular começa a sobrepor dados antigos quando os espaços se esgotam. Também podemos citar os gerenciadores de alocação multiplataforma como o nedmalloc, por exemplo, que tem uma implementação bastante simples até mesmo para aplicativos já prontos.
Veja a seguir uma comparação de performance entre os métodos de alocação utilizando o próprio aplicativo de teste que acompanha o nedmalloc:
Nesse gráfico é possível observar que a tanto a memória heap do processo, através de chamadas malloc/free, como as chamadas de private heap utilizando HeapAlloc/HeapFree, apresentam performance semelhantes estando com ou sem LFH ativado. Os dados também mostram que, apesar do nedmalloc conseguir resultado considerável em relação a alocação padrão, ele é superado com folga pelo LFH. Lembrando que esse teste simula uma condição artificial de estresse forçado que pode não representar a realidade de outros aplicativos, é sempre recomendado testes específicos de performance em aplicativos onde o tempo de resposta for crítico.
Para ativar o algoritimo LFH no espaço de memória padrão do seu aplicativo pode se utilizar o seguinte trecho de código no início do programa:
intptr_t hCrtHeap = _get_heap_handle(); ULONG ulEnableLFH = 2; HeapSetInformation((PVOID)hCrtHeap, HeapCompatibilityInformation, &ulEnableLFH, sizeof(ulEnableLFH));
Mas a história não acaba por aí. Em uma aplicação multitarefa as threads também podem concorrer entre sí para ter acesso aos recursos da memória heap padrão. Nesses casos é interessante separar a memória em partes independentes conhecidas como private heaps o que, através do método HeapCreate, cria blocos de alocação independentes do restante da memória do aplicativo e do sistema. Além de poder utilizar o LFH, esses blocos podem ser configurados com tamanho inicial, tamanho máximo ou até mesmo deixar para que ela aumente o espaço automaticamente conforme a demanda. Dessa forma cada fila de mensagens, cada tarefa ou qualquer outro tipo de organização pode ter seu próprio espaço de memória independente, como em um particionamento de disco, a fim de controlar o seu acesso e a sua concorrência. Essa técnica promove um grande alívio na espera por alocação em ambientes multitarefa.
A seguir um modelo de classe C++ que pode ser usada para facilitar o trabalho com private heaps:
#include <windows.h> class HeapSpace { public: HeapSpace(void); HeapSpace(unsigned long InitialSize); HeapSpace(unsigned long InitialSize, unsigned long MaximumSize); ~HeapSpace(void); private: HANDLE HeapHandler; private: void initialize(unsigned long InitialSize, unsigned long MaximumSize); public: void* alloc(unsigned long Size); public: void* reAlloc(void* MemoryPointer, unsigned long NewSize); public: void free(void* MemoryPointer); }; HeapSpace::HeapSpace(void) { this->initialize(0, 0); } HeapSpace::HeapSpace(unsigned long InitialSize) { this->initialize(InitialSize, 0); } HeapSpace::HeapSpace(unsigned long InitialSize, unsigned long MaximumSize) { this->initialize(InitialSize, MaximumSize); } HeapSpace::~HeapSpace(void) { HeapDestroy(this->HeapHandler); } void HeapSpace::initialize(unsigned long InitialSize, unsigned long MaximumSize) { //initialize the heap this->HeapHandler = HeapCreate(0, InitialSize, MaximumSize); //set low-fragmentation algorithmic ULONG HeapFragValue = 2; HeapSetInformation(this->HeapHandler, HeapCompatibilityInformation, &HeapFragValue, sizeof(HeapFragValue)); } void* HeapSpace::alloc(unsigned long Size) { return HeapAlloc(this->HeapHandler, 0, Size); } void* HeapSpace::reAlloc(void* MemoryPointer, unsigned long NewSize) { return HeapReAlloc(this->HeapHandler, 0, MemoryPointer, NewSize); } void HeapSpace::free(void* MemoryPointer) { HeapFree(this->HeapHandler, 0, MemoryPointer); }
Essa classe pode ser utilizada como no exemplo a seguir:
//cria heap com 10MB HeapSpace* MessageHeap = new HeapSpace(10485760); //aloca 256 bytes void* HeapAllocated = MessageHeap->alloc(256); //libera espaço alocado MessageHeap->free(HeapAllocated); //apaga heap inteira delete MessageHeap;
No comments:
Post a Comment