diff --git a/docs/_ext/bench_table.py b/docs/_ext/bench_table.py index d9e02d88..d98ddcf3 100644 --- a/docs/_ext/bench_table.py +++ b/docs/_ext/bench_table.py @@ -26,6 +26,7 @@ import json import math +from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING, Any, ClassVar, Final @@ -260,12 +261,53 @@ def _mark_entry(ordinal: int) -> nodes.entry: return entry +def _table_frame(ncols: int) -> tuple[nodes.table, nodes.tgroup]: + """Build the table and its column group: a wide first column for the row label, then one per metric column.""" + table = nodes.table(classes=["bench-table"]) + group = nodes.tgroup(cols=ncols) + table += group + group += nodes.colspec(colwidth=2) + for _ in range(ncols - 1): + group += nodes.colspec(colwidth=1) + return table, group + + +@dataclass(frozen=True) +class _Layout: + """What every row of one table shares: its columns, its metrics, and the numbered reasons its cells key into.""" + + parties: list[str] + metrics: list[str] + reasons: dict[str, int] + + +def _row_caveats(feed: dict[str, Any], taken: int) -> tuple[dict[str, int], dict[int, int]]: + """Assign each distinct per-row caveat an ordinal after those already taken, and map each row to its ordinal.""" + notes: dict[str, int] = {} + marks: dict[int, int] = {} + for index, note in sorted((int(key), value) for key, value in (feed.get("row_notes") or {}).items()): + notes.setdefault(note, taken + len(notes) + 1) + marks[index] = notes[note] + return notes, marks + + def _ordered_notes(feed: dict[str, Any], parties: list[str]) -> list[str]: """Return the noted parties in the order their columns appear, so ordinals read left to right.""" notes = feed.get("notes") or {} return [party for party in parties if party in notes] +def _merge_legend( + reasons: dict[str, int], notes: dict[str, int], row_notes: dict[str, int], feed: dict[str, Any] +) -> dict[str, int]: + """Gather the empty-cell reasons, the column notes and the row caveats into one numbered legend.""" + legend = dict(reasons) + for party, ordinal in notes.items(): + legend[f"{party} {feed['notes'][party]}"] = ordinal + legend.update(row_notes) + return legend + + def _legend(reasons: dict[str, int]) -> nodes.container: """Spell each numbered reason out under the table; reason text is literal so a message cannot inject markup.""" legend = nodes.container(classes=["bench-legend"]) @@ -291,30 +333,34 @@ def run(self) -> list[nodes.Node]: metrics = feed["metrics"] or ["time"] parties, rows, spread = _order_columns(feed["parties"], metrics, feed["rows"], feed.get("spread") or []) ncols = 1 + len(parties) * len(metrics) - table = nodes.table(classes=["bench-table"]) - group = nodes.tgroup(cols=ncols) - table += group - group += nodes.colspec(colwidth=2) - for _ in range(ncols - 1): - group += nodes.colspec(colwidth=1) - reasons = _reason_map(rows) + table, group = _table_frame(ncols) + layout = _Layout(parties, metrics, _reason_map(rows)) # notes number on from the empty-cell reasons so one legend carries both without colliding ordinals - notes = {party: len(reasons) + offset for offset, party in enumerate(_ordered_notes(feed, parties), start=1)} + notes = { + party: len(layout.reasons) + offset for offset, party in enumerate(_ordered_notes(feed, parties), start=1) + } + row_notes, row_marks = _row_caveats(feed, len(layout.reasons) + len(notes)) group += self._head(feed["label"], parties, feed["metrics"], notes) + group += self._body(rows, spread, layout, row_marks, ncols) + legend = _merge_legend(layout.reasons, notes, row_notes, feed) + return [table] if not legend else [table, _legend(legend)] + + def _body( + self, + rows: list[list[Any]], + spread: list[Any], + layout: _Layout, + row_marks: dict[int, int], + ncols: int, + ) -> nodes.tbody: body = nodes.tbody() - group += body - for row_index, cells in enumerate(rows): + for index, cells in enumerate(rows): if len(cells) != ncols: msg = f"bench-table row has {len(cells)} cells, expected {ncols}: {cells!r}" raise self.error(msg) - noise = spread[row_index] if row_index < len(spread) else None - body += self._body_row(cells, parties, metrics, reasons, noise) - legend = dict(reasons) - for party, ordinal in notes.items(): - legend[f"{party} {feed['notes'][party]}"] = ordinal - if not legend: - return [table] - return [table, _legend(legend)] + noise = spread[index] if index < len(spread) else None + body += self._body_row(cells, layout, noise, row_marks.get(index)) + return body def _head(self, label: str, parties: list[str], metrics: list[str], notes: dict[str, int]) -> nodes.thead: head = nodes.thead() @@ -352,15 +398,17 @@ def _head(self, label: str, parties: list[str], metrics: list[str], notes: dict[ return head def _body_row( - self, - cells: list[Any], - parties: list[str], - metrics: list[str], - reasons: dict[str, int], - spread: list[Any] | None = None, + self, cells: list[Any], layout: _Layout, spread: list[Any] | None = None, caveat: int | None = None ) -> nodes.row: + parties, metrics, reasons = layout.parties, layout.metrics, layout.reasons row = nodes.row() - row += self._entry(str(cells[0])) + label = self._entry(str(cells[0])) + # a caveat that belongs to one operation marks that row, not the library's whole column + if caveat is not None: + superscript = nodes.superscript() + superscript += nodes.Text(str(caveat)) + label.children[0] += superscript + row += label rendered: dict[int, tuple[str, str | None]] = {} marks: dict[int, int] = {} for metric_index, metric in enumerate(metrics): diff --git a/docs/changelog/692.feature.rst b/docs/changelog/692.feature.rst new file mode 100644 index 00000000..674c8e45 --- /dev/null +++ b/docs/changelog/692.feature.rst @@ -0,0 +1,20 @@ +Move the read path's per-item loops into C. :func:`turbohtml.saxparse.sax_parse` built a tuple, a typed record, and an +isinstance chain for every event; the tokenizer now binds the six handler methods once and drives them off the tree +walk, for 43.7% fewer instructions with instruction-cache misses down 71% on the WHATWG specification. URL cleaning +(:func:`turbohtml.extract.clean_url`, :func:`~turbohtml.extract.normalize_url`, +:func:`~turbohtml.extract.extract_links`), the :func:`~turbohtml.extract.dates` ```` stage, +:func:`turbohtml.query.escape_identifier`, and :func:`turbohtml.clean.linkify`'s scheme classification each moved their +loops to C as well, for 3.5% to 56.8% fewer instructions on those operations. Differential runs against the retired +Python across 1,484,640, 809,481, 360,282, 302,762, 38,590, and 4,828 inputs found one divergence: the URL resolver +matches ``%2e`` in either case as the `WHATWG URL path state `_ specifies, +which no public call reaches because percent-encoding uppercases escape hex first. +:func:`~turbohtml.saxparse.iter_events` keeps its typed records, since those are its public product. + +Other hot paths keep their Python entry and shed work. XSD validation resolves each schema element's qname once at +compile time into a binary-searched table for 17.2% fewer instructions, built for XSD only after the eager cache slowed +RELAX NG compilation by 8.15%. The link walk mints element wrappers on first use (14.8% fewer) and skips the URL join +that already returns an absolute reference (67.8% fewer); the ```` encoding prescan jumps inert bytes with +``memchr`` (8.67% fewer on ASCII); the gb18030 decoder maps astral pointers by arithmetic (13.4% fewer); and CSS +property dispatch gates on the first byte (9.3% fewer on ``bootstrap.css``). The migration and benchmark tables also +stopped over-claiming: a caveat marks only the rows of the operation it describes, a shared measurement shows in both +columns, XPath labels render as inline code, and the CSS-stripper note gives the measured 1-3% size difference. diff --git a/docs/development/bench/css-minification.json b/docs/development/bench/css-minification.json index 4c2ea699..f7574ad6 100644 --- a/docs/development/bench/css-minification.json +++ b/docs/development/bench/css-minification.json @@ -18,237 +18,237 @@ [ "normalize.css (6 kB)", 1750.0, - 2.4677963665453717e-05, + 3.2464727574677e-05, 1834.0, - 0.0006309785577135093, + 0.0009276293920568909, 1842.0, - 0.00148522892307786, + 0.0015539148210640026, 1847.0, - 0.0005264332545114788, + 0.0005475900335341066, 1816.0, - 0.008701107213103873, + 0.008969288615238232, 1751.0, - 6.477255408772938e-05, + 7.805699825288077e-05, 1775.0, - 6.877973989460884e-06, + 7.227439025925264e-06, 1809.0, - 0.006585243500618769 + 0.006976464842712933 ], [ "pico.css (90 kB)", 81155.0, - 0.0008415556360432674, + 0.0010817124017800477, 81883.0, - 0.29427182641423616, + 0.310369604092557, 81616.0, - 0.04783730468867967, + 0.04725994185234109, 81792.0, - 0.29104015965519164, + 0.290788572980091, 82466.0, - 0.01180374044815835, + 0.015580497833677024, 79992.0, - 0.002079070472670234, + 0.0022688593744533136, 82078.0, - 0.00026292298050141955, + 0.0002621144950959812, 82027.0, - 0.008949477426237232 + 0.008984445535437166 ], [ "animate.css (93 kB)", 73788.0, - 0.0009074143608813756, + 0.0012892222758485634, 75811.0, - 0.013143970048986375, + 0.01716736372933762, 75738.0, - 0.03357629689950651, + 0.034281230024741184, 75797.0, - 0.00972714366192425, + 0.009972065103890296, 73454.0, - 0.012262740454389132, + 0.01623697133497141, 68773.0, - 0.0016668788516653876, + 0.0016953111445824713, 75682.0, - 0.0002218701103515741, + 0.00022064549447501727, 74366.0, - 0.009337699213574524 + 0.00886903776518011 ], [ "foundation.css (164 kB)", 132898.0, - 0.0020137969298351286, + 0.0028156564673433118, 136503.0, - 0.6699292880948633, + 0.6890333229966927, 136405.0, - 0.08025021179734419, + 0.08035445658606477, 136345.0, - 0.6636087951774243, + 0.6649740381690208, 131798.0, - 0.015982563353948837, + 0.018953305122219415, "Parsing stylesheet failed: Invalid media query at :577:29", "Parsing stylesheet failed: Invalid media query at :577:29", 136682.0, - 0.0004910595569450985, + 0.00048533412052620406, 136011.0, - 0.012145074868991893 + 0.010203022582572885 ], [ "bootstrap.css (274 kB)", 229554.0, - 0.002807286729421321, + 0.003730066627516256, 234299.0, - 0.801409038171793, + 1.2760612014099024, 234206.0, - 0.10941284016977686, + 0.10736939241178334, 232361.0, - 0.775202558899764, + 0.8377778609962357, 233022.0, - 0.019398659741758213, + 0.023000642783396568, 228699.0, - 0.006114484483987326, + 0.006715550674622743, 233172.0, - 0.000885669812305423, + 0.0008058121210675987, 231850.0, - 0.01339823480520863 + 0.012535955936982646 ], [ "bulma.css (745 kB)", 681425.0, - 0.0070988878069329076, + 0.008693945529860988, 681335.0, - 3.7613919480742575, + 4.194504024237783, 681328.0, - 0.77290790976258, + 0.7723801529306608, 679329.0, - 3.675083399498059, + 3.896613041588959, 689213.0, - 0.03429648437304422, + 0.042386440123664215, 674333.0, - 0.015533043417235604, + 0.01717652689573394, 679974.0, - 0.0022972218422789106, + 0.0023041306975149687, 685284.0, - 0.020232135414213797 + 0.018881723948046176 ] ], "spread": [ [ null, null, - 0.021180385365301226, + 0.09728764011828976, null, - 0.02819642344696989, + 0.09710277770104533, null, - 0.05701234593429704, + 0.019826228382515288, null, - 0.01791848500486394, + 0.03951865967357222, null, - 0.06992896493221358, + 0.09593713934997752, null, - 0.02905783106689442, + 0.12029726598732139, null, - 0.026855005115815223, + 0.07105855210195146, null, - 0.07336334626591483 + 0.056758389135272015 ], [ null, null, - 0.05709012427212532, + 0.10598940421080025, null, - 0.01741493259203369, + 0.06063725346644621, null, - 0.026944394476272976, + 0.04641130544766816, null, - 0.01774728348797553, + 0.016772891775886107, null, - 0.04412680403612263, + 0.0703349525103437, null, - 0.03479701834134207, + 0.11557548323441176, null, - 0.018144959164507132, + 0.022144839124433217, null, - 0.13643615670380904 + 0.03821462337771131 ], [ null, null, - 0.029687965981479846, + 0.07158741875788914, null, - 0.020649446079126006, + 0.1449496297491353, null, - 0.019622682835288865, + 0.02695020928613877, null, - 0.021456622114735088, + 0.04642749177523619, null, - 0.027843021875434023, + 0.0740337837276571, null, - 0.025156253322567327, + 0.04477117584138788, null, - 0.025806845986799082, + 0.018206653247167335, null, - 0.1657667850197228 + 0.05216206832373801 ], [ null, null, - 0.06837573110647958, + 0.07838358803696824, null, - 0.009653882996928344, + 0.03607736265830405, null, - 0.02003352814730991, + 0.03547418507801415, null, - 0.010765088339266779, + 0.029390684090604573, null, - 0.029451061695924832, + 0.10257546470144385, null, null, null, - 0.02766298223470719, + 0.021725472885592095, null, - 0.24559582538980165 + 0.040623528790115465 ], [ null, null, - 0.023601218123909005, + 0.10694045427657661, null, - 0.032878105258768754, + 0.40929399018038914, null, - 0.033278616382448625, + 0.07345581630301083, null, - 0.008096442581343071, + 0.09389019306439964, null, - 0.02563378436764528, + 0.059898574018461964, null, - 0.03270786379047158, + 0.10864175208310488, null, - 0.1172214619078148, + 0.06589567367731815, null, - 0.060726743453138214 + 0.03603544359459969 ], [ null, null, - 0.02956177866788225, + 0.12618949595325846, null, - 0.016781771728862072, + 0.12147845408308942, null, - 0.009543429204608288, + 0.010728740875689466, null, - 0.008905647770977644, + 0.04641827022426012, null, - 0.05813764649113857, + 0.20987532850151677, null, - 0.022578577927933375, + 0.11449298554009829, null, - 0.029345195540840696, + 0.03676125016636636, null, - 0.08500393756077515 + 0.024791779131297275 ] ], "notes": { - "css-html-js-minify": "strips whitespace and comments only, leaving #ffffff, 0px 0px 0px 0px and font-weight:bold unshortened where turbohtml writes #fff, 0 and 700", - "cssmin": "strips whitespace and comments only, leaving #ffffff, 0px 0px 0px 0px and font-weight:bold unshortened where turbohtml writes #fff, 0 and 700", - "rcssmin": "strips whitespace and comments only, leaving #ffffff, 0px 0px 0px 0px and font-weight:bold unshortened where turbohtml writes #fff, 0 and 700" + "css-html-js-minify": "strips whitespace and comments without parsing, so it applies none of the color, number, and shorthand rewrites turbohtml does; on these stylesheets that leaves its output within 1.01-1.03x of turbohtml's, and 0.998x on bulma, so the structural shortenings change little on already-tight framework CSS", + "cssmin": "strips whitespace and comments without parsing, so it applies none of the color, number, and shorthand rewrites turbohtml does; on these stylesheets that leaves its output within 1.01-1.03x of turbohtml's, and 0.998x on bulma, so the structural shortenings change little on already-tight framework CSS", + "rcssmin": "strips whitespace and comments without parsing, so it applies none of the color, number, and shorthand rewrites turbohtml does; on these stylesheets that leaves its output within 1.01-1.03x of turbohtml's, and 0.998x on bulma, so the structural shortenings change little on already-tight framework CSS" } } diff --git a/docs/development/bench/date-extraction.json b/docs/development/bench/date-extraction.json index 5a95a0b0..5162baaf 100644 --- a/docs/development/bench/date-extraction.json +++ b/docs/development/bench/date-extraction.json @@ -12,62 +12,63 @@ "rows": [ [ "post (4 KiB)", - 1.1735952601436376e-05, - 0.0032615539830658236, - 2.6615594620693628e-05, - 0.008319053811646882, - 0.003010062393210925, - 0.00017194148086711417 + 5.9304065735214335e-06, + 0.0025007032345456537, + 1.688457954903318e-05, + 0.005928435221297453, + 0.002119573241998296, + 0.00011766121753945906 ], [ "longform (16 KiB)", - 1.7074957939418784e-05, - 0.011720733735273825, - 4.910956235685641e-05, - 0.0226524487795056, - 0.008552690978831379, - 0.0003579496258036367 + 1.030123477659591e-05, + 0.008274378248340023, + 3.404049680473994e-05, + 0.01590911588452097, + 0.00614426996041099, + 0.0002502549162007502 ], [ "100 meta candidates", - 0.00013010201349554032, - 0.005897449650850224, - 7.602037720270498e-05, - 0.01212375239507916, - 0.003707961801713585, - 9.28465288912624e-05 + 1.828077740384515e-05, + 0.004187224176651701, + 5.459096957357209e-05, + 0.00877185786581928, + 0.002589482204257365, + 7.32439337885656e-05 ] ], "spread": [ [ null, - 0.04329384536759802, - 0.04084589772460682, - 0.033263737396463294, - 0.058179691141717345, - 0.023286550889136903, - 0.08089387135645254 + 0.004697496985019512, + 0.15902130549302607, + 0.009112355406770883, + 0.03489928844853301, + 0.025555266163807724, + 0.019549316289211804 ], [ null, - 0.03092662857915166, - 0.03199195905089177, - 0.054986089231601934, - 0.022472019979233922, - 0.028635270494106826, - 0.03784222870872126 + 0.007721833090606347, + 0.008266166525272227, + 0.016598368815392547, + 0.020043951211199545, + 0.02156623309830427, + 0.023385975296491182 ], [ null, - 0.03639475424630214, - 0.07349111320004241, - 0.0308781661300203, - 0.04241792606111163, - 0.06898135336090556, - 0.035977232455401555 + 0.008026401759229225, + 0.017919851624633977, + 0.021206735172214816, + 0.04277533338146369, + 0.013182086495236387, + 0.058992245979526695 ] ], "notes": { - "htmldate": "returns no date for the 100-candidate case, so its timing there is the cost of giving up rather than of finding the date turbohtml reports" + "htmldate": "returns no date for the 100-candidate case, so its timing there is the cost of giving up rather than of finding the date turbohtml reports", + "trafilatura": "returns no date for the 100-candidate case, so its timing there is the cost of giving up rather than of finding the date turbohtml reports" } } diff --git a/docs/development/bench/encoding-detection.json b/docs/development/bench/encoding-detection.json index b57cadc7..e4a0139c 100644 --- a/docs/development/bench/encoding-detection.json +++ b/docs/development/bench/encoding-detection.json @@ -12,113 +12,113 @@ "rows": [ [ "ascii (1 kB)", - 2.683881651736423e-06, - 0.00016621675959527238, - 1.235750171598345e-06, - 0.00016340677547077576, - 6.459631354971407e-05, - 1.379581478166969e-06 + 1.1417041620257844e-06, + 0.0001670253838407613, + 1.1773475279142076e-06, + 0.00014564843067432776, + 4.932761466847069e-05, + 1.029075331541544e-06 ], [ "utf-8 russian (4 kB)", - 8.42264663110844e-06, - 0.0002101907146917862, - 5.730095120763205e-06, - 0.00020473426656053562, - 0.00045748297744315397, - 4.134158238855434e-06 + 5.395650118404187e-06, + 0.00019062113415202475, + 0.00018844744206110894, + 0.00016079087338501571, + 0.00034693029781844115, + 3.0797061919921966e-06 ], [ "windows-1251 russian (4 kB)", - 0.00023194885286178155, - 0.0007660471198960295, - 0.00040475461815958624, - 0.0007493408056310121, - 0.0004235422777204197, - 0.0004300928825008062 + 0.00017615521230140985, + 0.0006824471162190093, + 0.0010732773977603454, + 0.0006862004258512874, + 0.00030178521061922464, + 0.00035062181920390384 ], [ "windows-1252 french (4 kB)", - 0.00022984596078382916, - 0.0009623634165715581, - 0.000478496310885627, - 0.0009485056960632695, - 0.0014133275006618835, - 0.0004874247093008914 + 0.00017919206403386548, + 0.0009608074811543096, + 0.0013673634986541099, + 0.00096642504756043, + 0.001020589817774938, + 0.0003832794663291376 ], [ "shift_jis japanese (4 kB)", - 0.00010243962940137408, - 0.0007823282336403281, - 4.349980590963772e-05, - 0.0007782307262308071, - 0.00048443899155851494, - 4.1679475524839894e-05 + 8.727693684799458e-05, + 0.0011041370983093657, + 5.4442557456013674e-05, + 0.0007336918404992806, + 0.00034780594885811905, + 3.797536552241354e-05 ], [ "utf-8 page (95 kB)", - 1.0262474480586075e-06, - 0.0017221563046708372, - 6.99966448204729e-05, - 0.0017224917549659342, - 0.0007871857096309517, - 7.612719218741404e-05 + 9.080427233835545e-07, + 0.0017875253332325276, + 0.029714249164195888, + 0.0013319131658893941, + 0.000590463311179216, + 6.985357494215805e-05 ] ], "spread": [ [ null, - 0.017880736659094357, - 0.018251613038969026, - 0.009398139741706679, - 0.010724686631088845, - 0.0447636886199731, - 0.01669212179717228 + 0.009158479913916974, + 0.09234027113797867, + 0.10468818317444338, + 0.13673752459469665, + 0.04216924148520772, + 0.007167867015191341 ], [ null, - 0.013928849417596853, - 0.04709186896538359, - 0.024881909180794498, - 0.02288405959591961, - 0.08554927173830451, - 0.02525687093898264 + 0.0038712062652642206, + 0.0877226744657706, + 0.18627333668661952, + 0.04049806799196369, + 0.015485661727082682, + 0.0062008850358656635 ], [ null, - 0.030374690128129544, - 0.05534154336710747, - 0.03358285021685917, - 0.027327653967104393, - 0.0365463065399403, - 0.049845411634993224 + 0.004080075123940598, + 0.09585776641164494, + 0.02315224237339274, + 0.12213514583063445, + 0.016158813820105353, + 0.021001531511425205 ], [ null, - 0.024415342494636606, - 0.01597170853773811, - 0.026737823847515163, - 0.03283025755818031, - 0.02930554925922803, - 0.06204167242122492 + 0.059916699660372115, + 0.1426199999041337, + 0.04027212843423796, + 0.10209968241403904, + 0.015039601460871254, + 0.01910706894477701 ], [ null, - 0.011882884549205048, - 0.07681575774619458, - 0.022649954602732364, - 0.03392169078973562, - 0.037535858472558255, - 0.017271426636053563 + 0.09053675269683122, + 0.2738526726895093, + 0.0761583266038194, + 0.4077870034036728, + 0.020993992514227677, + 0.21023172288412514 ], [ null, - 0.027875914121933836, - 0.02169554352319668, - 0.016122380494513, - 0.02346546131727304, - 0.01868390630267981, - 0.016350123964366924 + 0.09984062000003383, + 0.13921153915640078, + 0.06216476696663551, + 0.010247606289130554, + 0.013470151385193491, + 0.12703643729605826 ] ], "notes": {} diff --git a/docs/development/bench/link-filtering.json b/docs/development/bench/link-filtering.json index b53c71ff..e24c0b77 100644 --- a/docs/development/bench/link-filtering.json +++ b/docs/development/bench/link-filtering.json @@ -14,93 +14,93 @@ "rows": [ [ "daring fireball (10 kB)", - 0.00019244289493750935, - 0.0017440380336969004, - 0.001393361708323937, - 0.0006250226910348525, - 0.00019721312124450682, - 0.000488335463084392, - 0.0005061203476846762, - 0.00038065648076705355 + 0.00014374658196440274, + 0.0017214570859020266, + 0.0012449160982062797, + 0.0005456723903686603, + 0.0001782515766611444, + 0.00031386787206126127, + 0.000519150458671902, + 0.00022196778021073746 ], [ "ars technica (56 kB)", - 0.00044414784745564856, - 0.007564480031457303, - 0.0060377776668853285, - 0.001392310166693278, - 0.0007186282415053332, - 0.001623948243529109, - 0.001544980251992456, - 0.0014216306973745911 + 0.0003625916885994229, + 0.007038899739200133, + 0.005221934676834887, + 0.0011215508353264643, + 0.0006590101314714047, + 0.0009077950169663987, + 0.0013566567936322826, + 0.0007716973326902613 ], [ "mozilla blog (95 kB)", - 0.0007429536545942028, - 0.016098750425347436, - 0.013269332885101903, - 0.0016785657044238178, - 0.0013903054759794031, - 0.001837482583293119, - 0.0025982647539422032, - 0.0030353482511600305 + 0.0006187186553461288, + 0.01468123565427959, + 0.01133143881209738, + 0.0014856392695037357, + 0.0012879373646986398, + 0.0015391715760415536, + 0.002153627226713676, + 0.002120180878894947 ], [ "whatwg spec (235 kB)", - 0.0011912915304037597, - 0.04389768487211162, - 0.03420185421418864, - 0.002558890350276973, - 0.0026491308064275168, - 0.0029560183917662166, - 0.005002468530619808, - 0.004674338865394627 + 0.0010619639210744936, + 0.04029734718399899, + 0.02999120229408921, + 0.002259075846571553, + 0.002192290147074042, + 0.0024546149131007646, + 0.003185567546703775, + 0.003025346355570946 ] ], "spread": [ [ null, - 0.08681901513017418, - 0.09382784824059776, - 0.05176701109354531, - 0.04141089927327427, - 0.04488725193794102, - 0.16714035155438928, - 0.08937633737215606, - 0.17030466917906792 + 0.016529254132972147, + 0.09998025007016155, + 0.049664879874346625, + 0.05383105561293264, + 0.03447410098169276, + 0.1747140577921019, + 0.1548508345091371, + 0.16077568061287117 ], [ null, - 0.0246925729997723, - 0.08924334518887247, - 0.07457086740968584, - 0.18645796673891377, - 0.05360533600629835, - 0.2701872344537394, - 0.045770404845422295, - 0.08557130079910938 + 0.030487043860911212, + 0.08727937480889225, + 0.019173792772694, + 0.03613938286491891, + 0.034279497415174796, + 0.08997365611435819, + 0.10023359791906923, + 0.08754994332451231 ], [ null, - 0.039027527214139324, - 0.05894036352347361, - 0.11979623071713057, - 0.10402639227526292, - 0.17998380661528665, - 0.11281040625913531, - 0.11107236330616468, - 0.15053014588465377 + 0.06359342029387884, + 0.03759982374861031, + 0.04678582098573661, + 0.07919831191163934, + 0.03935445011673912, + 0.07830637881531415, + 0.05121286092087061, + 0.07848986100113335 ], [ null, - 0.04936646995438586, - 0.11418698795426512, - 0.12059134279394809, - 0.059124346380420974, - 0.15330362335285372, - 0.02801464412122382, - 0.2631809071714095, - 0.16291120403068735 + 0.1209415112126743, + 0.09622909825766862, + 0.13128710317602377, + 0.037431188522577784, + 0.0443077820770914, + 0.02974163487052947, + 0.037910575804407244, + 0.05793325990372666 ] ], "notes": {} diff --git a/docs/development/bench/linkify-2.json b/docs/development/bench/linkify-2.json index ee090aaf..4aecb4df 100644 --- a/docs/development/bench/linkify-2.json +++ b/docs/development/bench/linkify-2.json @@ -8,45 +8,45 @@ "rows": [ [ "find comment (1 link, 1 email)", - 8.640649716099938e-07, - 4.180944566201106e-05 + 7.194867801540994e-07, + 4.504933335406728e-05 ], [ "find prose (1 KiB)", - 1.14918323094552e-05, - 0.00047429550526582415 + 9.341246917908089e-06, + 0.0004767802053417351 ], [ "has_link comment", - 3.2787860089940796e-07, - 3.0382425125689377e-05 + 3.198661542302972e-07, + 3.284947037703508e-05 ], [ "has_link prose (1 KiB)", - 3.2681881587670603e-06, - 6.782535560153254e-06 + 3.3264642806211477e-06, + 9.183685302597647e-06 ] ], "spread": [ [ null, - 0.07667559493150078, - 0.08598486268838779 + 0.0618758313713348, + 0.03938719948399893 ], [ null, - 0.059957129302934314, - 0.03809032894512174 + 0.028533419404982167, + 0.04244540017319504 ], [ null, - 0.05860313007389166, - 0.07780476576176569 + 0.033248584024003644, + 0.10193610366200706 ], [ null, - 0.049319246480825606, - 0.10847816873948599 + 0.051427368974886416, + 0.11944680145655517 ] ], "notes": {} diff --git a/docs/development/bench/linkify.json b/docs/development/bench/linkify.json index 30788053..25ec797f 100644 --- a/docs/development/bench/linkify.json +++ b/docs/development/bench/linkify.json @@ -9,41 +9,41 @@ "rows": [ [ "comment (1 link, 1 email)", - 3.781744958963884e-06, - 8.422372941652156e-05, - 9.69350645322701e-06 + 2.833639196916001e-06, + 5.694690112060622e-05, + 6.928771550581321e-06 ], [ "prose (1 KiB)", - 5.8999148810319944e-05, - 0.0004075837402789754, - 3.3387339279045136e-05 + 4.209216648121128e-05, + 0.00027776850697591726, + 2.2991062156307862e-05 ], [ "markup (4 KiB)", - 0.00015994283040754453, - 0.002666328884060931, - 0.0002128102349843175 + 0.00011087927229406584, + 0.0016154013672651975, + 0.0001395619845349453 ] ], "spread": [ [ null, - 0.07086155915802331, - 0.11050801498867782, - 0.06710410389389247 + 0.011325012124501117, + 0.05308123636925894, + 0.1543128775631675 ], [ null, - 0.03361607989819003, - 0.05731269830817569, - 0.049776091948529826 + 0.02196262985378034, + 0.029872045724290235, + 0.009715185602970897 ], [ null, - 0.09089060583931145, - 0.024059342839235647, - 0.1153154111287691 + 0.01391027709579616, + 0.00807868275609938, + 0.011463713324468537 ] ], "notes": { diff --git a/docs/development/bench/links-2.json b/docs/development/bench/links-2.json index fa29aab5..84b3ffea 100644 --- a/docs/development/bench/links-2.json +++ b/docs/development/bench/links-2.json @@ -13,85 +13,85 @@ "rows": [ [ "daring fireball (10 kB)", - 7.897827523587846e-05, - 8.12796183519519e-05, - 8.698945166922083e-05, - 0.000250384097607063, - 0.00038412135465174896, - 6.171742408866976e-05, - 7.739290134622934e-05 + 3.9356466906781407e-05, + 8.537428512530217e-05, + 9.082854247803122e-05, + 0.00026713285042963736, + 0.00040999616032877384, + 6.321872400860684e-05, + 7.794494342761027e-05 ], [ "ars technica (56 kB)", - 0.00020205170371430844, - 0.00020543835923566198, - 0.0002171601645386545, - 0.0008300663818469426, - 0.0009304267897126314, - 0.0001539198813039396, - 0.00019101100989852662 + 9.180021663250197e-05, + 0.00022045485021256658, + 0.00026355090974069145, + 0.0008682723103750808, + 0.0009889522232242598, + 0.0001591197206967839, + 0.00020195030824273394 ], [ "mozilla blog (95 kB)", - 0.00038564827292475456, - 0.00033134097158911874, - 0.0003516682579440082, - 0.0017100880424247105, - 0.001365599822065633, - 0.00022994135863048845, - 0.0002855855775199719 + 0.00015685727829387966, + 0.0006314839385671197, + 0.00039728056947296864, + 0.0017832083479637124, + 0.0014447408797574706, + 0.00023703871545421862, + 0.0002975679419705557 ], [ "whatwg spec (235 kB)", - 0.0002516408542116248, - 0.0005860067427647664, - 0.00060525605241916, - 0.004560957929849489, - 0.001670862858190958, - 0.0002454043833305756, - 0.000329380882969114 + 0.00022430012273844113, + 0.000799342570189765, + 0.0006994879075818972, + 0.004874164802458836, + 0.0018399412986885484, + 0.0002569203213814338, + 0.00034682959583657674 ] ], "spread": [ [ null, - 0.007903863312063752, - 0.007429985977232138, - 0.005709366712022955, - 0.011168518748499024, - 0.021407393605724665, - 0.006656947080682276, - 0.011522194117990854 + 0.033531589420485355, + 0.04178334636226221, + 0.03830741719481508, + 0.06037825177403394, + 0.05293479393063974, + 0.01444574611302369, + 0.01255406380524888 ], [ null, - 0.008416212374072259, - 0.012317455813324451, - 0.003385509132172714, - 0.011320861995438535, - 0.02602873694670547, - 0.011528585462947888, - 0.006757544346601625 + 0.009126761347801857, + 0.03805325222482105, + 0.09008961757808379, + 0.01561318603994385, + 0.03240088513209726, + 0.013455508700929193, + 0.017830661159506885 ], [ null, - 0.007817411254915526, - 0.0023171634993249296, - 0.009952790930482652, - 0.007166528598781359, - 0.021365815957723074, - 0.012502247588426139, - 0.006927772624274505 + 0.018904092332618992, + 0.399494085088914, + 0.05530115918703297, + 0.009489188340622848, + 0.02683016582508581, + 0.014220798955373341, + 0.04909279135012377 ], [ null, - 0.004943329428493678, - 0.024256358650669513, - 0.012263418617546295, - 0.015439725544316302, - 0.03968631110153472, - 0.006164063549205968, - 0.002263677275762299 + 0.012208253570299738, + 0.3257678679866179, + 0.03681841828896033, + 0.021499411697625023, + 0.046222957523014585, + 0.024146840663983656, + 0.03191584179688344 ] ], "notes": {} diff --git a/docs/development/bench/links-3.json b/docs/development/bench/links-3.json index 8f4490e9..ad17e201 100644 --- a/docs/development/bench/links-3.json +++ b/docs/development/bench/links-3.json @@ -13,85 +13,85 @@ "rows": [ [ "daring fireball (10 kB)", - 2.9012148117975776e-06, - 1.647298982613658e-05, - 2.085971535355687e-05, - 0.00016380464004820774, - 0.00031565070213446234, - 6.868505702565623e-06, - 3.0170525394150143e-05 + 2.996172853079552e-06, + 1.6866881041484778e-05, + 2.1706805336899986e-05, + 0.00015877394261565314, + 0.000377997809456095, + 6.781967589593781e-06, + 2.5443278827215938e-05 ], [ "ars technica (56 kB)", - 1.0269556899681712e-05, - 5.5183893759173465e-05, - 6.536129939623454e-05, - 0.0005821447746635992, - 0.0007010560300007759, - 2.8764750588550214e-05, - 7.275252779000614e-05 + 1.07103676191637e-05, + 5.6803982133146746e-05, + 6.689157613000891e-05, + 0.0006096830510765964, + 0.0007505117190855041, + 2.0788973480752777e-05, + 6.889208313509698e-05 ], [ "mozilla blog (95 kB)", - 1.9159150432083532e-05, - 0.00011386105690765665, - 0.00012480539973580562, - 0.0012378039816667297, - 0.0009803983301329329, - 4.462856290861813e-05, - 0.00011860925278028844 + 1.986850784400455e-05, + 0.00010975474037877575, + 0.00012791490657567314, + 0.0015380178776164637, + 0.0010356462663973314, + 3.3864474813090815e-05, + 9.120389460785343e-05 ], [ "whatwg spec (235 kB)", - 3.324762803439777e-05, - 0.0003635839235016647, - 0.0003821885512707013, - 0.004207888346172695, - 0.00117126177284869, - 8.501805953642361e-05, - 0.00020120629204711804 + 3.3264580665104404e-05, + 0.00036030607740637305, + 0.0004281737738741261, + 0.0043626483299400816, + 0.0012034119750599832, + 6.291491365573165e-05, + 0.00014887399307402424 ] ], "spread": [ [ null, - 0.010201463727222403, - 0.00933812413740348, - 0.011533179321718892, - 0.12236105538400183, - 0.05625420494982334, - 0.022898679174978225, - 0.08858303598727786 + 0.025611308465656468, + 0.024929672582158197, + 0.12761429143963907, + 0.027426530099872986, + 0.24629390122961012, + 0.01749473297617237, + 0.16459705665378513 ], [ null, - 0.01609684234917494, - 0.01366943413516007, - 0.0093306751370152, - 0.021519429819065447, - 0.019177383357006687, - 0.052164122173223595, - 0.11822096596853024 + 0.012653954590904381, + 0.03626613003301336, + 0.0359179002687463, + 0.04759523037029265, + 0.05540787554018989, + 0.04563892482693914, + 0.3423383136610574 ], [ null, - 0.020570331464049258, - 0.04141288334957416, - 0.039089211844657266, - 0.01635702860576924, - 0.02058753440810391, - 0.15837575088354486, - 0.07787966013169513 + 0.004280553044310928, + 0.00732471497482255, + 0.02772667459186675, + 0.1957462990870172, + 0.05034219665700394, + 0.03608952637075124, + 0.027443453926115163 ], [ null, - 0.013042710419117346, - 0.05247061676530608, - 0.04035566901289052, - 0.04823647361536105, - 0.06098590259526779, - 0.11855757337902133, - 0.06347518140035155 + 0.01749493908665723, + 0.017292612809595903, + 0.16058448881226262, + 0.03129440164383621, + 0.031074925454776758, + 0.02347315070153482, + 0.01986339625402406 ] ], "notes": {} diff --git a/docs/development/bench/links.json b/docs/development/bench/links.json index f26e6c3c..a094f500 100644 --- a/docs/development/bench/links.json +++ b/docs/development/bench/links.json @@ -14,93 +14,93 @@ "rows": [ [ "daring fireball (10 kB)", - 8.540413328243327e-06, - 1.525457977180622e-05, - 1.4777843472775961e-05, - 0.00014333100397342, - 6.561983910557956e-05, - 0.00016360655047265027, - 4.940687075164855e-06, - 1.3445289171206317e-05 + 1.3464031636563808e-05, + 2.3068925242360667e-05, + 2.2397782224459206e-05, + 0.0003142650891921524, + 9.973011774870126e-05, + 0.0003138677027815599, + 9.217930897402008e-06, + 2.158080079084357e-05 ], [ "ars technica (56 kB)", - 2.6025344868211658e-05, - 5.213720703522995e-05, - 5.237701754860306e-05, - 0.0005540351831617348, - 0.00014437131739934253, - 0.0003849035105645271, - 1.5787857462849313e-05, - 4.660196942059732e-05 + 4.56970487050512e-05, + 7.421023221354517e-05, + 7.385782030174444e-05, + 0.0009934364973105403, + 0.00021189110598622088, + 0.000759260253857974, + 3.0278296154762074e-05, + 7.016041224024623e-05 ], [ "mozilla blog (95 kB)", - 4.8695574463408775e-05, - 0.0001045537347768762, - 0.0001032916394857845, - 0.0011797156849600772, - 0.00020872188315479434, - 0.0005297147486468626, - 2.6800711765417873e-05, - 7.235715739284387e-05 + 7.54788005347488e-05, + 0.00014937739054706375, + 0.00016065289965657334, + 0.0019831697577501473, + 0.0003283788316821301, + 0.0009935191236157455, + 5.544195894439478e-05, + 0.00011029813313712111 ], [ "whatwg spec (235 kB)", - 7.78339928473315e-05, - 0.00034933633753553295, - 0.0003470544843745908, - 0.0038690176822152957, - 0.0002640740081195266, - 0.0006312846954112198, - 5.1771214761705174e-05, - 0.00010719847622908674 + 6.635500418402292e-05, + 0.0004964120270566733, + 0.0008334717876626504, + 0.005672234596204362, + 0.0004924321832125619, + 0.0009501940104807242, + 0.00011188485726165709, + 0.00017877139274939205 ] ], "spread": [ [ null, - 0.007242716319812719, - 0.03169356051174955, - 0.0043339976144837564, - 0.010369854528747738, - 0.012627035478506262, - 0.01772626344351562, - 0.006520162247562928, - 0.01634958110066657 + 0.1905638942872358, + 0.03679384648962025, + 0.04534085848493278, + 0.06410865945029473, + 0.05908721604046851, + 0.09626079076033368, + 0.0909416989953497, + 0.09917425590704307 ], [ null, - 0.009166139408406725, - 0.005314394159905668, - 0.03330596578214335, - 0.023762094377173783, - 0.011709054446375005, - 0.013334857731245094, - 0.014321374691221661, - 0.012827710105623906 + 0.13979209516180388, + 0.07871130987482713, + 0.03917221707941639, + 0.15994702991516405, + 0.05260820873932838, + 0.10844328620105656, + 0.056583749631377044, + 0.10048894632853245 ], [ null, - 0.014768482199177618, - 0.0076536327905726426, - 0.005099330122068768, - 0.02465577507151008, - 0.01509937603236634, - 0.01936229950139161, - 0.025897374770069242, - 0.011186973314843278 + 0.09227398650154403, + 0.04563892872259157, + 0.1447248262670361, + 0.11060052253645324, + 0.15167755894071033, + 0.0994675158765982, + 0.15721457116372592, + 0.1141461853260426 ], [ null, - 0.022917952283766932, - 0.009346667245721365, - 0.007447811410346574, - 0.007954385763851199, - 0.0103005754478309, - 0.009477237356627631, - 0.015177916807503744, - 0.027481283435293748 + 0.04344184305052661, + 0.03314387934749421, + 0.09659320127367052, + 0.028492143372953548, + 0.06490956049840732, + 0.04952645743644296, + 0.16176136051599438, + 0.2509424430049855 ] ], "notes": {} diff --git a/docs/development/bench/querying-6.json b/docs/development/bench/querying-6.json new file mode 100644 index 00000000..733f8b3d --- /dev/null +++ b/docs/development/bench/querying-6.json @@ -0,0 +1,23 @@ +{ + "label": "escape 1,000 raw CSS identifiers", + "parties": [ + "turbohtml", + "soupsieve" + ], + "metrics": [], + "rows": [ + [ + "mixed shapes (1,000)", + 6.690426293687324e-05, + 0.001079754202616338 + ] + ], + "spread": [ + [ + null, + 0.018333627446141816, + 0.007448910606025241 + ] + ], + "notes": {} +} diff --git a/docs/development/bench/url-cleaning.json b/docs/development/bench/url-cleaning.json index ed443a90..532f8fca 100644 --- a/docs/development/bench/url-cleaning.json +++ b/docs/development/bench/url-cleaning.json @@ -9,29 +9,29 @@ "rows": [ [ "clean 100 URLs", - 0.0003844782578047064, - 0.0007859442811574505, - 0.0009630483129967615 + 0.0002001551580595636, + 0.0006741690810182869, + 0.0008954732669508303 ], [ "normalize 100 URLs", - 0.0003070245703611363, - 0.0005983442112741008, - 0.0020177926968851048 + 0.00019102673005969942, + 0.0005294812288714942, + 0.0014264048398520874 ] ], "spread": [ [ null, - 0.022289454081313064, - 0.06607782769251852, - 0.03681145454277887 + 0.023980473452570077, + 0.27020235261272907, + 0.19362889792689836 ], [ null, - 0.028259459443914923, - 0.04054379615181831, - 0.04726044582904961 + 0.05593996284173067, + 0.10269535838979268, + 0.047716221029633424 ] ], "notes": {} diff --git a/docs/development/performance.rst b/docs/development/performance.rst index fe35e9e7..f0b150fc 100644 --- a/docs/development/performance.rst +++ b/docs/development/performance.rst @@ -78,8 +78,8 @@ escape each untrusted operand through the same C ``escape``. :func:`turbohtml.clean.linkify` against `bleach `_'s ``linkify``, the HTML-aware linkifier it succeeds, and `lxml-html-clean `_'s ``autolink``. All three parse the HTML and rewrite it. turbohtml's C candidate scan and its own tree carry it past bleach's html5lib pass -by seven to twenty times. It leads lxml's autolink on the comment and 4 KiB markup inputs and trails it on the plain 1 -KiB prose row (0.6x), though that row is not a like-for-like comparison: ``autolink`` only rewrites URLs already inside +by six to twenty times. It leads lxml's autolink on the comment and 4 KiB markup inputs and trails it on the plain 1 KiB +prose row (0.6x), though that row is not a like-for-like comparison: ``autolink`` only rewrites URLs already inside markup and never linkifies an email address, so on plain prose it produces no links at all where turbohtml produces thirty. Its figure there is the cost of finding nothing. @@ -89,8 +89,8 @@ thirty. Its figure there is the cost of finding nothing. The detection primitive on its own, :meth:`turbohtml.clean.LinkDetector.find` against ``LinkifyIt().match`` and :meth:`~turbohtml.clean.LinkDetector.has_link` against ``LinkifyIt().test``, scans a run of plain text and returns the spans or a boolean without rewriting any HTML, so this isolates the C scan from the full linkify rewrite above. It runs -37 to 93 times faster, except on the ``has_link`` prose row (2.1x), where ``test`` short-circuits on the first link near -the start. +51 to 103 times faster, except on the ``has_link`` prose row (2.8x), where ``test`` short-circuits on the first link +near the start. .. bench-table:: :file: bench/linkify-2.json @@ -207,11 +207,11 @@ navigation and footer each classifier must reject are part of the measured cost. finder, and the article extractors trafilatura, newspaper3k, goose3, and news-please that surface a date beside the body text. All read the same signals -- publication/modification ```` tags, JSON-LD, ``