Techninis straipsnis

Dideles Excel darbaknyges nasumas Delphi su HotXLS

Kai 300 000 eiluciu eksportas virsija savo atminties biudzeta, paprastai kaltinamas eiluciu skaicius. Eiluciu skaicius paprastai yra nekaltas. Brangios dideliu darbaknyges dalys yra tos, kurios sukuriamos kaip salutinis poveikis: stiliu baseinas, augantis vienu irasu vienai lastelei, nes formatavimas buvo pridedamas cikle viduje, darbalakio XML surenkamas kaip vienas milziniskas eilutes eilejis issaugojimo metu, milijonai identisku formuliu turinu issaugomi po vieną. HotXLS, losLab gimtojo Delphi biblioteka XLS ir XLSX failams, suteikia jums konkretu svertu kiekvienai is siu islaidu. Neiena is ju nera ijungta pagal numatytaji, nes kiekviena keicia kompromisa, todel zinojimas, koks svertas atitinka kuri simptoma, yra tikrasis nasumas igudis.

Kur dideliu darbaknyge isleido atminti

Yra du atskiri atminties rezimai, apie kuriuos reikia mastyti. Generavimo metu atminties lasteles modelis auga su kiekviena lastele, kurios jusu lieciama: reiksmes, formatai ir formuliai tampa objektais arba baseino irasais. Issaugojimo metu numatytasis XLSX kelias papildomai pateikia kiekvieno darbalakio XML i plata eilute pries jis suspauzdamas i zip konteineri, todel didziu naudojima yra modelis plius didZiausio lapo serializuota forma. Uzduotis, kuri isgyvena kurybos cikla ir tada mirsra viduje SaveAs, yra pataikanti i antraji rezima, o ne pirmas, ir pataisymas vienam nieko nedaro kitam.

Failo dydis seka susijusia taisykle: lasteles yra tik vienas priezastinis, kartu su stiliais, bendrais eilutemis, formulemis, vaizdais ir komentarais. Audito praejimas su ForEachCell ir lapiniai rinkiniui skaiciai nurodo, koks steklis is tikruju dominuoja problematini faile pries optimizuoti neteisingaja. Vienas matavimo subtilumas: Sheet.Cells.Count XLSX puseje pranesha instancuotu lasteleiu skaiciu prietaisyne sandely, o ne naudojamo diapazono plota. Lapas, kurio duomenys uzsima 1000 x 50 staciakampi su puse lasteleiu tusciomis, skaicio apytiksliai 25 000, o ne 50 000. Tas skirtumas svarbus, kai lyginsite kliento "dideli" faila su savo fiksacijomis, nes naudojamo-diapazono plotas ir tikrasis lasteleiu populiacija gali skirtis eiliu tvarka retose finansinese isdestymuose.

StreamingWrite taiso issaugojimo kelia, o ne kurybos kelia

Nustatydami TXLSXWorkbook.StreamingWrite := True perjungia SaveAs i srautiniu serializatoriumi, kuris raso darbalakio XML tiesiai i zip srautu, pasalinant tarpine eilute kiekvienam lapui. Jis numatytas False elgesio suderinamumui, ir ijungimas yra vienos eilutes pakeitimas:

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;   // sheet XML streams into the zip container
  Book.SaveAs('bulk.xlsx');
finally
  Book.Free;
end;

Budi tikslius, ka tai suteikia: lasteleiu modelis, sukurtas ciklu, uzsima lygiai tiek atminties kaip ir anksciu. StreamingWrite sugludina issaugojimo laiko spike, kuris yra skirtumas tarp paketo darbas, kuris baigiasi, ir vienas kuris nepasiseka 95% zymeklio metu. Jei pats kurybos ciklas isnaudoja atminti, jums reikalingi du kiti svertai.

Stiliu baseinai: pridekite vieną karta, pakartotinai naudokite indeksa

XLSX formatavimas HotXLS yra baseinu pagrindu: Book.Fonts.Add(...), Fills.AddSolid(...) ir Borders.Add(...) grazina 0 pagrinsto baseinu indeksa, kuri lasteles nurodo. Kvieciamo Fonts.Add su identisku parametrais cikle deduplikuoja, todel tai svaisto laika, o ne erdves. Alignments.Add elgiasi skirtingai: jis grazina nauja objekta kiekvienam kvietimui, todel lasteles lygiavimosi kurimas auga bazeina liniisikas su eiluciu skaiciu. Vienas iprotis apima abu atvejus. Ispreskte kiekviena baseino indeksa vieną karta, uz ciklo, ir priskirkite indeksus jame viduje.

// hoist pool lookups out of the hot loop
HeaderFont := Book.Fonts.Add('Calibri', 11, True, False);   // 0-based pool index
for C := 1 to 24 do
  Sheet.Cells[1, C].FontIndex := HeaderFont + 1;            // cells store 1-based; 0 = default

+ 1 nera rasyba, o jos uzmirsimas yra klasikine simptomines klaidos cia: baseinai isteikia 0 pagrindstus indeksus, o lasteles puses savybes traktuoja 0 kaip "numatytasis", todel kiekvienas baseino indeksas turi buti pastu vienu priskyrimo metu. Klystate is neatsargumo ir jusu antrastites tykiai atvaizduojamos netinkamu sviriniu darbaknygiu, defektu, kurio niekas nepastebi iki prekinio zenklino apzvalga.

Pakeiskite lasteles-lygio Variant srautu eiluciu atsaukimais

Kiekvienas Sheet.Cells[R, C].Value := X apima lasteleiu paieska arba sukurima plius Variant priskyrima. Keletui simitums tukstanciu lasteleiu, tas prieigos islaidos tampa matomas profilyje. HotXLS teikia masinio atsaukimo API abiems fasaduose (ForEachCell ir ForEachRow skaitymui, WriteCells ir WriteRows rasymui), kurios perkelia iteracija variklio viduje ir perduoda jums visas eilutes vienu metu:

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;     // stop the whole write
    Exit;
  end;
  Values := VarArrayOf([FRows[Row - 1].Account,
                        FRows[Row - 1].PostedOn,
                        FRows[Row - 1].Amount]);
end;

// one engine call instead of hundreds of thousands of property hits
Sheet.WriteRows(1, 1, FCount, 3, FillRow);

Atsaukimo Skip pozymis palieka eilute nepaliesta nepertraukdamas, o Cancel anksciu baigia operacija, kas naudinga, kai saltinis yra skaitytuvas, kurio ilgi atrandate eidami. Suporuokite WriteRows kurybai su StreamingWrite issaugojimui ir kurybos kelias neturi likucio lasteles karstojo taskas.

Skaitymo puses svertai XLS fasade

Dideli senojo .xls failai turi savo priemoniu rinkinI. _DisableGraphics := True pries Open visai praleidzia braizinio sluoksnio apdorojima, kas pagreitina darbaknyges, nesiancio metu sukaupto formu ir ivdestu paveikslelu. Apribojimas yra grietas: braizinio sluoksnis tada nera modelio, todel tokios darbaknyges issaugojimas raso faila be jo piestuvu. Rezervuokite si poziomi tik skaitymo analizei. SetTempDir nukreipia BIFF rasytojo laikinos bylos, kas svarbu serveriuose, kur numatytoji temp vieta turi kvota arba yra letu saugykloje. UseSharedFormulas grupuoja pasikartojancius formuliu turinus i bendras formuliu irasus, sutrumpindamas failorius, kur formuliu stulpelis kartojasi siesdesimt tukstanciu eiluciu.

Skaitymo ciklai per XLS duomenis turi indeksavimo spastienas, verta pazymeti, nes tai dvigubina darba, kai tvarkomasi gynybiskai, ir sugadina rezultatus, kai praleistas: UsedRange pranesha savo FirstRow, LastRow, FirstCol ir LastCol ribas 0 pagrinstu, o Cells.Item[Row, Col] yra 1 pagrinsto. Skenavimas, einantis naudojama diapazonu, turi prie vienos koordinates prieigos, kaip Cells.Item[Row + 1, Col + 1], arba jis skaito tiklaraisti paslinkta is vienos lasteles istrizkaine, tykiai nustumiamas paskutine eilute ir stulpeli, ir iskaitant fantominI pirma. ForEachCell atsaukimas visai aplenkia neatitikima, kas yra dar viena prieastis jo teikti viso lapo skenavimui.

Patikrinkite failus pries juos ikraunant

Pigiausia dideliu darbaknyges operacija yra ta, kurios isvengate. GetSheetNames abiems fasaduose isvardija failo darbalapiuS neikraudamas lasteleiu duomenu. XLSX implementacija skaito tik darbaknyges manifesta zip archyve ir eksplicitiskai palieka darbaknyges instancija neuzpildyta, o XLS fasadas sustabdo skenavima pirmoje posrauties riboje. Tai padaro ji tinkama išbandymu tikrinimui "kuri lapu turi siekti sio importo darbas", o CanReadEncrypted atsako "ar tai uzkoduotas konteineris" pries beviltiska Open bandyma.

Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
  if Book.GetSheetNames('big-unknown.xlsx', Names) <= 0 then
    raise Exception.Create('cannot enumerate sheets');   // failure clears the list
  // pick the target sheet, then decide whether a full Open is worth it
finally
  Book.Free;
  Names.Free;
end;

Atkreipkite demesi i grazinimo kodo konvencija: sios zondavimo funkcijos signalizuoja nesekme su reiksmemis, lygiavertemis nuliui arba zemiau, ir issvalyto isvesties sarasa, todel bandykite <= 0, o ne lyginti su vienu konkreciu sekmes reiksmeniu.

Dydzo metodas uzduociai

Nepriezuriniams konvejeriams, kurie generuoja daug dideliu failu is eiles, dar du iprociai uzbaigti vaizda. Darbaknyges objektai nera gijoms saugus dalijimui, taciau niekas netrukdo naudoti viena nepriklausoma darbaknyge kiekvienai darbininko gijai, kas parallelizuoja paketiniu konversija svariai. Ir kai isvestis eina i HTTP, o ne disk, TStream issaugojimo perkrovimai derinami su StreamingWrite, todel didelis atsakas niekada nemateriuoja kaip laikinas failas. Viena operacine pastaba taikoma: srauto issaugojimas raso nuo dabartines pozicijos neapsisukes, todel nustatykite Position := 0 pries perduodami srautu atsakymo strukturai. Srauto rasytojo ir paketo darbų straipsnis pletoja ta serverio puses schemą, o duomenu bazes eksporto straipsnis parodo, kur sio svertai inka i duomenu-valdomu ataskaitą.

Galiausiai laikykite viena blogiausio atvejo fiksacija kiekvienai ataskaitos seimynai ir laiko ji CI. Nasumas regresijos dokumentu generavime retai paskelbj save. Stilius, pridedamas cikle, arba zondavimas pakeiciant visu Open niekuo funkcionalia, ir nakties paketas tiesiog uztrunka keturiasdesimt minuciu ilgiau. Laikytas bandymas reprezentatyviame puse milijono-lasteleiu fiksacijoje ta nunusinta pavercia raudonais kurybos, o ne operacinio incidento.

Vertinimo kurybos, demo projektai su masinio generavimo pavyzdziu ir visa API nuoroda yra prieinami HotXLS Component puslapyje.