Технічна стаття

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

Що дає блок у 256 рядків

Локальність. Комірки тримаються в блоках по 256 рядків зі стабільними публічними дескрипторами, серіалізуються порядах, тож запис аркуша обходить пам'ять у порядку, в якому вона випускатиме байти, а не гоняє вказівники купою. Стабільні дескриптори важливі для поверхні API: дескриптор, який тримає викличний код, залишається чинним через внутрішню реорганізацію, яку виконує блочне компонування, що й робить компактне подання деталлю реалізації, а не breaking change

Компакція пулу стилів працює поруч. Перед кожним збереженням шрифти, заливки, рамки, числові формати, вирівнювання та захист, на які не посилається жодна комірка, відкидаються. Довголiving книги накопичують невикористані записи стилів так само, як довголiving документи накопичують невикористані стилі, і книга, яку користувач редагував годину, може нести сотні їх у файл, з якого ніхто ніколи не прочитає

Потокове збереження рядків і коли воно не застосовується

Із увімкненим StreamingWrite — за замовчуванням — кожен ряд аркуша пишеться прямо в потік deflate пакета. Альтернатива, яку вимикає цей прапорець, спочатку будує повний XML аркуша, а тоді стискає його, тож піке пам'яті масштабується цілим аркушем. Стрімінг змушує його масштабуватися одним рядом

Спільні рядки та допоміжні частини слідують тій самій дисципліні через один багаторазовий серіалізатор UTF-8, що випускає записи по одному, обмежуючи піке пам'яті найбільшим одним записом, а не цілою частиною. Це охоплює таблицю спільних рядків та записи зведених таблиць, що на широкій аналітичній книзі часто більші за будь-який окремий аркуш

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, а платити за це за замовчуванням — платити за випадок, у який більшість застосунків ніколи не потрапляє

Як визначити, чи накладання фактично використовуються

Стежте за кількістю комірок, а не за графіком пам'яті. Якщо аркуш звітує правдоподібну кількість фізичних комірок після того, як ви застосували широке форматування, накладання роблять свою справу. Якщо кількість стрибає на розмір форматованого діапазону, щось у шляхи коду матеріалізувало комірки — зазвичай цикл, що читає кожну комірку в діапазоні, щоб перевірити її стиль, що примусово вирішує по одній комірці та руйнує всю домовленість

Вирішуйте стиль, коли вам потрібен ефективний формат однієї комірки. Не вирішуйте стиль для мільйона комірок, щоб дізнатися, що в стовпця є числовий формат; спитайте накладання. Те саме правило стосується запису: призначайте значення коміркам, що мають значення, і дозвольте форматуванню залишатись інтервалом

Куди йде решта пам'яті

Щойно комірки та стилі стають компактними, наступними найбільшими споживачами у великій книзі є таблиця спільних рядків та будь-які сателітні частини, що несе файл — кеші зведених таблиць, графіки, збережений XML з частин, які об'єктна модель не моделює. Для них є власні стратегії, і чесна відповідь полягає в тому, що жодне налаштування не розв'язує всіх одразу

Якщо вузьке місце — відкриття, а не збереження, вибіркове завантаження — це важіль: нарис метадані-only та вибіркового завантаження аркушів описує читання книги без сплати за аркуші, яких ви не торкнетеся. Щодо пропускної здатності шляху читання на дуже великих файлах дивіться нотатки про паралельний розбір XLSX та алокатор пам'яті, а щодо навантажень лише на вивід, які взагалі не потребують об'єктної моделі, потокові записи для серверних пакетних завдань зазвичай підходять краще, ніж будь-яке тут налаштування

HotXLS читає та пише XLS і XLSX з рідного коду Delphi та C++Builder без встановлення Excel та без OLE-автоматизації, що й робить ці характеристики пам'яті спостережуваними та керованими з самого початку — сторінка компонента електронних таблиць HotXLS перераховує підтримувані формати та версії RAD Studio