Artigo Técnico

Armazenamento de Células em Blocos de Linha e Salvamento em Streaming de XLSX no HotXLS

O HotXLS armazena células de planilha em blocos compactos de 256 linhas, resolve formatação de linha, coluna e retângulo por meio de sobreposições preguiçosas de intervalo em vez de criar objetos de célula, e faz streaming de cada linha direto no stream deflate do pacote ao salvar. Juntas essas três mudanças decidem o perfil de memória de uma pasta de trabalho grande: o pico de uso segue a maior linha única em vez do tamanho do XML completo da planilha

O motivo pelo qual isso importa é uma forma que todo desenvolvedor de planilha encontra mais cedo ou mais tarde. Um usuário formata uma coluna inteira — um clique, um milhão de células — e um modelo de objetos ingênuo responde alocando um milhão de objetos de célula para segurar um índice de formato numérico. O arquivo no disco continua minúsculo porque o formato XLSX expressa isso como uma única entrada <col>. O processo de forma nenhuma continua minúsculo

Por que formatar uma coluna custa mais memória do que preenchê-la?

Porque formatação não tem dados para justificar o objeto. Uma célula com um valor precisa existir em algum lugar. Uma célula que está vazia mas estilizada existe apenas para carregar um índice de estilo, e materializar milhões dessas é o jeito clássico de uma aplicação de planilha em Delphi ficar sem espaço de endereçamento em um arquivo que o Excel abre instantaneamente

Sobreposições de estilo por intervalo removem a necessidade. Uma instrução de formatação de linha, coluna ou retângulo é armazenada uma vez como um intervalo mais as partes de estilo que ela contribui, e resolve preguiçosamente quando uma célula naquele intervalo é de fato acessada. Sobreposições sobrevivem a edições estruturais — inserir uma linha dentro de um bloco formatado move o intervalo em vez de reconstruí-lo — e fazem ida e volta como entradas compactas de coluna, linha e célula somente estilo, que é exatamente como o Excel as escreve

Diagrama de sobreposição de estilos por intervalo para o HotXLS mostrando uma faixa de formatação de coluna armazenada como um único record com partes de formato reivindicadas em vez de um milhão de objetos de célula vazios em Delphi
Um formato de coluna, linha ou retângulo vive uma vez como um registro de sobreposição de intervalo reivindicando apenas as partes que possui. O estilo efetivo é montado preguiçosamente na primeira vez em que uma célula dentro do intervalo é realmente tocada
var
  Sheet: TXLSXWorksheet;
  State: TXLSXCellStyleState;
begin
  Sheet := Workbook.Sheets[1];
  // Os índices de estilo vêm dos pools de estilo da pasta de trabalho, por exemplo, de uma célula
  // que você já formatou do jeito que quer que o intervalo fique
  State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
  // Formata as colunas B..D sem criar um único objeto de célula vazia
  Sheet.StyleOverlays.Add(1, 2, MaxRowIndex, 4,
    [xfpNumberFormat, xfpAlignment], State);
end;

TXLSXFormatParts é o conjunto que decide o que uma sobreposição contribui: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment e xfpProtection. Nomear apenas as partes que você quer dizer é o que permite que sobreposições se acumulem de forma sensata — uma sobreposição de coluna que fornece um formato numérico não briga com uma sobreposição de linha que fornece um preenchimento, porque nenhuma reivindica a parte da outra

O que o bloco de 256 linhas dá a você

Localidade. Células são mantidas em blocos de 256 linhas com handles públicos estáveis, serializadas em ordem de linha, então escrever uma planilha percorre memória na ordem em que vai emitir bytes em vez de perseguir ponteiros pelo heap. Handles estáveis importam para a superfície da API: um handle que um chamador segura segue válido através da reorganização interna que o layout de bloco realiza, que é o que torna a representação compacta um detalhe de implementação em vez de uma mudança quebradora

Diagrama de armazenamento em blocos de linhas mostrando células de planilha do HotXLS mantidas em blocos fixos de 256 linhas com handles Delphi estáveis e serialização linha a linha no fluxo deflate do XLSX
As células ficam em blocos fixos de 256 linhas que alocam apenas as linhas ocupadas e serializam na mesma ordem em que os bytes deixam o processo. Os handles públicos sobrevivem à reorganização interna, então o layout de blocos permanece invisível aos chamadores

A compactação do pool de estilos roda junto. Antes de cada salvamento, fontes, preenchimentos, bordas, formatos numéricos, alinhamentos e proteções que nenhuma célula referencia são descartados. Pastas de trabalho de longa vida acumulam registros de estilo sem referência do mesmo jeito que documentos de longa vida acumulam estilos sem uso, e uma pasta de trabalho editada por um usuário durante uma hora pode carregar centenas deles para um arquivo de onde ninguém nunca vai lê-los

Salvamento com streaming de linhas, e quando ele não se aplica

Com StreamingWrite habilitado — o padrão — cada linha da planilha é escrita diretamente no stream deflate do pacote. A alternativa, que é o que o flag desliga, constrói o XML completo da planilha primeiro e o comprime depois, então a memória de pico escala com a planilha inteira. Streaming faz escalar com uma linha

Strings compartilhadas e partes auxiliares seguem a mesma disciplina por meio de um serializador UTF-8 reutilizável que emite entradas uma de cada vez, limitando a memória de pico pela maior entrada única em vez da parte inteira. Isso cobre a tabela de strings compartilhadas e registros de tabela dinâmica, que numa pasta de trabalho analítica ampla costumam ser maiores do que qualquer planilha individual

var
  Workbook: TXLSXWorkbook;
begin
  Workbook := TXLSXWorkbook.Create;
  try
    Workbook.Open('ledger-2026.xlsx');
    // StreamingWrite tem padrão True; desligue apenas quando uma etapa
    // posterior exigir que o XML completo da planilha exista antes da compressão
    Workbook.StreamingWrite := True;
    Workbook.SaveAs('ledger-2026-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Deixe ligado a menos que você tenha um motivo concreto para não. O caminho sem streaming existe para os casos em que outra coisa no pipeline precisa do XML montado, e pagar por isso por padrão é pagar por um caso que a maioria das aplicações nunca atinge

Como dizer se sobreposições estão de fato em uso

Observe a contagem de células, não o gráfico de memória. Se uma planilha relata um número plausível de células físicas depois que você aplicou formatação ampla, as sobreposições estão fazendo seu trabalho. Se a contagem pula pelo tamanho do intervalo formatado, algo no caminho de código materializou as células — geralmente um loop que lê cada célula do intervalo para conferir seu estilo, o que força resolução uma célula por vez e derrota todo o arranjo

Diagrama StreamingWrite comparando streaming deflate por linha no HotXLS contra montar todo o XML da planilha em memória antes da compressão em Delphi
Com StreamingWrite ativado, cada linha flui direto para o stream deflate do pacote e a memória de pico acompanha uma linha. Desativar a flag primeiro monta todo o XML da planilha, o que escala a memória de pico com a planilha inteira

Resolva um estilo quando você precisa do formato efetivo de uma célula. Não resolva um estilo para um milhão de células para descobrir que a coluna tem um formato numérico; pergunte à sobreposição. A mesma regra se aplica a escrita: atribua valores às células que têm valores, e deixe formatação seguir como intervalo

Para onde vai a memória restante

Depois que células e estilos estão compactos, os maiores consumidores seguintes numa pasta de trabalho grande são a tabela de strings compartilhadas e qualquer parte satélite que o arquivo carregue — caches dinâmicos, drawings, XML preservado de partes que o modelo de objetos não modela. Esses têm suas próprias estratégias, e a resposta honesta é que nenhuma configuração única resolve todos de uma vez

Se o seu gargalo é abrir em vez de salvar, o carregamento seletivo é a alavanca: o guia sobre carregamento de planilha somente metadados e seletivo cobre ler uma pasta de trabalho sem pagar pelas planilhas que você não vai tocar. Para throughput de leitura em arquivos muito grandes, veja as notas sobre análise paralela de XLSX e o alocador de memória, e para cargas somente de saída que nunca precisam de um modelo de objetos de forma nenhuma, escrita em streaming para jobs em lote no servidor costuma ser um encaixe melhor do que qualquer ajuste aqui

O HotXLS lê e escreve XLS e XLSX a partir de código nativo em Delphi e C++Builder sem instalação do Excel e sem automação OLE, que é o que torna essas características de memória observáveis e controláveis em primeiro lugar — a página do componente de planilha HotXLS lista os formatos e versões do RAD Studio suportados