Technisch artikel

Rijblok-celopslag en gestreamde XLSX-opslag in HotXLS

HotXLS slaat werkbladcellen op in compacte 256-rij-blokken, lost rij-, kolom- en rechthoekopmaak op via luie interval-overlays in plaats van celobjecten te maken, en streamt elke rij rechtstreeks in de deflate-stream van het pakket bij opslaan. Samen beslissen die drie wijzigingen het geheugenprofiel van een grote werkmap: piekgebruik volgt de grootste enkele rij in plaats van de grootte van de volledige werkblad-XML

De reden dat dit er toe doet, is een vorm die elke spreadsheetontwikkelaar uiteindelijk tegenkomt. Een gebruiker formatteert een hele kolom — één klik, één miljoen cellen — en een naïef objectmodel antwoordt door een miljoen celobjecten te alloceren om één getalnotatie-index vast te houden. Het bestand op schijf blijft klein omdat het XLSX-formaat dat uitdrukt als één <col>-entry. Het proces blijft helemaal niet klein

Waarom kost formatteren van een kolom meer geheugen dan hem vullen?

Omdat opmaak geen gegevens heeft om het object te rechtvaardigen. Een cel met een waarde moet ergens bestaan. Een cel die leeg is maar gestyled bestaat alleen om een stijlindex te dragen, en er miljoenden materialiseren is de klassieke manier waarop een Delphi-spreadsheetapplicatie zonder adresruimte komt te zitten op een bestand dat Excel direct opent

Interval-stijloverlays nemen die noodzaak weg. Een rij-, kolom- of rechthoek-opmaakinstructie wordt één keer opgeslagen als een bereik plus de stijldelen die het bijdraagt, en lost lui op wanneer een cel in dat bereik daadwerkelijk wordt benaderd. Overlays overleven structurele bewerkingen — een rij invoegen binnen een geformatteerd blok verplaatst het interval in plaats van het te herbouwen — en ze round-trippen als compacte kolom-, rij- en alleen-stijl-cel-entries, wat precies is hoe Excel ze schrijft

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 is de verzameling die beslist wat een overlay bijdraagt: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment en xfpProtection. Alleen de delen benoemen die je bedoelt, is wat overlays verstandig laat schermen — een kolom-overlay die een getalnotatie levert, vecht niet met een rij-overlay die een vulling levert, want geen van beide claimt het deel van de ander

Wat het 256-rij-blok je geeft

Lokaliteit. Cellen worden vastgehouden in blokken van 256 rijen met stabiele publieke handles, rij-groot geserialiseerd, zodat het schrijven van een werkblad geheugen doorloopt in de volgorde waarin het bytes zal emitteeren in plaats van pointers over de heap na te jagen. Stabiele handles tellen voor de API-oppervlakte: een handle die een aanroeper vasthoudt blijft geldig door de interne reorganisatie die de blokindeling uitvoert, wat is wat de compacte representatie een implementatiedetail maakt in plaats van een breaking change

Stijlpool-compactie draait ernaast. Vóór elke opslag worden fonts, vullingen, randen, getalnotaties, uitlijningen en beschermingen waar geen cel naar verwijst gedropt. Langlevende werkmappen stapelen unreferenced-stijlrecords op zoals langlevende documenten ongebruikte stijlen opstapelen, en een werkmap die een uur door een gebruiker is bewerkt kan er honderden meedragen naar een bestand dat niemand ze ooit uit zal lezen

Rij-gestreamde opslag, en wanneer het niet van toepassing is

Met StreamingWrite ingeschakeld — de standaard — wordt elke werkbladrij rechtstreeks in de deflate-stream van het pakket geschreven. Het alternatief, wat is wat de flag uitzet, bouwt eerst de volledige werkblad-XML en comprimeert die daarna, zodat piekgeheugen schaalt met het hele werkblad. Streamen maakt het schalen met één rij

Gedeelde tekenreeksen en hulpparts volgen dezelfde discipline via één herbruikbare UTF-8-serializer die entries één voor één emitteert, en piekgeheugen begrenst op de grootste enkele entry in plaats van de hele part. Dat dekt de gedeelde-tekenreekstabel en pivot-records, die op een breed analytisch werkblad vaak groter zijn dan welk individueel werkblad dan ook

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;

Laat het aan tenzij je een concrete reden hebt het niet te doen. Het niet-streamende pad bestaat voor de gevallen waarin iets anders in de pijplijn de samengestelde XML nodig heeft, en er standaard voor betalen is betalen voor een geval dat de meeste applicaties nooit raken

Hoe vast te stellen of overlays daadwerkelijk worden gebruikt

Kijk naar het celantal, niet naar de geheugengrafiek. Als een werkblad na brede opmaak een plausibel aantal fysieke cellen rapporteert, doen overlays hun werk. Als het aantal springt met de grootte van het geformatteerde bereik, heeft iets in het codepad de cellen gematerialiseerd — meestal een lus die elke cel in het bereik leest om zijn stijl te controleren, wat resolutie één cel tegelijk forcert en de hele arrangement verslaat

Los een stijl op wanneer je de effectieve opmaak van één cel nodig hebt. Los geen stijl op voor een miljoen cellen om vast te stellen dat de kolom een getalnotatie heeft; vraag de overlay. Dezelfde regel geldt voor schrijven: wijs waarden toe aan de cellen die waarden hebben, en laat opmaak een interval blijven

Waar het overige geheugen heen gaat

Eens cellen en stijlen compact zijn, zijn de volgende grootste verbruikers op een grote werkmap de gedeelde-tekenreekstabel en welke satelliet-parts het bestand meedraagt — pivot-caches, tekeningen, bewaarde XML van parts die het objectmodel niet modelleert. Die hebben hun eigen strategieën, en het eerlijke antwoord is dat geen enkele instelling ze allemaal tegelijk oplost

Als je flessenhals openen is in plaats van opslaan, is selectief laden de hefboom: de doorloop van alleen-metagegevens en selectief werkblad laden behandelt het lezen van een werkmap zonder te betalen voor de werkbladen die je niet zult aanraken. Voor read-path-doorvoer op zeer grote bestanden, zie de notities over parallel XLSX-parsen en de geheugenallocator, en voor alleen-schrijf-workloads die nooit een objectmodel nodig hebben, is streaming schrijven voor server-batchtaken meestal een betere pasvorm dan enige hoeveelheid tuning hier

HotXLS leest en schrijft XLS en XLSX vanuit native Delphi en C++Builder-code zonder Excel-installatie en zonder OLE-automatisering, wat is wat deze geheugenkarakteristieken observeerbaar en controleerbaar maakt in de eerste plaats — de HotXLS-spreadsheetcomponentpagina somt de ondersteunde formaten en RAD Studio-versies op