Skip to main content

DOM read measurements

The direct DOM APIs remove Selenium's attribute-helper script. Reusing the element that passed visibility also removes one lookup on a successful read. Visibility still uses Selenium's displayedness script. This is not a script-free browser path, and it does not establish that a particular ScriptTimeoutException will be prevented.

Method​

Measured locally on 2026-10-07, starting from master 2553d592: Linux WSL2 6.18.40.1-microsoft-standard-WSL2, Intel Core Ultra 7 155U (14 logical CPUs exposed), Java 21.0.6, Chrome/ChromeDriver 154.0.8037.92, Selenium 4.10.0, Selenide 6.16.0. Headless Chrome, local driver, static data-URL fixture, identical options, logging reduced to WARN during timing. No CDP features are used; Selenium warns that this Chrome version has no matching CDP module.

Each variant has 15 untimed warmup operations followed by 100 measured operations per browser. Runs use one and four independent browser sessions/workers. Each configuration is repeated twice, reversing the order of the three read variants in the second repeat. Startup, navigation, warmup and shutdown are excluded. P95 and median are computed over operation samples; throughput is completed operations divided by elapsed measurement time across all workers.

Two paths are measured:

  • primitive: a read of an already-found WebElement, followed by an equality assertion.
  • pumpo-assertion: Pumpo agent lookup, visibility, read and equality assertion. This measures a representative assertion scenario, not an entire application test suite or the annotation proxy/reporting overhead.

The legacy API is unchanged, so it provides the before behavior in the same process/browser configuration as the new APIs. Raw before lookup reuse measurements retain the intermediate direct-read implementation with two lookups. Raw final measurements measure the implementation with reuse. The separate runs cannot perfectly control host scheduling, JIT, thermal behavior or unrelated machine load; use the within-run legacy comparisons and command counts as the strongest evidence.

The meaningful migrations compared below are legacy value → DOM property value, and legacy data-offering-id → DOM attribute data-offering-id. The CSV also contains attribute value and property data-offering-id for inspection; these are not equivalent semantics after typing or for a missing custom property, so they must not be treated as interchangeable optimizations.

Results​

Ranges below span the two repeats, not confidence intervals. Latencies are milliseconds; throughput is successful assertion scenarios per second across all workers. “Direct” uses property value or attribute data-offering-id as appropriate.

WorkersPathReadLegacy median msDirect median msLegacy p95 msDirect p95 msLegacy ops/sDirect ops/s
1primitivevalue14.8–17.87.9–8.618.8–23.710.6–13.355.4–67.9109.8–126.1
1primitivedata-offering-id12.7–15.88.1–9.215.6–19.710.1–12.662.4–80.1108.0–125.6
1pumpo-assertionvalue51.0–52.634.4–35.260.6–60.640.1–42.919.0–19.728.7–29.4
1pumpo-assertiondata-offering-id52.2–52.335.6–37.365.4–65.441.1–46.819.0–19.226.8–28.3
4primitivevalue22.1–24.112.8–19.129.7–33.415.6–29.3159.0–173.4198.8–302.8
4primitivedata-offering-id20.5–24.713.7–14.930.6–36.419.2–22.1151.1–183.9252.1–274.1
4pumpo-assertionvalue83.1–95.854.6–68.0105.2–135.968.6–90.240.0–46.956.4–70.8
4pumpo-assertiondata-offering-id80.6–111.155.9–80.998.6–159.069.8–120.134.2–48.547.2–70.1

All intended within-repeat migrations improved median latency and throughput in this final local run. However, parallel load produced substantial variation: four-browser data-attribute scenarios ranged from 55.9 to 80.9 ms even with the same implementation. The separately measured intermediate direct-read run had lower absolute latency in some configurations than the final run despite doing an extra lookup; machine/runtime variation prevents treating that cross-run difference as a causal regression or speedup. The command reduction is deterministic, but its timing benefit depends on conditions.

No improvement is claimed for a full consuming test suite, remote grids, other browsers, failed assertions, or an unresponsive browser. No timeout was increased. The four-browser run increases aggregate throughput but also increases individual operation latency. The source and visibility primitives retained in the inventory have not been benchmarked as replacement candidates, so no broader speedup is claimed.

Validation: the core suite completed with 381 tests, zero failures/errors and 4 skipped tests. The opt-in Chrome semantic and measurement tests both passed separately. All 48 final CSV rows were checked for expected command totals (one for primitives; four for legacy Pumpo reads; three for direct Pumpo reads). Semantic checks include preserving visibility rejection and reacquiring an element replaced immediately before a property read.

Protocol evidence​

DomReadProtocolTest checks the pinned Selenium codec's actual HTTP method/path, and the browser harness counts commands through RemoteWebDriver.execute(CommandPayload).

Successful readFind commandsDisplayedness scriptsValue-read scriptsDirect value GETsTotal round trips
Legacy Pumpo read21104
Direct read before lookup reuse21014
Direct read with lookup reuse11013

A primitive value read remains one round trip: POST /execute/sync becomes GET /element/{id}/attribute/{name} or GET /element/{id}/property/{name}. No fallback read is added. Failure diagnostics, retries, stale elements and delayed visibility can add commands. The lookup reuse is local to one read attempt, never a cross-action element cache. A stale first read is retried through Selenide's locator, with the existing visibility timeout and subsequent read timeout preserved.

Reproduce​

From the repository root, with a compatible installed ChromeDriver:

mvn -f clients/pom.xml -pl core test \
-Dtest=DomReadBrowserTest \
-Dpumpo.dom.browser=true \
-Dwebdriver.chrome.driver=/absolute/path/to/chromedriver \
-Djunit.jupiter.execution.parallel.enabled=false

The harness writes clients/core/target/dom-read-benchmark.csv. -Dpumpo.dom.iterations=100 controls the sample count. The browser tests are opt-in; the ordinary core test suite does not start Chrome. The semantic test covers typed values, live attribute mutations, booleans, missing/empty values, visibility rejection, late insertion, stale handles and replacement between the visibility check and read.

To reproduce the intermediate two-lookup direct path, use getElement(selector, type, timeout).getDomAttribute(name) / .getDomProperty(name) in the two agent methods. Keep all browser/runtime conditions and the harness unchanged. The legacy behavior remains available directly for comparisons without modifying production code.