Repository navigation
Conversation
parse_next_tag() is called in exactly 1 place, and it a few lines earlier `after_tag()` is called. after_tag() does some significant processing, so removing the redundant call has an impact on parsing speed
The private `parse_next_attribute()` method was always called in a loop, until all attributes were exhausted and the end of the tag was reached. The method is replaced with `parse_all_attributes()` that loops internally until no more attributes are found. `parse_all_attributes()` also consistently sets the incomplete state flag if the attribute parsing does not terminate before exhausting the input string, indicating an incomplete token. This simplifies calling code and eliminates duplicate and redundant checks, establishing the invariant that `parse_all_attributes()` will always terminate on the tag's closing `>` _OR_ will set the incomplete input state.
- strspn skipped whitespace - leading "=" gives at least length 1 - strcspn must be at least 1
When the next byte is `>`, parse_all_attributes() now stops without calling strspn() for whitespace and slashes. On the matching base this made the Tag Processor 2.8% faster with tracing JIT and 1.8% faster with JIT off, with no difference for the HTML Processor.
This reverts commit 03d8747.
…cuments. Adds a workflow which compares how the HTML API parses real-world HTML with and without a pull request. It fetches a fresh batch of documents from the latest CommonCrawl crawl, replays the same batch through the target branch and through the pull request merged into it, and leaves a report as a comment on the pull request. The report covers: - Tag Processor and HTML Processor parse times, with totals, per-document deltas, and percentage changes for all, small, medium, and large documents. - A chart of median parse time by document size for both checkouts, leaving out sizes at either end with too few documents for a meaningful median. - How many documents the HTML Processor does not support with each checkout, grouped by the reason it stopped and the line it stopped on. - Which documents became newly supported or unsupported. The analysis takes a while, so it only runs when the `html-api-commoncrawl` label is added to a pull request which changes the HTML API, or when the workflow is run manually with a pull request number. The label is removed as soon as it is added, so it can be added again to request another analysis. How many documents to fetch, how long to spend fetching them, and how many timing rounds to run are configurable. Documents are fetched and replayed with a vendored build of cc-analyzer, which is built without its default HTTP identity. The analysis scripts load the HTML API, and what `wp_kses()` needs, from an explicit list of files without loading WordPress, presenting a UTF-8 site at example.com. The label removal and the comment are handled by a separate `pull_request_target`/`workflow_run` workflow, because workflows triggered by pull requests from forks cannot write to them. It replaces the report from any earlier analysis, searching every page of comments for it. That workflow only runs from the default branch, so it can only be tested after this is merged, or on a fork's default branch. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds `wp_kses( $html, 'post' )` as a third measurement beside the Tag Processor and the HTML Processor. Each document's time is recorded for both checkouts, and the report gains a section for it with the same per-size-group table, chart, and largest slowdowns as the parsers. Only the files `wp_kses()` needs are loaded, without WordPress's default filters such as those on `pre_kses`, so its times show how a change affects `wp_kses()` itself rather than what a full site would measure. The report notes this. Since `wp_kses()` is now measured, pull requests which change `kses.php` can also request the analysis. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| name: HTML API CommonCrawl Comments | ||
|
|
||
| on: | ||
| pull_request_target: |
| - 'tools/html-api-commoncrawl/**' | ||
| - '.github/workflows/html-api-commoncrawl.yml' | ||
| - '.github/workflows/html-api-commoncrawl-comment.yml' | ||
| workflow_run: |
The first run of the CommonCrawl analysis spent half an hour fetching one WARC file: cc-analyzer re-summed its whole cache before storing each document, and read every record of the file, though only about a third are HTML responses. Rebuild cc-analyzer.phar from a cc-analyzer branch which keeps a running total of cached bytes, can sample captures from random blocks of a crawl's URL index, and downloads those records concurrently, retrying throttled transfers without holding up the others. The fetch now samples successful `text/html` captures from many hosts across the whole crawl, downloads only those records four at a time, and retries transfers which CommonCrawl throttles. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fetch in the CommonCrawl analysis failed on GitHub's runner with "Stop reason: worker-failures" and no error, because cc-analyzer did not print why its worker failed. Rebuild cc-analyzer.phar from a version which lists each failed worker's error after a batch fetch. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hed. On GitHub's runner, CommonCrawl answered the first record of the sample with HTTP 403, and that one record failed the whole fetch, so the analysis had no documents. Rebuild cc-analyzer.phar from a version which honors CommonCrawl's rate limiting, waiting out any Retry-After after HTTP 429 or 503, and which skips and lists a record it can't download instead of failing. A 403 means the runner has been blocked, so fetching stops there and the analysis runs on the documents already fetched. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommonCrawl refused every WARC record the runner requested with HTTP 403, while serving the index throughout sampling. That fits either a block on the runner's address after sampling's burst of requests, or a refusal of byte-range requests for records from GitHub's network. Temporarily request a known record by range, the same file without a range, and the index, before the fetch and again if the fetch fails, printing each status, the CDN's headers, and the body of any refusal. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CommonCrawl's CDN blocked the runner outright ("Request blocked." for
every path, ranged or not) after the fetch had sampled its URL index
with about 2,000 requests. Fetching 10,000 records one request each
would draw the same block, so go back to streaming whole WARC files,
which takes a handful of requests in all, and lower the default to
2,000 documents to reduce the demand of each run. One WARC file holds
around 20,000 HTML documents, so the default reads about a tenth of
one.
Rebuild cc-analyzer.phar from a version which keeps the documents a
WARC file delivered even if its download fails, lists the failure, and
finishes with what it has. Remove the temporary access check.
See #66430.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in Jon Surrell's change so that the CommonCrawl analysis can be tried with it. parse_next_tag() has one caller, base_class_next_token(), which calls after_tag() just before it. See #65984, #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
…ommonCrawl report. The report counts the documents the HTML Processor stopped on for each reason, but did not say which ones, so a reason could not be looked into without rerunning the analysis. List up to three example documents for each reason, the smallest from different hosts, with their size and the call site where the HTML Processor stopped, as the report already does for its largest slowdowns. Examples come from the PR, or from the target branch for a reason only it stops for. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The number of documents was only ever meant to bound the network load and the time spent downloading, but the fetch stopped reading a WARC file once it had that many documents, discarding the rest of a file it had already started on. Count WARC files instead: download whole files, one by default, and keep every HTML document in them, around 20,000 per file. The fetch duration still bounds the download. Rebuild cc-analyzer.phar from a version which accepts --limit all, to read each WARC file to its end. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…port examples. The example documents for each unsupported reason, and for documents whose support changed, gave a URL and a call site but not where in the document the HTML Processor stopped, so finding the markup meant reparsing the document. Record what WP_HTML_Unsupported_Exception reports: the byte offset and length of the token being processed, its raw text, and the stack of open elements. When the parser stopped without an exception, record the breadcrumbs of its last token. Show the token, its byte range, and the innermost breadcrumbs in the example tables. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pped. The token alone is often too little to recognize the markup which stopped the HTML Processor: a bare `</a>` or `</font>`. Record up to 30 extended grapheme clusters of the document on either side of the token, with `…` where the document continues beyond them, and show the token within that text in a new Context column of the report's example tables. A long token is shortened in the middle so that the text on either side stays in view. Tokens longer than 300 bytes are now recorded by their start and end rather than their start. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Crawl analysis. WordPress uses the HTML API implementation of wp_kses() by default: default-filters.php adds `__return_false` to `wp_kses_force_legacy_parser`. The analysis does not load that file, so it timed the legacy implementation instead, without the `pre_kses` filters which WordPress runs before it. Without them, the legacy implementation never returns on documents such as https://pdn.ps/p/20371, which stopped every analysis of the crawl's first WARC file. Add the same filter, and load what the HTML API implementation needs: the block processor and the block attribute filters in blocks.php. The other wp_kses() hooks in default-filters.php do nothing for this implementation with `'post'`. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With every HTML document of a WARC file, around 20,000, each pass of the timing step takes several minutes, and printed nothing until it ended, so a run looked stuck. Report the number of documents processed, the time taken, and the rate every 1,000 documents, from both the timing and the support scripts. See #66430. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l analysis. The analysis timed out at 90 minutes: six timing passes in sequence took 80 minutes, before support was recorded. Now each round runs one process for each checkout, with as many rounds at once as there are pairs of vCPUs: on the 4-vCPU runner, two rounds of both checkouts run together. Both checkouts are timed under the same load, as on a busy server, and timing takes about as long as one round. Support is recorded for both checkouts at once as well. The default number of rounds drops from three to two, which fill the runner. Progress lines name their results directory, such as `base/round-2`, since the processes share the log, and the report says when each document was timed only once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The analysis always read the first WARC file listed for the crawl, because cc-analyzer took the listing in order and each run starts with an empty workspace. Every run analyzed the same 21,642 documents until CommonCrawl published its next crawl. The fetch now picks its WARC files at random from the crawl's whole list, around 100,000 files fetched over a couple of weeks. The seed which picked them is shown in the report with the files read, and the new `seed` and `crawl` inputs analyze the same documents again. cc-analyzer.phar is rebuilt from cc-analyzer commit 1bca572, which adds `--warc-order random` and `--seed`, and finds the latest crawl from the data server's listing when index.commoncrawl.org cannot be reached, instead of falling back to a months-old crawl. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Built with Claude code.
Trac ticket: Core-66430