Artigo Técnico

Células em Blocos de Linha e Gravações XLSX em Fluxo no HotXLS

O HotXLS armazena células de folha de cálculo em blocos compactos de 256 linhas, resolve formatação de linhas, colunas e retângulos através de sobreposições de intervalo preguiçosas em vez de criar objetos de célula, e faz o fluxo de cada linha diretamente para o stream deflate do pacote ao gravar. Juntas essas três mudanças decidem o perfil de memória de um livro grande: o pico de uso segue a maior linha individual em vez do tamanho do XML completo da folha de cálculo

A razão pela qual isto importa é uma forma que todos os programadores de folhas de cálculo encontram eventualmente. Um utilizador 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 conter um único índice de formato numérico. O ficheiro em disco mantém-se minúsculo porque o formato XLSX expressa isso como uma única entrada <col>. O processo não se mantém minúsculo de todo

Porque razão é que formatar uma coluna custa mais memória do que preenchê-la?

Porque a formatação não tem dados para justificar o objeto. Uma célula com um valor tem de existir em algum lado. Uma célula que está vazia mas estilizada existe apenas para transportar um índice de estilo, e materializar milhões dessas é a forma clássica como uma aplicação de folha de cálculo Delphi fica sem espaço de endereçamento num ficheiro que o Excel abre instantaneamente

As sobreposições de estilo por intervalo removem essa necessidade. Uma instrução de formatação de linha, coluna ou retângulo é armazenada uma única vez como um intervalo mais as partes de estilo que contribui, e resolve-se preguiçosamente quando uma célula nesse intervalo é efetivamente acedida. As sobreposições sobrevivem a edições estruturais — inserir uma linha dentro de um bloco formatado move o intervalo em vez de o reconstruir — e fazem a ida e volta como entradas compactas de coluna, linha e célula só com estilo, que é exatamente como o Excel as escreve

Diagrama de sobreposição de estilos por intervalo para o HotXLS mostrando um intervalo de formatação de coluna armazenado como um único registo com as partes de formato que reivindica, 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 registo de sobreposição de intervalo que reivindica apenas as partes que possui. O estilo efetivo é montado preguiçosamente na primeira vez que uma célula dentro do intervalo é realmente tocada
var
  Sheet: TXLSXWorksheet;
  State: TXLSXCellStyleState;
begin
  Sheet := Workbook.Sheets[1];
  // Os índices de estilo provêm dos pools de estilos do livro, ex.: de uma célula
  // que já formatou da forma como pretende que o intervalo fique
  State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
  // Formate as colunas B..D sem criar um único objeto de célula vazio
  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 pretende é o que permite às sobreposições sobrepor-se de forma sensata — uma sobreposição de coluna que fornece um formato numérico não compete com uma sobreposição de linha que fornece um preenchimento, porque nenhuma reclama a parte da outra

O que o bloco de 256 linhas lhe dá

Localidade. As células são mantidas em blocos de 256 linhas com manípulos públicos estáveis, serializados linha-principal, pelo que escrever uma folha de cálculo percorre a memória pela ordem em que vai emitir bytes em vez de perseguir ponteiros pela heap. Os manípulos estáveis importam para a superfície da API: um manípulo que um chamador detém mantém-se válido através da reorganização interna que a disposição em blocos realiza, que é o que torna a representação compacta um detalhe de implementação em vez de uma mudança de rutura

Diagrama de armazenamento em blocos de linhas mostrando as células da folha de cálculo do HotXLS mantidas em blocos fixos de 256 linhas com handles Delphi estáveis e serialização por ordem de linhas para a stream deflate XLSX
As células assentam em blocos fixos de 256 linhas que alocam apenas linhas ocupadas e serializam pela mesma ordem em que os bytes saem do processo. Os handles públicos sobrevivem à reorganização interna, pelo que o layout de blocos permanece invisível aos chamadores

A compactação do conjunto de estilos corre ao lado dela. Antes de cada gravação, tipos de letra, preenchimentos, margens, formatos numéricos, alinhamentos e proteções que nenhuma célula referencia são descartados. Os livros antigos acumulam registos de estilo sem referência da mesma forma que os documentos antigos acumulam estilos não usados, e um livro que foi editado por um utilizador durante uma hora pode transportar centenas deles num ficheiro de onde ninguém os vai ler

Gravação em fluxo por linha, e quando não se aplica

Com StreamingWrite ativado — a predefinição — cada linha de folha de cálculo é escrita diretamente no stream deflate do pacote. A alternativa, que é o que o sinalizador desliga, constrói o XML completo da folha de cálculo primeiro e comprime-o depois, pelo que a memória de pico escala com a folha inteira. O fluxo faz com que escale com uma linha

As cadeias partilhadas e as partes auxiliares seguem a mesma disciplina através de um serializador UTF-8 reutilizável que emite entradas uma de cada vez, limitando a memória de pico pela maior entrada individual em vez de pela parte inteira. Isso cobre a tabela de cadeias partilhadas e os registos pivô, que num livro analítico largo são frequentemente maiores do que qualquer folha de cálculo individual

var
  Workbook: TXLSXWorkbook;
begin
  Workbook := TXLSXWorkbook.Create;
  try
    Workbook.Open('ledger-2026.xlsx');
    // StreamingWrite tem True por padrão; desative-o apenas quando um passo a jusante
    // exigir que o XML da folha inteira exista antes da compressão
    Workbook.StreamingWrite := True;
    Workbook.SaveAs('ledger-2026-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Deixe-o ligado a menos que tenha uma razão concreta para o contrário. O caminho não em fluxo existe para os casos em que outra coisa no pipeline precisa do XML montado, e pagá-lo por predefinição é pagar por um caso que a maioria das aplicações nunca atinge

Como dizer se as sobreposições estão efetivamente a ser usadas

Veja a contagem de células, não o gráfico de memória. Se uma folha de cálculo reporta um número plausível de células físicas depois de ter aplicado formatação ampla, as sobreposições estão a fazer o seu trabalho. Se a contagem salta pelo tamanho do intervalo formatado, algo no caminho de código materializou as células — normalmente um ciclo que lê cada célula no intervalo para verificar o seu estilo, o que força a resolução uma célula de cada vez e derrota todo o arranjo

Diagrama StreamingWrite comparando a stream deflate por linha no HotXLS com a montagem de todo o XML da folha de cálculo em memória antes da compressão, em Delphi
Com StreamingWrite ligado, cada linha flui direto para o fluxo deflate do pacote e a memória de pico acompanha uma linha. Desligar primeiro o sinalizador monta o XML inteiro da folha, o que faz a memória de pico escalar com a folha completa

Resolva um estilo quando 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 à escrita: atribua valores às células que têm valores, e deixe a formatação ficar um intervalo

Para onde vai a memória restante

Depois de células e estilos compactos, os maiores consumidores seguintes num livro grande são a tabela de cadeias partilhadas e quaisquer partes satélite que o ficheiro transporte — caches pivô, desenhos, XML preservado de partes que o modelo de objetos não modela. Essas têm as suas próprias estratégias, e a resposta honesta é que nenhuma definição isolada resolve todas de uma vez

Se o seu gargalo é abrir em vez de gravar, o carregamento seletivo é a alavanca: o guia de carregamento de folhas de cálculo apenas com metadados e seletivo aborda ler um livro sem pagar pelas folhas que não vai tocar. Para débito do caminho de leitura em ficheiros muito grandes, veja as notas sobre análise XLSX paralela e o alocador de memória, e para cargas de trabalho só de saída que nunca precisam de um modelo de objetos de todo, escritas em fluxo para trabalhos em lote de servidor é normalmente um encaixe melhor do que qualquer afinação aqui

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