Skip to content

A sheet repeats 26 style strings a quarter-million times, and the browser pays 10-20 KB a cell #822

Description

@andiwand

Measured while answering OpenDocument.droid#637 ("say when a spreadsheet was cut, and offer to
reload it with the cut relaxed"). The answer there turned out to be that the app cannot offer to
relax anything, because the browser cannot hold what the current limits already produce. The
cost is concentrated in one place, and it looks addressable here.

What a rendered cell costs a browser

Measured with the android app, one document per process, sampling every process it owns —
including com.google.android.webview:sandboxed_process0, which is where the page actually
lives. Two emulators: 2 GB / API 31 and 8 GB / API 36.

odr-public/ods/file_example_ODS_*.ods, the same 8-column sheet at three lengths, translated with
editable=false and the app's limits (spreadsheet_limit 100000x500, spreadsheet_cell_limit
500000, spreadsheet_limit_by_content):

sheet html renderer process, peak RSS
100 rows 0.2 MB 195 MB (this is the floor)
1000 rows 1.4 MB 342 MB
5000 rows 6.9 MB 949 MB (817 MB PSS by dumpsys meminfo, settling to 339 MB)

So roughly 10-20 KB of browser memory per rendered cell, against ~226 bytes of html per cell —
a multiplier of 40-90x on the bytes.

At the top of that curve, odr-public/ods/Supervised_Business_Register_300425.ods (4.5 MB,
one sheet of 66,523 x 21) renders 23,809 x 21 = 500,000 cells as 113 MB of html, and the renderer
process climbs to 4.7 GB laying it out — with the system UI going unresponsive on the way.
The same page cannot be displayed at all on the 2 GB device: the load dies mid-stream after ~80
seconds. Rendering the whole sheet (1.4 M cells, 316 MB of html) translates fine on the big device
— peak 1.33 GB, so Html::translate is in good shape after #776/#780 — but there is no browser
that can then show it.

Where the bytes go

The html of one non-empty cell, read-only:

<td style="vertical-align:top;">
  <x-p style="display:block;text-align:left;margin-left:0cm;font-family:Calibri;font-size:12pt;"
    ><x-s style="font-family:Calibri;font-size:12pt;color:#000000;">01/04/2022</x-s><wbr></x-p>
</td>

Over test/data/reference-output/.../Supervised_Business_Register_300425.ods/sheet0.html:

  • 259,957 style="…" attributes, 14.78 MB of the 26.66 MB file — 55%, and about 72% of what
    is left once that file's contenteditable/data-odr-path attributes (6.18 MB, absent in a
    read-only render) are discounted.
  • 26 distinct style strings in the whole sheet. Three of them account for 251,937 of the
    259,957 attributes: vertical-align:top;,
    display:block;text-align:left;margin-left:0cm;font-family:Calibri;font-size:12pt; and
    font-family:Calibri;font-size:12pt;color:#000000;.

A sheet is the case where a document's styles are most repetitive and most repeated, and inline
style is the one shape a browser cannot share: every attribute is parsed into its own declaration
block, and every element gets its own ComputedStyle.

What would help

  1. Emit repeated styles as classes rather than inline attributes, at least for sheets. 26
    classes in a <style> block would remove over half the html bytes and, more importantly, let
    the engine share style objects across cells instead of building a quarter-million of them.
  2. Do not spend three elements and a <wbr> on a cell holding one plain string. td > x-p > x-s > text is four nodes per cell before any content; a plain cell could be td > text with
    the paragraph's style on the td.

Either one moves the ceiling for every embedder that shows a sheet in a browser engine, which is
what the html limits are really budgeting for. #781 is the same shape one layer down (three
registry elements per non-empty cell before any html limit applies).

Numbers above are from emulators, so treat the absolute megabytes as indicative; the ratios
repeated across runs and devices, and the failure modes (lmkd kill, a load dying mid-stream, an
unresponsive system UI) were the real ones.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions