Техническа статия

Съхранение на клетки в блокове от редове и струйно записване на XLSX в HotXLS

HotXLS съхранява клетките на работен лист в компактни блокове от 256 реда, разрешава форматирането на редове, колони и правоъгълници през мързеливи наслагвания на интервали, вместо да създава обекти клетки, и струйно вкарва всеки ред направо в deflate потока на пакета при запис. Заедно тези три промени решават профила на паметта за голяма работна книга: пиковата употреба следва най-големия единичен ред, а не размера на пълното работно лист XML

Причината това да има значение е форма, с която всеки разработчик на електронни таблици рано или късно се среща. Потребител форматира цяла колона — един клик, един милион клетки — и наивен обектен модел отговаря, като заделя милион обекта клетки, за да държи един индекс на числов формат. Файлът на диск остава малък, защото форматът XLSX изразява това като единичен запис <col>. Процесът изобщо не остава малък

Защо форматирането на колона струва повече памет от запълването и?

Защото форматирането няма данни, които да оправдаят обекта. Клетка със стойност трябва да съществува някъде. Клетка, която е празна, но стилизирана, съществува само за да носи индекс на стил, а материализирането на милиони такива е класическият начин, по който Delphi приложение за електронни таблици изчерпва адресно пространство върху файл, който Excel отваря мигновено

Наслагванията на интервален стил премахват нуждата. Инструкция за форматиране на ред, колона или правоъгълник се съхранява веднъж като диапазон плюс частите на стила, които тя допринася, и се разрешава мързеливо, когато клетка в този диапазон реално бъде достъпена. Наслагванията оцеляват структурни редакции — вмъкването на ред вътре в форматиран блок премества интервала, вместо да го прездравява — и се върнат като компактни записи за колона, ред и клетка само със стил, което е точно как 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 е множеството, което решава какво допринася едно наслагване: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment и xfpProtection. Назоваването само на частите, които имате предвид, е това, което позволява на наслагванията да се слояват разумно — наслагване на колона, което доставя числов формат, не се бори с наслагване на ред, което доставя запълване, защото нито едното претендира за частта на другото

Какво ви дава блокът от 256 реда

Локалност. Клетките се държат в блокове от 256 реда със стабилни публични манипулатори, сериализирани редово-мажоритарно, така че писането на работен лист върви в паметта в реда, в който ще излъчва байтове, вместо да преследва указатели из хийпа. Стабилните манипулатори имат значение за API повърхността: манипулатор, който извикващият държи, остава валиден през вътрешната реорганизация, която блоковото оформление извършва, което е точно това, което прави компактното представяне детайл на реализацията, а не счупваща промяна

Компактирането на пула от стилове тече успоредно с него. Преди всеки запис шрифтове, запълвания, рамки, числови формати, подравнявания и защити, които никоя клетка не реферира, биват отхвърляни. Работни книги с дълъг живот натрупват нередерирани записи на стилове по начина, по който дългоживеещи документи натрупват неизползвани стилове, а работна книга, която е била редактирана от потребител час, може да носи стотици от тях във файл, от който никой никога няма да ги прочете

Записване по редове и кога не се прилага

С StreamingWrite включено — по подразбиране — всеки ред на работен лист се записва директно в deflate потока на пакета. Алтернативата, която е това, което флагът изключва, изгражда пълното работно лист XML първо и го компресира след това, така че пиковата памет мащабира с целия лист. Струйното прави я мащабира с един ред

Споделените низове и помощните части следват същата дисциплина чрез един преизползваем UTF-8 сериализатор, който излъчва записи един по един, ограничавайки пиковата памет по най-големия единичен запис, а не по цялата част. Това покрива таблицата със споделени низове и pivot записи, които при широка аналитична работна книга често са по-големи от всеки отделен работен лист

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;

Оставете го включен, освен ако нямате конкретна причина да не го направите. Неструйният път съществува за случаите, когато нещо друго в пайплайна се нуждае от сглобеното XML, а заплащането за него по подразбиране е плащане за случай, който повечето приложения никога не удрят

Как да разберете дали наслагванията реално се използват

Следете броя клетки, а не графиката на паметта. Ако работен лист отчита правдоподобен брой физически клетки, след като сте приложили широко форматиране, наслагванията вършат работата си. Ако броят скача с размера на форматирания диапазон, нещо в кодовия път е материализирало клетките — обикновено цикъл, който чете всяка клетка в диапазона, за да провери стила си, което принуждава разрешаване една клетка по едно време и проваля цялата подредба

Разрешете стил, когато се нуждаете от ефективен формат на една клетка. Не разрешавайте стил за милион клетки, за да разберете, че колоната има числов формат; попитайте наслагването. Същото правило се прилага за писане: присвоявайте стойности на клетките, които имат стойности, и оставете форматирането да остане интервал

Къде отива останалата памет

След като клетките и стиловете са компактни, следващите най-големи консуматори в голяма работна книга са таблицата със споделени низове и каквито сателитни части файлът носи — pivot кешове, чертежи, запазено XML от части, които обектният модел не моделира. Тези имат свои собствени стратегии, а честният отговор е, че нито една настройка решава всички наведнъж

Ако вашето тясно място е отварянето, а не записът, селективното зареждане е лостът: ръководството за зареждане само на метаданни и селективно на работни листове разглежда четенето на работна книга, без да плащате за листовете, които няма да докоснете. За производителност на пътя на четене при много големи файлове вижте записките за паралелен XLSX анализ и алокаторът на памет, а за изходни натоварвания, които изобщо не се нуждаят от обектен модел, струйно записване за партидни задачи на сървър обикновено пасва по-добре от какъвто и да е тунинг тук

HotXLS чете и записва XLS и XLSX от нативен Delphi и C++Builder код без инсталиран Excel и без OLE автоматизация, което е точно това, което прави тези характеристики на паметта наблюдаеми и контролируеми на първо място — страницата на HotXLS компонент за електронни таблици изброява поддържаните формати и версии на RAD Studio