O Pool de Buffers de Memória no Horse é uma otimização de alta performance projetada para minimizar o overhead de alocação de memória (heap churn) e a contenção de locks no Gerenciador de Memória sob cargas pesadas de requisições concorrentes.
Normalmente, frameworks web alocam novas estruturas de memória para cada payload de requisição e resposta HTTP (usando TMemoryStream ou TBytesStream). Em aplicações web altamente concorrentes em Delphi/FPC, a alocação e liberação contínua (GetMem/FreeMem) leva a:
- Fragmentação de Heap: Divisão da memória física em blocos muito pequenos.
- Contenção de Lock: Servidores multithread travando o mutex global do gerenciador de memória do sistema operacional ao alocar memória, causando pequenos travamentos (stalls) e reduzindo a eficiência da CPU.
O Horse implementa um pool de buffers thread-safe baseado em pilha (THorseMemoryBufferPool) e uma implementação de stream reciclável (THorsePooledStream herdando de TStream).
Em vez de alocar nova memória:
- Request Body: Provedores de alta performance (como HttpSys e Epoll) adquirem um buffer de stream pré-alocado do pool para ler e processar os dados vindos do socket.
- Response Body: Handlers enviando bytes ou respostas vazias adquirem streams do pool.
- Reciclagem Automática: Quando o processamento termina e o método
.Freeé acionado no stream, o buffer deTBytessubjacente é automaticamente devolvido ao pool global para a próxima requisição.
Isso resulta em um mapeamento zero-allocation de payloads de requisição e resposta para a vasta maioria das operações HTTP.
Para desenvolvedores que apenas utilizam o framework, esta otimização é 100% transparente. Você não precisa reescrever nenhuma rota ou middleware para usufruir dos ganhos. O código típico permanece exatamente igual:
THorse.Get('/ping',
procedure(Req: THorseRequest; Res: THorseResponse)
begin
Res.Send('pong'); // Utiliza o Pool de Buffers de forma automática por baixo dos panos
end);Se você está desenvolvendo middlewares customizados ou rotas que necessitam de streaming pesado ou manipulação manual de arquivos, você pode voluntariamente aproveitar o pool de buffers para evitar alocações de heap:
uses
Horse,
Horse.Core.MemoryBufferPool,
System.Classes;
begin
THorse.Get('/relatorio',
procedure(Req: THorseRequest; Res: THorseResponse)
var
LStream: TStream;
begin
// Adquire um stream pré-alocado do pool global de forma thread-safe
LStream := THorseMemoryBufferPool.DefaultPool.AcquireStream;
try
// Grava o conteúdo no stream reciclado
LStream.WriteBuffer(PChar('Dados do Relatório...')^, 21);
// Retorna o stream. O Horse enviará os dados e chamará LStream.Free,
// devolvendo o buffer de bytes automaticamente para o pool.
Res.SendFile(LStream, 'relatorio.txt');
except
LStream.Free; // Garante a devolução ao pool em caso de exceção
raise;
end;
end);
THorse.Listen(9000);
end.Por padrão, o pool global inicializa com as seguintes diretrizes:
- Tamanho Padrão do Buffer:
65.536 bytes (64 KB). - Capacidade Máxima do Pool:
1.024buffers inativos mantidos em RAM. - Tamanho Máximo Reciclável:
2.097.152 bytes (2 MB).
Note
Se um stream crescer dinamicamente além de 2 MB (ex: processando um upload muito grande), ele é descartado no momento da destruição e um novo buffer padrão de 64 KB é reinserido no pool para prevenir vazamento e manter o consumo de RAM estável.