Techninis straipsnis

HotXLS srauto rasymas Delphi serverio paketo uzduotims

Tarkime, nakties Delphi tarnyba generuoja viena XLSX kiekvienam klientui, keleta simitų failų, kai kurie is ju 400 000 eiluciu ploto. Profilizuokite ji ir nuostaba paprastai nera lasteleiu-pildymo ciklas. Tai SaveAs kvietimas. Su numatytuoju rasytoju kiekvienas darbalakis yra serializuotas i viena atminties XML eilute pries ta eilute suspaudžiant i OOXML zip, ir didelio lapo trumpoji eilute gali nustelb lasteleiu modeli, is kurio buvo sukurta. Tad uzduotis, kuri patogiai sukuria savo duomenis ir sedi prie 800 MB, bus sprogti uz 2 GB konteinerio riba issaugojimo metu, o OOM zaludikas pateikia klaidų ataskaitą 03:00, kai niekas nestebi. HotXLS, losLab gimtoji skaiciuokliu biblioteka Delphi ir C++Builder, turi savybe tiesiogiai skirta tam spyrciui: StreamingWrite. Aplink ji yra du papildomi svertai, nulemiiantys, ar paketo darbininkas laikosi savo atminties ir laiko biudzeto viduje: eiluciu-lygio rasymo atsaukimai ir stiliu baseino elgesys tamsiame cikle.

Ką numatytasis issaugojimo kelias buferizuoja ir ką StreamingWrite keicia

Numatytasis XLSX rasytojus teikia paprastumą. Jis pilnai atvaizduoja darbalakio XML, tada perduoda baigtą eilute zip kompresoriui. Tai yra teisingas kompromisas didžiajai daugumai darbaknygiui, kur viso lapo XML telpa keliame megabaitams. Ji nustoja buti teisinga, kai vieno lapo serializuota forma siekia simitų megabaitų. Skaiciuokliniu XML yra zodzinis: kiekviena skaitine lastele kainuoja desimt simboliu zymejimo, ir eilute, laikanti visus juos, turi buti nenutraukta. Atminties grafike parašas yra sunku praleisti. Ilgas plokscias plynaukste, kol eilutes pildos, tada aštrus trikampis spurtas issaugojimo metu, tada žlugimas, kai zip yra istrauktas.

Nustačius Book.StreamingWrite := True perjungia SaveAs i darbalakio rasytoja, kuris issireiškia lapo XML tiesiai i zip srautu generuodamas. Tarpine eilute niekada negaluojama, ir trikampis spurtas sugludinamas i triukšmą.

Budi tikslius apie tai, ką tai is tikruju perka, nes persipardavimas veda prie klaidingu pajegumų planu. Poziomis keicia tik issaugojimo kelia. Darbaknygės kurimas vis tiek alokuoja pilno atminties lasteleiu modeli, todel plynaukste pildymo etape yra tiksliai tokia pat alta kaip ir anksciu. Kas dingsta yra serializacijos spurtas, kuris buvo uzstatomas ant tos plynaukstės issaugojimo metu, ir uzduociai, pildanciai 400 tūkst. eiluciu, tas spurtas paprastai yra visas skirtumas tarp tilpti atminties biudzete ir ji išpusti. Savybe numatytai False, siekiant issaugoti istorini elgesi, todel prisijungimas yra viena ekspliciti eilute, kurią rasysite tycia.

Masinis eksportas su ijungtu poziomiu

Book := TXLSXWorkbook.Create;
try
  BoldIdx := Book.Fonts.Add('Calibri', 11, True, False); // pool index, 0-based
  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;
    if (R mod 1000) = 0 then
      Sheet.Cells[R, 2].FontIndex := BoldIdx + 1;        // 1-based at the cell
  end;
  Book.StreamingWrite := True;   // stream sheet XML straight into the zip
  Book.SaveAs('bulk.xlsx');
finally
  Book.Free;
end;

Cells[R, C] sukuria lasteles pagal poreiki, kas palieka ciklo kuna svarų. Dvi tinklelio apribojimai verti isiminimo: 1 048 576 eiluciu ir 16 384 stulpeliai, atskleisti kaip XlsxMaxRow ir XlsxMaxCol. Duomenu tiekimas, virsijantis eiluciu ribą, turi buti paskirstytas per lapus jusu kode. Niekas toliau pastebejimo virsijima arba ji itsaveisi, ir failas tiesiog baigiasi sutrumpintas riboje.

Eiluciu pildymas be lasteleiu-lygio Variant pridedamos islaidos

Kiekvienas Cells[R, C].Value priskyrimas moka lasteleiu paieskos ir Variant konversijos. Desimts tukstanciu eiluciu niekas nepastebi. Milijone eiluciu su dvidesimt stulpeliu kiekvienoje, tas kvietimo-lygio pridedamos islaidos tampa dominuojancio pildymo etapo kaina, ir profilis tiksliai nurodys ji. Paketiniu sasajos leidzia jums perduoti rasytojui visa eilute vienu metu. WriteRows vairuoja atsaukimą, kuris tiekia viena eilute per ivokimą:

procedure TBulkExporter.FillRow(Sender: TObject; SheetIndex, Row, FirstCol,
  LastCol: Integer; var Values: Variant; var Skip: Boolean;
  var Cancel: Boolean);
begin
  if not FReader.Next then
  begin
    Cancel := True;              // data source drained: stop cleanly
    Exit;
  end;
  Values := VarArrayCreate([FirstCol, LastCol], varVariant);
  Values[FirstCol]     := FReader.RecordId;
  Values[FirstCol + 1] := FReader.CustomerName;
  Values[FirstCol + 2] := FReader.Amount;
end;

// fill rows 2..100001, columns A..C, pulling from the reader
Sheet.WriteRows(2, 1, 100001, 3, FillRow);

Cancel poziomis yra tai, kas pavercia fiksuota eiluciu diapazona i "iki N eiluciu", kas yra natūrali forma, kai eiluciu skaicius ateina is uzklausos, kurios dar nebaigte vykdyti. Skip yra silpnesnis prisilietimas: jis palieka atskira eilute tuscia nepertraukdamas vykdymo. Be lasteleiu pildymo, atsaukimas pasirodo kaip gera namai operaciniams rupescciams, kurie kitaip butu prikabinti prie pildymo ciklo nepatogiomis budais. Progreso skaiciklis, besikeiciantis kas tukstantis eiluciu, atšaukimo zenklas, tikrinamas is uzduocių planuotuvo, norma ribotojo ant skaitymų is saltinio duomenu bazes: visa tai gyvena vienoje vietoje, o ne yra persriegtas per lasteleiu-rasymo koda. Skaitymo puseje ForEachRow ir ForEachCell atspindi ta pati modelį, kas svarbu, kai paketo uzduotis tiek naudoja, tiek sukuria didelius failus.

Stiliu baseinai atlygina iskylimą

XLSX stilizavimo modelis yra bendruju baseinu rinkinys. Fonts.Add, Fills.AddSolid ir Borders.Add visi grazina 0-pagrindstu baseino indeksa, ir lastele nurodo siriftą issaugodama ta indeksą plius vienas FontIndex, kur nulis yra rezervuotas darbaknyges numatytajam. +1 yra tiksliai ten masinio pavyzdyje auksciaus. Uzmirskte ji ir lastele tykiai paima klaidinga stiliu, nes perstumimas vienu bazeiną baseino indekse vis tiek yra galiojantis indeksas ir niekas neisskelia.

Disciplina, einanti is to, yra sukurti kiekvieną stiliaus objekta pries eiluciu ciklą ir nurodyti jo indeksą cikle. Fonts.Add deduplikuoja identiskas apibreztis, todel jos kvietimas kartą per eilutę tik svaisto CPU. Alignments.Add yra spastinas, nes jis grazina svieza irasa kiekviename kvietime. 100 tūkst. eiluciu cikle ta palaidia styles.xml su simtu tukstanciu pasikartojančiu lygiavimosi irasu, kas puchina faila diske ir letina kiekvieną velesnI atidaryma Excel, kai dublikatai yra vėl analizuojami. Sukurkite kiekvieną stiliu viena karta uz ciklo, tada nurodykite jo indeksą tiek kartų, kiek jums reikia.

Srautai, laikinieji katalogai ir paketo ciklas aplink visa tai

Neienas is to nereikalauja failu sistemos. Abu fasadai nesha TStream perkrovimus per jų IO pavyrsiu, Open ir SaveAs ir SaveAsCSV ir SaveAsHTML ir SaveAsODS tarp ju, todel paketo darbininkas gali atvaizduoti tiesiai i TMemoryStream, skirtą blob saugyklai arba HTTP atsakymui be kada nors paliesti disko. Yra vienas aštraus krasto prisiminti. SaveAs(Stream) raso is srauto dabartines pozicijos ir neperstraukia veliau, todel nustatykite Position := 0 pats pries perduodami srautu bet kas pristatyma, arba vartotojas skaito nulio baitu. XLS fasadas prideda du savo valdiklius. SetTempDir nukreipia BIFF rasytojo laikinuosius failus i toma, turintį erdvę ir IO galvos priesas juos absorbuoti, kas svarbu serveriuose, kur numatytasis temp kelias guli ant suspausto sistemos disko. UseSharedFormulas sulanksto pasikartojancius formuliu kūnus i bendras grupes, realus dydžio sumazinimas klasikinei ataskaitos formai, kur viena formule yra kopijuojama zemiaus viso stulpelio.

Paketo ciklas pats lieka nuobodus tycia:

for FileName in SourceFiles do
begin
  Book := TXLSXWorkbook.Create;        // fresh instance: no state bleed
  try
    Book.StreamingWrite := True;
    if Book.Open(FileName) <> 1 then
      Continue;                        // one bad input must not kill the batch
    Book.SaveAsCSV(ChangeFileExt(FileName, '.csv'), 0, ',');
  finally
    Book.Free;
  end;
end;

Svieza darbaknyges instancija vienam failui kainuoja mikrosekunde ir pasalina visa kryztinio-failo uzterstumo klaidu kategorija: stiliai, apibrezi pavadinimai ir dokumentu savybes is 17-o failo neturi kelio nuteketi i 18-a. Praleidimas-ir-tęsimas nepasisekusiam Open uzdirbamas lygiai tiek pat, nes vienas sutrumpintas ikeles 600-failu paketyje turetu jums kainuoti viena zurnalo eilutę, o ne likusio vykdymo. Taip pat verta pazymeti, ka CSV koja tycia nedaro. SaveAsCSV raso formules kaip literalinį teksta ir niekada jų neivykdo, todel konversijos paketas, kurio vartotojai tikisi apskaiciuotų skaičių, turi vykdyti Calculate atitinkamuose lastelese pirma, arba pradeti nuo darbaknygiU, kurios jau nesha isaugotus rezultatus is ankstesnio apskaiciavimo.

Lygiagretumo modelis: viena darbaknyge vienai gijai

Neieno fasado objektai nera gijoms saugūs, ir projektavimas niekada to neapsimetė. Nes tarp instanciju nera bendros globalios busenos, mastelio taisykle yra paprasciausias: viena darbaknyge vienam darbininko gijai, be bendrinimo darbaknyges tarp gijų. N darbininkų bazė, kiekvienas turintis savo TXLSXWorkbook, mastelio artimam linijiniam, kol atmintis tampa lubomis, ir tos lubos yra kazkas, kuri galite i skaiciaus: didžiausias vienalaikis lasteleiu modelis, padaugintas is darbininkų skaiciaus, plius bet koks issaugojimo-laiko pridedamos islaidos StreamingWrite sugludino. Kai eilė veikia gilu, taikykite atzviliu spaudimą uzduocių eileje, o ne rasytojo viduje. Alkaną gija, kuri per puse parase darbaknygę, nieko naudingo nepadarė, o uzduotis, kuri palaukė keletą sekundžių laisvo darbininko, baigiasi nepaliesta.

Platesniam derimo vaizdui, iskaitant bendras formules, grafikos praleidimą skaitymo puseje ir XLS-specifines svirtis, ziurekite dideles darbaknyges nasumas vadovą. Paketo uzduotys, kurių eilutes tiesiogiai ateina is uzklausos, aptariamos atskirai duomenu bazes eksporto modeliai Delphi ataskaitoms.

HotXLS kompiliuojasi i jusu Delphi arba C++Builder tarnybą kaip gimtasis Object Pascal be isoriniu priklausomybiu; versijos ir licenciavimas yra HotXLS Component produkto puslapyje.