Når en eksport på 300 000 rader sprenger minnebudsjettet sitt, får radantallet vanligvis skylden. Radantallet er som regel uskyldig. De kostbare delene av en stor arbeidsbok er de som skapes som en bivirkning: et stilbasseng som vokser med én oppføring per celle fordi formatering ble lagt til inne i løkken, regneark-XML satt sammen som én kjempestreng ved lagring, en million identiske formelkropper lagret én etter én. HotXLS, losLabs native Delphi-bibliotek for XLS- og XLSX-filer, gir deg en spesifikk spak for hver av disse kostnadene. Ingen av dem er aktivert som standard, fordi hver endrer en avveining, så å vite hvilken spak som matcher hvilket symptom, er den egentlige ytelsesferdigheten
Hvor en stor arbeidsbok bruker minne
Det finnes to atskilte minneregimer å resonnere om. Under generering vokser cellemodellen i minnet med hver celle du berører: verdier, formater og formler blir alle objekter eller bassengoppføringer. Under lagring gjengir XLSX-standardstien i tillegg hvert arks XML til en bred streng før den komprimerer den inn i zip-beholderen, så toppforbruket er modellen pluss det største arkets serialiserte form. En jobb som overlever byggeløkken og deretter dør inne i SaveAs, treffer det andre regimet, ikke det første, og løsningen for det ene gjør ingenting for det andre
Filstørrelse følger en beslektet regel: celler er bare én bidragsyter, ved siden av stiler, delte strenger, formler, bilder og kommentarer. En revisjonsrunde med ForEachCell og tellingene til samlingene per ark forteller deg hvilken ressurs som faktisk dominerer en problemfil før du optimaliserer feil en. Én måledetalj: Sheet.Cells.Count på XLSX-siden rapporterer antallet instansierte celler i det glisne lageret, ikke arealet til det brukte området. Et ark hvis data opptar et rektangel på 1000 ganger 50 med halvparten av cellene tomme, teller omtrent 25 000, ikke 50 000. Det skillet betyr noe når du sammenligner en kundes «enorme» fil mot fixturene dine, fordi arealet til det brukte området og den faktiske cellepopulasjonen kan avvike med en størrelsesorden i glisne finansielle oppsett
StreamingWrite fikser lagringsstien, ikke byggestien
Å sette TXLSXWorkbook.StreamingWrite := True bytter SaveAs til en strømmende serialisator som skriver arkets XML direkte inn i zip-strømmen, og eliminerer strengmellomleddet per ark. Den er False som standard for atferdskompatibilitet, og å skru den på er en énlinjes endring:
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Bulk');
for R := 1 to 100000 do
begin
Sheet.Cells[R, 1].Value := R;
Sheet.Cells[R, 2].Value := 'Row ' + IntToStr(R);
Sheet.Cells[R, 3].Value := R * 1.5;
end;
Book.StreamingWrite := True; // arkets XML strømmes inn i zip-beholderen
Book.SaveAs('bulk.xlsx');
finally
Book.Free;
end;
Vær presis om hva dette kjøper deg: cellemodellen bygget av løkken opptar nøyaktig like mye minne som før. StreamingWrite flater ut toppen ved lagringstidspunktet, som er forskjellen mellom en batchjobb som fullføres, og én som feiler ved 95 %-merket. Hvis selve byggeløkken tømmer minnet, er spakene du trenger, de neste to
Stilbassenger: legg til én gang, gjenbruk indeksen
XLSX-formatering i HotXLS er bassengbasert: Book.Fonts.Add(...), Fills.AddSolid(...) og Borders.Add(...) returnerer en 0-basert bassengindeks som celler refererer til. Å kalle Fonts.Add med identiske parametere inne i en løkke dedupliseres, så det sløser tid snarere enn plass. Alignments.Add oppfører seg annerledes: den returnerer et ferskt objekt per kall, så per-celle-justeringsoppretting vokser bassenget lineært med radantallet. Én vane dekker begge tilfellene. Løs opp hver bassengindeks én gang, utenfor løkken, og tildel indekser inne i den
// løft bassengoppslag ut av den varme løkken
HeaderFont := Book.Fonts.Add('Calibri', 11, True, False); // 0-basert bassengindeks
for C := 1 to 24 do
Sheet.Cells[1, C].FontIndex := HeaderFont + 1; // celler lagrer 1-basert; 0 = standard
+ 1 er ikke en skrivefeil, og å glemme den er den klassiske symptomgenererende feilen her: bassengene deler ut 0-baserte indekser, mens celleegenskapene behandler 0 som «standard», så hver bassengindeks må forskyves med én ved tildeling. Får du det feil ved utelatelse, gjengis overskriftene dine stille i arbeidsbokens standardfont, en defekt ingen legger merke til før merkevaregjennomgangen
Erstatt per-celle Variant-trafikk med radcallback-er
Hver Sheet.Cells[R, C].Value := X involverer et celleoppslag-eller-opprett pluss en Variant-tildeling. Ved noen hundre tusen celler blir den per-tilgang-overheaden målbar i profiler. HotXLS tilbyr bulk-callback-API-er på begge fasadene (ForEachCell og ForEachRow for lesing, WriteCells og WriteRows for skriving) som flytter iterasjonen inn i motoren og gir koden din hele rader om gangen:
procedure TLedgerExport.FillRow(Sender: TObject;
SheetIndex, Row, FirstCol, LastCol: Integer;
var Values: Variant; var Skip: Boolean; var Cancel: Boolean);
begin
if Row > FCount then
begin
Cancel := True; // stopp hele skrivingen
Exit;
end;
Values := VarArrayOf([FRows[Row - 1].Account,
FRows[Row - 1].PostedOn,
FRows[Row - 1].Amount]);
end;
// ett motorkall i stedet for hundretusenvis av egenskapstreff
Sheet.WriteRows(1, 1, FCount, 3, FillRow);
Callback-ens Skip-flagg lar en rad stå urørt uten å avbryte, og Cancel avslutter operasjonen tidlig, noe som er nyttig når kilden er en leser hvis lengde du oppdager underveis. Kombiner WriteRows for byggingen med StreamingWrite for lagringen, og genereringsstien har ikke noe gjenværende per-celle-hotspot
Lesesidespaker på XLS-fasaden
Store, gamle .xls-filer har sitt eget verktøysett. _DisableGraphics := True før Open hopper helt over parsing av tegnelaget, noe som fremskynder lasting av arbeidsbøker som bærer år med akkumulerte former og innebygde bilder. Begrensningen er hard: tegnelaget er da fraværende fra modellen, så å lagre en slik arbeidsbok skriver en fil uten tegningene sine. Reserver dette flagget for skrivebeskyttede analysejobber. SetTempDir omdirigerer BIFF-skriverens midlertidige filer, noe som betyr noe på servere der standard midlertidig plassering har en kvote eller ligger på treg lagring. UseSharedFormulas grupperer gjentatte formelkropper inn i delte formel-poster, og krymper filer der en formelkolonne gjentar seg nedover seksti tusen rader
Leseløkker over XLS-data har en indekseringsfelle verdt å flagge fordi den dobler arbeidet når den håndteres defensivt og ødelegger resultater når den overses: UsedRange rapporterer grensene sine FirstRow, LastRow, FirstCol og LastCol 0-basert, mens Cells.Item[Row, Col] er 1-basert. Et søk som går gjennom det brukte området, må legge til én til hver koordinat ved celletilgangen, som i Cells.Item[Row + 1, Col + 1], ellers leser det et rutenett forskjøvet diagonalt med én celle, som stille dropper den siste raden og kolonnen og inkluderer en fantom første. ForEachCell-callback-en unngår misforholdet helt, noe som er enda en grunn til å foretrekke den for hele-ark-søk
Sond filer før du laster dem
Den billigste store-arbeidsbok-operasjonen er den du unngår. GetSheetNames på begge fasadene lister opp en fils regneark uten å laste celledata. XLSX-implementasjonen leser bare arbeidsbokmanifestet inne i zip-en og lar eksplisitt arbeidsbokinstansen stå ufylt, og XLS-fasaden slutter å skanne ved den første understrømgrensen. Det gjør den til riktig forhåndssjekk for «hvilket ark denne importjobben bør sikte mot», og CanReadEncrypted svarer på «er dette en kryptert beholder» før et dømt Open-forsøk
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetSheetNames('big-unknown.xlsx', Names) <= 0 then
raise Exception.Create('cannot enumerate sheets'); // feil tømmer listen
// velg målarket, avgjør deretter om et fullt Open er verdt det
finally
Book.Free;
Names.Free;
end;
Legg merke til returkode-konvensjonen: disse sonderende funksjonene signaliserer feil med verdier ved eller under null og tømmer utdatalisten, så test <= 0 i stedet for å sammenligne mot én spesifikk suksessverdi
Å tilpasse tilnærmingen til jobben
For pipelines uten tilsyn som genererer mange store filer i sekvens, runder to vaner til av bildet. Arbeidsbokobjekter er ikke trådsikre for deling, men ingenting hindrer én uavhengig arbeidsbok per arbeidertråd, noe som parallelliserer batchkonvertering rent. Og når utdata går til HTTP i stedet for disk, kombineres TStream-lagringsoverbelastningene med StreamingWrite, slik at et stort svar aldri materialiserer seg som en midlertidig fil. Én driftsmessig fotnote gjelder: strømlagringen skriver fra gjeldende posisjon uten å spole tilbake, så sett Position := 0 før du gir strømmen til responsrammeverket. Artikkelen om strømmeskriving og batchjobber utvikler det server-sidemønsteret, og artikkelen om databaseeksport viser hvor disse spakene passer inn i en datasettdrevet rapport
Til slutt, behold én verste-tilfelle-fixture per rapportfamilie og ta tiden på den i CI. Ytelsesregresjoner i dokumentgenerering annonserer sjelden seg selv. En stil lagt til inne i en løkke eller en sonde erstattet med et fullt Open, endrer ingenting funksjonelt, og nattbatchen tar rett og slett førti minutter lenger. En tidtatt test på en representativ halv-million-celle-fixture gjør den avdriften om til en rød build i stedet for en driftshendelse
Evalueringsbygg, demoprosjekter med et bulk-genereringseksempel, og den fullstendige API-referansen er tilgjengelig på HotXLS Delphi Component-siden