Technický článek

Row-block buněčné úložiště a streamované ukládání XLSX v HotXLS

HotXLS ukládá buňky listu v kompaktních 256řádkových blocích, řeší formátování řádků, sloupců a obdélníků přes lazy interval overlay místo aby vytvářel objekty buněk, a při ukládání streamuje každý řádek rovnou do deflate streamu balíčku. Společně tyto tři změny rozhodují o profilu paměti velkého sešitu: špičkové využití paměti sleduje největší jediný řádek spíše než velikost kompletního XML listu

Důvod, proč to má váhu, je tvar, se kterým se každý vývojář tabulek dříve či později setká. Uživatel zformátuje celý sloupec — jedno kliknutí, milion buněk — a naivní objektový model odpoví alokováním milionu objektů buněk, aby držel jeden index number format. Soubor na disku zůstane maličký, protože formát XLSX to vyjadřuje jako jedinou položku <col>. Proces maličký vůbec nezůstane

Proč stojí formátování sloupce víc paměti než jeho vyplnění?

Protože formátování nemá data, která by ospravedlňovala objekt. Buňka s hodnotou musí někde existovat. Buňka, která je prázdná, ale stylovaná, existuje jen proto, aby nesla index stylu, a materializovat miliony takových je klasický způsob, jak aplikace tabulky v Delphi dojde adresního prostoru na souboru, který Excel otevře okamžitě

Interval style overlay tuto potřebu odstraňují. Instrukce formátování řádku, sloupce nebo obdélníku se uloží jednou jako rozsah plus části stylu, které přispívá, a líně se řeší, když je buňka v tomto rozsahu skutečně přistoupena. Overlay přežijí strukturální úpravy — vložení řádku uvnitř formátovaného bloku posune interval místo aby ho přestavělo — a v round tripu se vracejí jako kompaktní položky sloupců, řádků a style-only buněk, což je přesně to, jak je zapisuje Excel

var
  Sheet: TXLSXWorksheet;
  State: TXLSXCellStyleState;
begin
  Sheet := Workbook.Sheets[1];
  // Style indexes come from the workbook style pools, e.g. from a cell
  // you have already formatted the way you want the range to look
  State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
  // Format columns B..D without creating a single empty cell object
  Sheet.StyleOverlays.Add(1, 2, MaxRowIndex, 4,
    [xfpNumberFormat, xfpAlignment], State);
end;

TXLSXFormatParts je sada, která rozhoduje, co overlay přispívá: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment a xfpProtection. Jmenovat jen ty části, které myslíte, je to, co umožňuje overlay rozumně vrstvit — overlay sloupce dodávající number format nekoliduje s overlay řádku dodávající fill, protože ani jeden si nenárokuje část toho druhého

Co dává 256řádkový blok

Lokalitu. Buňky se drží v blocích po 256 řádcích se stabilními veřejnými handly, serializované row-major, takže zápis listu prochází pamětí v pořadí, v jakém bude emitovat bajty, místo aby naháněl pointery napříč heapem. Stabilní handly mají váhu pro API povrch: handle, který drží volající, zůstává platný napříč interní reorganizací, kterou rozložení bloků provádí, což dělá z kompaktní reprezentace implementační detail místo breaking change

Kompakce poolu stylů běží vedle. Před každým uložením se zahazují fonts, fills, borders, number formaty, alignmenty a protectiony, na které neodkazuje žádná buňka. Dlouho žijící sešity hromadí neodkazované style records tak, jak dlouho žijící dokumenty hromadí nepoužité styly, a sešit, který byl hodinu upravován uživatelem, jich může do souboru nanést stovky, které z něj nikdo nikdy nepřečte

Row-streamed ukládání a kdy se neaplikuje

S StreamingWrite zapnutým — výchozí stav — se každý řádek listu zapisuje přímo do deflate streamu balíčku. Alternativa, kterou příznak vypíná, staví nejprve kompletní XML listu a komprimuje ho dodatečně, takže špička paměti roste s celým listem. Streamování dělá, aby rostla s jedním řádkem

Shared strings a auxiliary parts následují stejnou kázeň přes jeden znovupoužitelný UTF-8 serializer, který emituje položky po jedné, a omezuje špičku paměti největší jedinou položkou spíše než celou částí. To pokrývá tabulku shared strings a pivot records, které na širokém analytickém sešitu bývají často větší než kterýkoli jednotlivý list

var
  Workbook: TXLSXWorkbook;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.Open('ledger-2026.xlsx');
    // StreamingWrite defaults to True; turn it off only when a downstream
    // step requires the whole worksheet XML to exist before compression
    Workbook.StreamingWrite := True;
    Workbook.SaveAs('ledger-2026-out.xlsx');
  finally
    Workbook.Free;
  end;
end;

Nechte ho zapnutý, pokud nemáte konkrétní důvod k vypnutí. Nestreamovací cesta existuje pro případy, kdy něco jiného v rouře potřebuje sestavené XML, a platit za ni ve výchozím stavu je platit za případ, do kterého většina aplikací nikdy nedoběhne

Jak poznat, zda se overlay skutečně používají

Sledujte počet buněk, ne graf paměti. Pokud list po širokém formátování hlásí uvěřitelný počet fyzických buněk, overlay odvádějí svou práci. Pokud počet vyskočí o velikost formátovaného rozsahu, něco v cestě kódu buňky materializovalo — obvykle smyčka, která čte každou buňku v rozsahu, aby zkontrolovala její styl, což vynutí řešení buňku po buňce a porazí celé uspořádání

Řešte styl, když potřebujete efektivní formát jedné buňky. Neřešte styl pro milion buněk, abyste zjistili, že sloupec má number format; zeptejte se overlay. Stejné pravidlo platí pro zápis: přiřazujte hodnoty buňkám, které hodnoty mají, a nechte formátování zůstat intervalem

Kam odchází zbytky paměti

Jakmile jsou buňky a styly kompaktní, dalšími největšími konzumenty na velkém sešitu jsou tabulka shared strings a satellite parts, které soubor nese — pivot caches, drawings, preserved XML z částí, které objektový model nemodeluje. Ty mají vlastní strategie a poctivá odpověď je, že žádné jediné nastavení je všechny nevyřeší naráz

Pokud je bottleneck otevíráním spíše než ukládáním, pákou je selektivní načítání: průvodce načítáním pouze z metadat a selektivním načítáním listů rozebírá čtení sešitu bez placení za listy, kterých se nebudete dotýkat. Pro propustnost na straně čtení u velmi velkých souborů viz poznámky k paralelnímu parsování XLSX a alokátoru paměti a pro workloady určené jen pro výstup, které nikdy nepotřebují objektový model, je streamované zápisy pro serverové dávkové úlohy obvykle lepší volbou než jakékoliv množství ladění zde

HotXLS čte a zapisuje XLS a XLSX z nativního kódu Delphi a C++Builder bez instalace Excelu a bez OLE automatizace, což dělá tyto paměťové charakteristiky pozorovatelnými a kontrolovatelnými vůbec poprvé — stránka spreadsheet komponenty HotXLS uvádí podporované formáty a verze RAD Studio