diff --git a/perf/request-response/README.md b/perf/request-response/README.md new file mode 100644 index 00000000..30396682 --- /dev/null +++ b/perf/request-response/README.md @@ -0,0 +1,404 @@ +# Datasette request/response performance on Python 3.15 + +This directory holds a profiling study of Datasette's core URL handling and +request/response cycle at commit `cf113a5`. It covers routing, middleware, +`Request`/`Response`, cookies and signing, URL building, template plumbing, +static files and error paths. Optimising SQL queries against user data was +out of scope. Per-request work that only *touches* SQLite without being a +data query, such as probing a throwaway `:memory:` connection on every +request, was in scope and is labelled "DB-adjacent". + +Everything was measured on **Python 3.15.0b4** (installed with `uv python +install 3.15`). Profiling used the new statistical sampling profiler, +`python -m profiling.sampling` (PEP 799). + +**Nothing in `datasette/` is changed by this branch.** The proposed changes +are the patch files in `patches/`, one per optimisation, plus +`patches/ALL-combined.diff`. + +## Headline results + +`ALL-combined.diff` applies 48 patches to `cf113a5`. With it applied, the +full test suite passes on 3.15: 2869 passed, which is the 2829 baseline +tests plus 40 new ones. Black and ruff are clean. + +These timings are in-process: the harness calls the ASGI app directly, with +no HTTP server. The fixtures database is opened mutable (the `datasette +data.db` default) with 3 SQL threads. Each figure is the median of 5 +interleaved rounds. Base and patched trees were checked out at the same path +depth. + +| Request | Before | After | Change | +|---|---:|---:|---:| +| `GET /favicon.ico` | 897 µs | 31 µs | −97% | +| `GET /-/static/app.css` (83 KB) | 4,141 µs | 147 µs | −96% | +| `GET /-/static/app.css?_hash=…`, the URL every page links to | 4,386 µs | 200 µs | −95% | +| `GET /-/versions` (HTML) | 3,420 µs | 1,011 µs | −70% | +| `GET /-/versions.json` | 1,935 µs | 596 µs | −69% | +| `GET /fixtures/nope/nope/nope/nope` (404 page) | 1,509 µs | 484 µs | −68% | +| `GET /-` (301 redirect) | 120 µs | 39 µs | −67% | +| `GET /-/actor.json` with a `ds_actor` cookie | 206 µs | 80 µs | −61% | +| `GET /-/static/app.css` → 304 (`If-None-Match`) | 274 µs | 120 µs | −56% | +| `GET /fixtures/` (trailing-slash redirect) | 102 µs | 50 µs | −51% | +| `POST /-/actor.json` (405) | 118 µs | 58 µs | −51% | +| `GET /fixtures/facetable.json?_size=5…` | 6,794 µs | 3,635 µs | −46% | +| `GET /fixtures/facetable/1.json` | 3,031 µs | 1,894 µs | −38% | +| `GET /fixtures/compound_three_primary_keys?_size=100` | 48.8 ms | 32.3 ms | −34% | +| `GET /fixtures/facetable?_size=5` (HTML) | 58.2 ms | 43.0 ms | −26% | +| `GET /` (index HTML) | 62.2 ms | 49.8 ms | −20% | +| `GET /…/compound_three_primary_keys.csv?_size=1000` | 21.6 ms | 17.2 ms | −20% | +| `GET /fixtures` (database HTML) | 65.5 ms | 55.7 ms | −15% | +| `GET /.json` | 53.7 ms | 47.5 ms | −11% | + +The same fixtures opened as an **immutable** database (`datasette -i`): + +| Request | Before | After | Change | +|---|---:|---:|---:| +| `GET /fixtures/facetable.json?…` | 7,300 µs | 1,981 µs | −73% | +| `GET /fixtures/facetable/1.json` | 3,386 µs | 1,094 µs | −68% | +| `GET /fixtures` | 53.3 ms | 23.8 ms | −55% | +| `GET /fixtures/facetable` | 56.5 ms | 28.0 ms | −50% | + +For the light endpoints, no patched round was slower than any base round. +The heavy pages (tens of ms, dominated by SQL thread-pool round trips) vary +±10–15% between rounds, so their gains are less precise. The raw per-round +numbers are in `results/`. + +End-to-end results under a real `datasette serve` (uvicorn) are +[further down](#end-to-end-under-datasette-serve). There, the HTTP server +layer is a large fixed cost, so the in-process percentages are diluted on +light requests. Fixing the server stack is a lever of the same size. + +## Where the time went (baseline) + +Share of wall-clock samples on the event-loop thread, from 10 kHz wall-mode +profiles of the in-process harness at `cf113a5` (`results/baseline-profile-breakdown.txt`): + +| Request | Biggest costs | +|---|---| +| Cookie-authenticated JSON (`/-/actor.json`) | re-parsing cookies, headers and query string 32%; itsdangerous signing 20%; GC 11%; dispatch (inspect, hooks, telemetry, routing) 10% | +| Anonymous redirect (`/-`) | dispatch 27% (17% is `inspect.signature()`); GC 16%; signing 10%; re-parsing 10% | +| 404 page | Jinja 78%: walking every template directory, plus `static()` calling `Path.resolve()` 5–12 times | +| `/-/versions.json` | SQLite work on the event loop 53%: a new `:memory:` connection and FTS probes on every request | +| `/-/static/app.css` | waiting on the thread pool 81%: one `aiofiles` hop per 4 KB chunk | +| `/fixtures/facetable.json` | waiting on the thread pool 83%: 12 SQL round trips per request, most of them schema introspection | + +Most of the cost is re-doing work that never changes. Every access to +`request.cookies` re-parses the Cookie header, and there are three per +request. Every `sign`/`unsign` call builds a new serializer. Every request +unsigns a `ds_messages` cookie that isn't there, which raises an exception +and leaves a reference cycle for the GC. Every view call runs +`inspect.signature()`. Every 404 walks every template directory. Every static +asset is hashed or chunked in ways that cost far more than the bytes. None +of this is new work per request; it is repeated work. + +## The proposed changes + +Each patch applies on its own to `cf113a5` with `git apply`, unless noted. +They were found and prototyped by investigators working per area, with +interleaved A/B timing, profiler evidence and the relevant tests. Each area +was then reviewed adversarially for correctness, security and plugin +compatibility. Where the reviewer asked for changes, the corrected version +is the one in this directory. + +"In-process effect" is from the final sequential run of each area's patches +alone against the same base (`results/inprocess-summary.txt`). Effects +below about ±8% are inside the noise of that run. Smaller wins are +supported by micro-benchmarks and profiler sample shares in the area notes. + +### Request object and router — `request-object-*`, `signing-*` + +Area effect: cookie-auth JSON −52%, redirect −39%, trailing slash −37%, +POST 405 −44%, static 304 −35%, index HTML −14%. + +| Patch | What it does | +|---|---| +| `request-object-1-cache-request-headers` | `Request.headers` decoded every scope header on every access: 4–18 times per request. It is now cached per `Request` against a snapshot of `scope["headers"]`, so it stays correct when `route_path` reassigns `request.scope` or a plugin replaces or appends headers. It still returns a fresh dict. | +| `request-object-2-memoize-cookie-parse` | `Request.cookies` built a new `SimpleCookie` on every access. Parsing is now memoised by Cookie-header string (LRU of 128, headers up to 8 KB only), and a copy is returned. | +| `request-object-3-single-pass-send-wrapper` | The Cache-Control/Vary privacy wrapper in `route_path` built `request.headers` twice and made four passes over the response headers. It now makes one pass. Output was fuzzed as byte-identical. | +| `request-object-4-cache-request-args` | `Request.args` re-ran `parse_qs` 29–34 times per table request. It is now cached per query string, with fresh lists on each access. | +| `request-object-5-skip-absent-ds-messages-unsign` | `route_path` unsigned the `ds_messages` cookie even when absent. `unsign("")` always raises `BadSignature`, and the traceback it left behind formed a reference cycle that triggered GC. (`signing-2` is the same fix; apply only one.) | +| `signing-1-cache-serializer` | `Datasette.sign`/`unsign` built a new `URLSafeSerializer` per call: itsdangerous runs `json.dumps({})` to sniff the serializer and re-derives the key. Serializers are now cached per `(secret, namespace)`. Unhashable secrets, such as key-rotation lists, fall back to an uncached serializer. | +| `signing-3-actor-cookie-single-parse` | `actor_from_request` read `request.cookies` twice. | + +### Routing and view dispatch — `routing-*` + +Area effect: redirect −17%. `datasette.client` requests are 13–44% faster; +see `routing-2`. + +| Patch | What it does | +|---|---| +| `routing-1-cache-view-signatures` | `_gather_arguments` called `inspect.signature()` on every request for every wrapped view and output renderer, and `wrap_view_function` called `inspect.iscoroutinefunction` per request. The parameter names are now cached in a `WeakKeyDictionary`, and the coroutine check moves to wrap time. | +| `routing-2-reuse-default-routes` | `DatasetteClient.app` calls `ds.app()` for **every** `datasette.client` request, which rebuilt about 61 routes and called `get_plugins()`. The default route table is now reused until the plugin registry or `static_mounts` changes. Plugin `register_routes` is still called every time. `ds.app()` drops from 318 to 5 µs. | + +### Middleware, OpenTelemetry and GC — `middleware-gc-*` + +Area effect: static 304 −11%, trailing slash −10%, index HTML −15%, +index JSON −12%. + +| Patch | What it does | +|---|---| +| `middleware-gc-1-telemetry-no-provider-fast-path` | With no OpenTelemetry provider installed, `TelemetryMiddleware` still ran `extract()` and three nested no-op span context managers on every request, about 8 µs. It now skips the span when it would be `INVALID_SPAN` anyway. | +| `middleware-gc-2-skip-noop-db-query-spans` | Two no-op spans per SQL query (`db.query` and `db.query.execute`), about 10 µs of Python per query, which adds up on pages making 70+ queries. (Conflicts textually with patch 1; `ALL-combined.diff` resolves it.) | +| `middleware-gc-3-csrftoken-no-scope-cycle` | The legacy `scope["csrftoken"]` closure captured `scope` and was stored in it, so every request leaked a cycle to the GC. Combined with `request-object-5`, a request that makes no permission checks, such as `/-/actor.json`, now leaves **zero** cyclic garbage, down from 53 objects. Permission checks still leave about 13 each; see the bugs list. | +| `middleware-gc-4-router-single-scope-copy` | One `dict(scope, …)` copy per request instead of two, and a module-level import instead of one per request. | +| `middleware-gc-5-telemetry-header-getter-length-check` | The trace-context header getter lowercased every header name. It now compares lengths first. | +| `middleware-gc-7-tracer-trace-fast-path` | `datasette.tracer.trace()` looked up the asyncio task on every SQL query even with no `?_trace=1`. Decorator use of `@trace(...)` keeps working. | + +### Static files — `static-files-*` + +Area effect: favicon −92%, `app.css` −94%, `app.css?_hash=` −95%, 404 page +−47%. + +| Patch | What it does | +|---|---| +| `static-files-1-cache-hash-param-sha256` | A `?_hash=` request, which is every static URL Datasette's templates emit, ran `sha256_file()` over the whole file **synchronously on the event loop** on every request. The hash is now cached per `(inode, size, mtime_ns, ctime_ns)`. | +| `static-files-2-send-file-fewer-thread-hops` | `asgi_send_file` did a thread-pool hop for stat, open, close and every 4 KB read: about 25 hops and 21 ASGI messages for `app.css`. It now reads files up to 1 MiB in one hop and streams 64 KB chunks above that. | +| `static-files-3-memoize-static-template-urls` | The template `static()` helper resolved paths with `Path.resolve()` (about 50–90 µs) on every call, 5–12 times per HTML page. Successful results are now memoised. | +| `static-files-4-favicon-from-memory` | The favicon was re-read from disk, using 4 thread hops, on every request. | +| `static-files-5-resolve-static-root-once` | The static root was resolved again on every request. The first version of this patch was **refuted** in review, because a symlinked `--static` root re-pointed at runtime broke. This version re-resolves when the root's `(st_dev, st_ino)` changes and adds a regression test. | +| `static-files-6-serve-small-static-files-from-memory` | Files up to 1 MiB are served from a 16 MiB stat-validated in-memory cache, with zero thread hops. It keeps a running byte counter, after review found an O(n) re-sum on every cache miss. | + +### Error paths — `error-paths-*` + +Area effect: 404 page −20%. `table_json` −8% and permission checks generally +are faster through patch 2. + +| Patch | What it does | +|---|---| +| `error-paths-1-list-only-pages-templates` | Every 404 called `Environment.list_templates()`, walking every template directory (the default one twice), to look for `pages/{wildcard}.html`. It now lists only `pages/`, with the same results and order on 19 loader shapes. | +| `error-paths-2-permission-params-no-per-request-regex` | `PermissionSQL.allow()` names each parameter `:reason_N` with a global counter, so `check_permissions_for_actions` compiled a never-seen regex for every permission check. It now does a single-pass rename. | +| `error-paths-3-rich-traceback-only-for-5xx` | **Bug.** With `rich` installed, every 4xx printed a `show_locals=True` traceback, about **0.9 s** per table-not-found 404. Unmatched-route 404s **crashed** with `ValueError` (500), because `print_exception` ran outside an `except` block. Rich tracebacks now print only for 5xx errors, as the plain-Python path already does. | + +### Template rendering plumbing — `template-plumbing-*` + +Area effect: 100-row table page −16%, table HTML −7%. Render-only, the +100-row table page goes from 13.1 ms to 5.7 ms. + +| Patch | What it does | +|---|---| +| `template-plumbing-1-buffer-table-rows-in-templates` | In Jinja's async mode, every output fragment travels through `async for event in gen: yield event` at each `extends`/`block`/`include` level, about 170 ns per level per fragment. A 100-row table yields 6,810 fragments through 4–5 levels. The row loops in `_table.html` and `_query_results.html` are now captured with `{% set %}…{% endset %}`. Output is byte-identical on 47 endpoints. Users' overridden `_table.html` templates are unaffected. | +| `template-plumbing-2-buffer-base-content-block` | Does the same for `base.html`'s content block, inside an empty `{% with %}` so the buffer variable cannot shadow a child template's or plugin's `content_html`. That collision was found in review. | +| `template-plumbing-3-absolute-url-plain-str` | `absolute_url()` passed a `PrefixedUrlString` to `urljoin`, costing about 58 µs per call. | +| `template-plumbing-4-skip-render-cell-hook-without-impls` | `render_cell` was dispatched once per **cell** even with no plugin implementing it. `DATASETTE_TRACE_PLUGINS` output is unchanged. | +| `template-plumbing-5-hoist-table-path-out-of-row-loop` | `urls.table()` was computed once per row. | +| `template-plumbing-6-skip-template-hooks-without-impls` | `render_template` called six hooks with no implementations on every page. This saves about 12 µs. | +| `template-plumbing-7-database-view-reuse-selected-template` | The database view selected its template twice. | + +Jinja async mode itself costs 35–43% on a render microbenchmark compared +with sync mode. It cannot be turned off, because templates call async +functions (`crumb_items`, `menu_links`) and the plugin docs promise that +awaitables in template context are auto-awaited. + +### URL building — `url-building-*` + +Area effect: table JSON −10%. Facet-heavy HTML pages improve 4–19% in the +area's lower-noise harness. + +| Patch | What it does | +|---|---| +| `url-building-1-prefixed-url-string-class-methods` | `PrefixedUrlString.__getattribute__` called `dir(str)` and built a closure on **every** attribute access, about 9.5 µs each, and `urljoin` makes about 7 per call. The wrapped methods are now defined once on the class. A property test over 180,000 calls gave identical results. | +| `url-building-2-tilde-encode-safe-fast-path` | `tilde_encode` ran a per-byte generator even for names that need no encoding. A regex fast path was checked identical on 200,000 strings. | +| `url-building-3-tilde-decode-skip-token-when-nothing-to-decode` | `tilde_decode` called `secrets.token_hex(16)`, an `os.urandom` syscall, on every call, including for every database and table name in every URL. | + +### Response serialization — `response-serialization-*` + +These matter most under a real server, so the in-process harness shows +little. + +| Patch | What it does | +|---|---| +| `response-serialization-1-chunk-csv-stream-writes` | CSV sent **one ASGI message per row**. A 20,000-row `_stream=1` CSV was 20,003 messages. It now buffers up to 64K characters, flushed per page. Under uvicorn, a 1,000-row CSV goes from 45 ms to 17 ms and a 20k-row stream from 1.24 s to 0.67 s. Bytes are identical. | +| `response-serialization-2-json-renderer-skip-infinity-scan` | The JSON renderer scanned every value of every row in Python for infinities. It now encodes with `allow_nan=False` and falls back to the old path on `ValueError`. **Behaviour change:** `_shape=arrayfirst` with an infinity in a non-first column used to 500 (AssertionError) and now returns 200. A regression test is included. | +| `response-serialization-3-csv-bytes-check-without-genexpr` | Per-row `any(isinstance(…) for …)` becomes `any(map(…))`. | +| `response-serialization-4-head-skips-csv-generation` | `HEAD` on a CSV endpoint generated and streamed the whole CSV. For `_stream=1` that meant paginating the whole table: 680 ms became 6 ms. Status and headers are identical. | + +### DB-adjacent per-request work — `db-adjacent-*` + +These are not data queries. They are introspection and probes that ran on +every request. Area effect: `/-/versions.json` −50%, `/-/versions` −27%, +table JSON −22%. On an immutable database, table JSON is −57%, row JSON −45% +and the database page −45%. + +| Patch | What it does | +|---|---| +| `db-adjacent-1-cache-versions-probe` | `/-/versions` opened a `:memory:` connection, ran every `prepare_connection` hook and created three FTS virtual tables on **every** request. The result is now cached until the plugin set changes. | +| `db-adjacent-2-memoize-detect-json1` | `detect_json1()` with no connection opened a fresh SQLite connection on every table HTML page. | +| `db-adjacent-3-column-types-skip-introspection` | `get_column_types` ran `PRAGMA table_xinfo` even when no column types were assigned. | +| `db-adjacent-4-reuse-table-columns-for-sort` | `table_columns()` was introspected twice per table request. | +| `db-adjacent-5-row-reuse-resolved-results` | The row page ran the same primary-key `SELECT` twice. | +| `db-adjacent-6-cache-immutable-db-introspection` | For immutable databases the schema cannot change, so introspection is cached from one `sqlite_master` read. Review bounded the cache to real schema names, so junk `?_through=` names cannot fill it. | +| `db-adjacent-7-batch-refresh-schemas-reads` | `refresh_schemas()` made 8 sequential round trips to read the internal catalog. | +| `db-adjacent-8-skip-unused-label-column-lookups` | `table_view_data` looked up a label column for every foreign key, then discarded the result. | + +### Found by the completeness critic — `critic-*` + +| Patch | What it does | +|---|---| +| `critic-1-permission-sql-stable-param-names` | Because of the `:reason_N` counter, **the SQL text of every permission check was unique**, so SQLite re-prepared it every time: about 170 µs instead of 10 µs on the internal database. Parameters are now named by position. This needs `error-paths-2` applied first. With `error-paths-2` and `critic-2`, the effect was `/-/versions.json` −20%, row JSON −16% and table JSON −18%. | +| `critic-2-table-rows-dict-zip` | `dict(sqlite3.Row)` does a case-insensitive name lookup per column, which is O(columns²) per row. It now uses `zip` when names are unique. | + +## End-to-end under `datasette serve` + +E2E_PLACEHOLDER + +## Bugs found along the way + +Fixed by the patches above: + +- **`rich` makes 4xx responses very slow and makes unmatched routes crash** + (`error-paths-3`). +- **`HEAD` on CSV URLs generates the entire CSV** (`response-serialization-4`). +- **`?_hash=` static requests hash the whole file on the event loop** + (`static-files-1`). +- **Every permission query is a fresh SQLite statement** (`critic-1`). +- **`_shape=arrayfirst` returns a 500** when a row holds an infinity outside + the first column (`response-serialization-2` changes this to 200). + +Not fixed, reported for follow-up: + +- **httpx2 retries a failed `import sniffio` on every `datasette.client` + request.** sniffio is no longer installed, and a failed import is not + cached, so `sys.path` finders are walked on every client and `TestClient` + request. That is about 29% of a client request (454 → 324 µs when the + failure is cached). Fix: depend on `sniffio`, or fix it upstream in httpx2. +- **`calculate_etag`'s cache is never invalidated.** After a static file is + edited in place, the old ETag keeps being served and clients get 304s for + changed content. Keying it on `(size, mtime_ns)`, as `static-files-1` does + for the hash, would fix it. +- **`tilde_decode` is non-deterministic** for input where a `~` escape is cut + short by `%`. For example, `tilde_decode("90~3%")` returns `90%` only about + 63% of the time, because the random placeholder token gets decoded. +- **`--pdb` with an unmatched-route 404 crashes** in + `pdb.post_mortem(None)`. +- **`database_download` sends both `Content-Length` and + `Transfer-Encoding: chunked`.** +- **Foreign-key label links use `tilde_encode(database_name)`** instead of the + database route (`views/table.py` `LINK_WITH_LABEL`). +- **Each permission check leaves about 13 cyclic objects** through the + self-recursive closures in `Datasette._allowed_many`. +- **`tests/test_plugins.py` unregisters the wrong plugin** around line 1547. + +## Further proposals, not in `ALL-combined.diff` + +These are prototypes in `patches/optional/`. Each was measured, but either +not adversarially reviewed or carrying a trade-off to decide first: + +- **Install httptools by default**, for example through a `datasette[fast]` + extra or `uvicorn[standard]`. See the end-to-end section. No code change. +- **Add `--no-access-log` to `datasette serve`.** The uvicorn access log is + 8–13% of server CPU on light requests. +- **Send `Content-Length` on non-streaming responses.** About 3% end to end. + It also gives clients sizes and progress. +- `critic-3-extras-sequential`: table HTML resolves about 30 "extras" as + concurrent asyncio tasks (asyncinject `parallel=True`). Running them + sequentially measured −18 to −20% on table HTML. This contradicts + `run_sequential` in `table.py` (#2189), but the count and facet queries + may genuinely benefit from overlapping on large tables. +- `critic-5-request-scoped-introspection-memo`: foreign keys are + introspected 5×, and primary keys and `PRAGMA schema_version` 2×, per + table HTML request. A request-scoped memo plus a one-trip prefetch + measured −25% on table JSON. **It does not yet clear the memo on writes** + within the same request. +- `critic-6-inline-tableless-permission-checks`: permission checks whose + SQL reads no table could run inline (7 µs) instead of through the thread + pool (330 µs). That would take `/-/versions.json` from 567 to 238 µs. +- `critic-4-skip-fts-lookup-for-data-formats`: one FTS-detection round trip + per JSON/CSV table request that only HTML uses. +- `middleware-gc-6-serve-gc-freeze-after-startup`: `gc.freeze()` after + startup removes about 40 ms gen-2 pauses on query-heavy pages. It made no + difference end to end on light requests, and it hides startup objects + from `gc.get_objects()`. **Not recommended** without an opt-out. + +## Ideas measured and rejected + +- **uvloop.** `datasette serve` never uses it: `cli.py` calls + `asyncio.run()`, which bypasses uvicorn's `loop="auto"`. Forcing it gained + nothing measurable. +- **One combined alternation regex, or prefix dispatch, for routing.** Route + matching is 0.3–15 µs (2.5% of a row request). A combined regex is only + 1.4× faster and unsafe for arbitrary plugin regexes. +- **Sync Jinja.** It is faster, but async template functions are a + documented API. +- **`auto_reload=False`, and caching negative template lookups.** These + would stop new or edited templates being picked up without a restart. +- **GC threshold tuning.** Within noise. GC came from cycles, which are now + removed, not from heap size. +- **Coalescing uvicorn's header and body writes.** No gain, because only the + first write of a response, the one that wakes the peer, is expensive. +- **Reusing one `httpx2.AsyncClient` across `datasette.client` calls.** It + would leak cookies between calls. +- **Running introspection on the event loop thread.** A locked database + would block the loop for up to the 5 s busy timeout. +- **Overlapping thread-pool trips with `asyncio.gather`.** This was 5–14% + *slower*: more wake-ups and GIL contention. + +## The Python 3.15 sampling profiler + +Every finding started from `profiling.sampling`. Examples: + +```bash +# in-process steady-state loop, 10 kHz, collapsed stacks for analysis +python -m profiling.sampling run -r 10khz --mode wall --collapsed -o x.collapsed \ + tools/bench_asgi.py loop actor_json_cookie --seconds 6 +python tools/analyze_collapsed.py x.collapsed --buckets +python tools/analyze_collapsed.py x.collapsed --children "DatasetteRouter.handle_404" + +# attach to a running `datasette serve` while load runs +python -m profiling.sampling attach -r 10khz -d 10 --mode cpu --collapsed -o serve.collapsed PID + +# which tree is a server running? dump shows file paths in the live stack +python -m profiling.sampling dump PID +``` + +These features were all useful: wall, `cpu` and `gil` modes; `-a` for all +threads; `--async-aware`; `--opcodes` (with `--heatmap`/`--flamegraph`); +`--binary` plus `replay`; `--diff-flamegraph` against a binary baseline; +`--heatmap`; and `--blocking`. Sampling is out-of-process, so it slows the +target very little, and `attach` works on a production-shaped server with +no restart. + +Things to know when reading its output on 3.15.0b4: + +- **Many samples fail in non-blocking mode: 25–65% "error rate"**, worst + inside Jinja-compiled template code. Frames for included templates and + child blocks often never appear, so template-body cost shows up on + pass-through lines. `--blocking` drops errors to about 0.5% but slows the + target about 3×. The layer split matched between the two within about 6 + points. +- **`` is attributed to whichever frame next hits the eval breaker**, so + it can appear under unrelated code such as `Datasette.setting`. +- **`run` puts the current directory first on `sys.path`** (like `-m` and + `-c`). Run it from outside a source tree you don't intend to import. +- **`--opcodes` is not accepted with `--collapsed` or `--pstats`.** +- **`--async-aware --async-mode all` failed with 100% errors** on the + table endpoint. It worked on lighter ones. +- **Line numbers after `--binary` + `replay` were sometimes implausible**, + for example comment lines. Line-level shares here come from direct + `--collapsed` runs. + +## Reproducing + +```bash +uv python install 3.15 +uv venv -p 3.15 /tmp/venv315 +uv pip install -p /tmp/venv315 -e . pytest pytest-asyncio pytest-xdist beautifulsoup4 \ + pytest-timeout trustme cogapp opentelemetry-sdk "multipart-form-data-conformance==0.1a0" + +# two trees at the same path depth: Datasette.static() calls Path.resolve(), +# which costs more for deeper install paths and skews HTML-page comparisons +git worktree add --detach ../t/base cf113a5 +git worktree add --detach ../t/patched cf113a5 +(cd ../t/patched && git apply "$OLDPWD"/perf/request-response/patches/ALL-combined.diff) + +/tmp/venv315/bin/python perf/request-response/tools/measure_ab.py \ + --tree base=../t/base --tree patched=../t/patched \ + --endpoints actor_json_cookie,redirect_dash,versions_json,not_found,static_css \ + --n 1000 --rounds 5 +``` + +`tools/bench_asgi.py` drives the ASGI app directly with realistic browser +headers. `tools/measure_ab.py` interleaves trees round by round, using a +fresh process per run. `tools/analyze_collapsed.py` attributes collapsed +stacks to components. The measurements were taken on a 4-vCPU cloud VM, so +compare ratios rather than absolute numbers. diff --git a/perf/request-response/results/inprocess-heavy.json b/perf/request-response/results/inprocess-heavy.json new file mode 100644 index 00000000..19a2198e --- /dev/null +++ b/perf/request-response/results/inprocess-heavy.json @@ -0,0 +1,1022 @@ +{ + "index_html": { + "base": { + "median_us": 62239.6, + "runs": [ + 55740.5, + 62239.6, + 59573.6, + 65316.6, + 64298.5 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "combined": { + "median_us": 49837.1, + "runs": [ + 42958.2, + 48203.1, + 49837.1, + 56248.0, + 52976.5 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "A_request_signing": { + "median_us": 53709.0, + "runs": [ + 53630.4, + 53229.3, + 70637.0, + 64856.2, + 53709.0 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "B_routing": { + "median_us": 55989.5, + "runs": [ + 53692.5, + 52038.8, + 55989.5, + 63829.6, + 65343.4 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "C_middleware": { + "median_us": 53208.8, + "runs": [ + 52661.8, + 50627.1, + 58799.7, + 53208.8, + 59298.3 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "D_static": { + "median_us": 60280.6, + "runs": [ + 50666.0, + 60280.6, + 59719.2, + 62596.3, + 63699.6 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "E_errors": { + "median_us": 56233.2, + "runs": [ + 51699.5, + 59222.1, + 56233.2, + 53480.3, + 63424.8 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "F_templates": { + "median_us": 57762.9, + "runs": [ + 53542.1, + 57762.9, + 55039.3, + 61748.7, + 58175.7 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "G_urls": { + "median_us": 56583.0, + "runs": [ + 54075.1, + 53611.0, + 56583.0, + 66771.3, + 59083.1 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "H_response": { + "median_us": 59315.1, + "runs": [ + 59315.1, + 57877.3, + 59902.9, + 59336.1, + 56715.7 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "I_dbadj": { + "median_us": 59276.0, + "runs": [ + 50527.9, + 64694.1, + 58426.1, + 66785.9, + 59276.0 + ], + "status_bytes": [ + 200, + 3043 + ] + }, + "J_critic": { + "median_us": 57427.3, + "runs": [ + 52038.7, + 56923.0, + 60401.5, + 57427.3, + 57546.9 + ], + "status_bytes": [ + 200, + 3043 + ] + } + }, + "index_json": { + "base": { + "median_us": 53657.9, + "runs": [ + 48846.2, + 53170.9, + 53657.9, + 60937.8, + 55669.7 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "combined": { + "median_us": 47492.6, + "runs": [ + 47492.6, + 43407.0, + 47430.7, + 59973.9, + 51064.3 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "A_request_signing": { + "median_us": 56852.3, + "runs": [ + 50452.8, + 56852.3, + 60516.0, + 67470.1, + 52498.9 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "B_routing": { + "median_us": 58082.3, + "runs": [ + 58082.3, + 51919.6, + 60027.6, + 64115.4, + 57774.8 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "C_middleware": { + "median_us": 47178.7, + "runs": [ + 52667.3, + 47178.7, + 46656.2, + 42564.4, + 59244.4 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "D_static": { + "median_us": 55826.2, + "runs": [ + 55639.0, + 52601.8, + 59060.2, + 55826.2, + 57862.4 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "E_errors": { + "median_us": 52773.8, + "runs": [ + 49251.0, + 49333.3, + 56813.4, + 55658.8, + 52773.8 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "F_templates": { + "median_us": 58680.3, + "runs": [ + 46191.4, + 55478.3, + 58680.3, + 66570.5, + 67530.0 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "G_urls": { + "median_us": 57226.4, + "runs": [ + 60438.0, + 55993.0, + 61462.0, + 57226.4, + 53730.3 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "H_response": { + "median_us": 62428.9, + "runs": [ + 63553.3, + 57521.3, + 67665.3, + 55959.0, + 62428.9 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "I_dbadj": { + "median_us": 53976.3, + "runs": [ + 49871.7, + 48615.0, + 60260.2, + 53976.3, + 60860.8 + ], + "status_bytes": [ + 200, + 1611 + ] + }, + "J_critic": { + "median_us": 54827.2, + "runs": [ + 53029.9, + 48517.0, + 64903.5, + 56555.9, + 54827.2 + ], + "status_bytes": [ + 200, + 1611 + ] + } + }, + "database_html": { + "base": { + "median_us": 65521.8, + "runs": [ + 55656.9, + 67323.6, + 65521.8, + 58244.7, + 67991.1 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "combined": { + "median_us": 55694.0, + "runs": [ + 57144.3, + 44598.8, + 55846.7, + 55516.1, + 55694.0 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "A_request_signing": { + "median_us": 61789.9, + "runs": [ + 60830.3, + 65962.2, + 61789.9, + 67951.1, + 60608.1 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "B_routing": { + "median_us": 64556.0, + "runs": [ + 65119.9, + 64311.4, + 63289.9, + 69658.0, + 64556.0 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "C_middleware": { + "median_us": 60600.5, + "runs": [ + 54163.5, + 60600.5, + 54967.4, + 61942.0, + 70908.2 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "D_static": { + "median_us": 62105.6, + "runs": [ + 56227.3, + 62877.6, + 62105.6, + 61823.5, + 67835.7 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "E_errors": { + "median_us": 66711.1, + "runs": [ + 66711.1, + 59833.8, + 62041.0, + 74195.1, + 73532.3 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "F_templates": { + "median_us": 61229.7, + "runs": [ + 57298.9, + 59519.7, + 65802.3, + 61229.7, + 62966.5 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "G_urls": { + "median_us": 62698.2, + "runs": [ + 63253.2, + 57377.4, + 60719.0, + 65565.7, + 62698.2 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "H_response": { + "median_us": 66549.3, + "runs": [ + 62005.2, + 66912.6, + 70790.9, + 66549.3, + 62183.2 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "I_dbadj": { + "median_us": 66807.6, + "runs": [ + 70260.1, + 67814.8, + 59345.5, + 66140.1, + 66807.6 + ], + "status_bytes": [ + 200, + 23013 + ] + }, + "J_critic": { + "median_us": 66002.7, + "runs": [ + 66002.7, + 59691.4, + 68672.7, + 67526.0, + 60664.8 + ], + "status_bytes": [ + 200, + 23013 + ] + } + }, + "table_html": { + "base": { + "median_us": 58207.3, + "runs": [ + 53182.5, + 58207.3, + 66807.7, + 61900.2, + 52599.8 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "combined": { + "median_us": 42954.1, + "runs": [ + 37740.9, + 41111.7, + 47034.3, + 43366.7, + 42954.1 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "A_request_signing": { + "median_us": 58564.8, + "runs": [ + 56725.5, + 49933.8, + 60827.3, + 58564.8, + 60467.0 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "B_routing": { + "median_us": 57928.2, + "runs": [ + 63721.9, + 57665.6, + 65023.8, + 57928.2, + 54561.0 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "C_middleware": { + "median_us": 56490.5, + "runs": [ + 56490.5, + 56561.8, + 55267.7, + 56190.0, + 58117.9 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "D_static": { + "median_us": 55819.1, + "runs": [ + 50981.4, + 55819.1, + 58578.8, + 54073.9, + 62422.4 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "E_errors": { + "median_us": 58918.9, + "runs": [ + 53240.3, + 54093.9, + 58918.9, + 61386.1, + 62684.9 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "F_templates": { + "median_us": 54376.2, + "runs": [ + 49009.5, + 52215.1, + 58903.5, + 60934.2, + 54376.2 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "G_urls": { + "median_us": 58430.1, + "runs": [ + 50549.5, + 71577.1, + 56843.4, + 59127.7, + 58430.1 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "H_response": { + "median_us": 62191.9, + "runs": [ + 51512.0, + 62191.9, + 72588.2, + 62449.3, + 57212.3 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "I_dbadj": { + "median_us": 55630.1, + "runs": [ + 54773.6, + 50739.9, + 55630.1, + 58914.6, + 56333.4 + ], + "status_bytes": [ + 200, + 26551 + ] + }, + "J_critic": { + "median_us": 54872.0, + "runs": [ + 55864.6, + 53267.4, + 53616.1, + 60426.4, + 54872.0 + ], + "status_bytes": [ + 200, + 26552 + ] + } + }, + "table_html_100": { + "base": { + "median_us": 48844.6, + "runs": [ + 43891.5, + 48844.6, + 51173.0, + 49023.5, + 46990.8 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "combined": { + "median_us": 32287.4, + "runs": [ + 28398.4, + 32287.4, + 34612.5, + 31726.4, + 32390.4 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "A_request_signing": { + "median_us": 50922.8, + "runs": [ + 43472.1, + 41866.0, + 50922.8, + 57089.3, + 51586.2 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "B_routing": { + "median_us": 45681.2, + "runs": [ + 42973.9, + 50093.2, + 51820.9, + 45681.2, + 43994.0 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "C_middleware": { + "median_us": 46526.3, + "runs": [ + 46412.7, + 46526.3, + 51218.3, + 41523.6, + 49807.7 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "D_static": { + "median_us": 46093.2, + "runs": [ + 42234.6, + 46093.2, + 53732.5, + 45587.8, + 55271.0 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "E_errors": { + "median_us": 45938.2, + "runs": [ + 44451.1, + 45938.2, + 50462.1, + 45795.8, + 48772.4 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "F_templates": { + "median_us": 40864.0, + "runs": [ + 36632.4, + 40864.0, + 45042.5, + 40015.7, + 43558.3 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "G_urls": { + "median_us": 49289.5, + "runs": [ + 44963.8, + 54828.9, + 49289.5, + 48126.9, + 50164.5 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "H_response": { + "median_us": 50459.4, + "runs": [ + 48376.7, + 50459.4, + 45614.8, + 50558.6, + 52359.4 + ], + "status_bytes": [ + 200, + 66669 + ] + }, + "I_dbadj": { + "median_us": 45203.7, + "runs": [ + 45203.7, + 44938.9, + 43420.3, + 49838.4, + 51441.3 + ], + "status_bytes": [ + 200, + 66670 + ] + }, + "J_critic": { + "median_us": 49174.1, + "runs": [ + 42480.9, + 46592.0, + 51273.1, + 49174.1, + 57613.0 + ], + "status_bytes": [ + 200, + 66670 + ] + } + }, + "table_csv_1000": { + "base": { + "median_us": 21580.3, + "runs": [ + 20338.4, + 21580.3, + 24641.4, + 21428.0, + 24137.9 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "combined": { + "median_us": 17162.9, + "runs": [ + 14720.6, + 17162.9, + 18873.9, + 19974.0, + 16266.5 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "A_request_signing": { + "median_us": 21607.9, + "runs": [ + 18608.3, + 21607.9, + 24948.1, + 23608.8, + 19898.9 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "B_routing": { + "median_us": 22777.9, + "runs": [ + 21657.3, + 25616.4, + 25250.3, + 21820.9, + 22777.9 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "C_middleware": { + "median_us": 22975.4, + "runs": [ + 22975.4, + 21116.0, + 24456.5, + 25110.3, + 20937.9 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "D_static": { + "median_us": 23429.8, + "runs": [ + 22052.8, + 22709.9, + 27117.6, + 23429.8, + 28135.8 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "E_errors": { + "median_us": 23105.5, + "runs": [ + 18648.9, + 23105.5, + 21766.8, + 25948.9, + 25936.0 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "F_templates": { + "median_us": 21913.8, + "runs": [ + 20947.5, + 21913.8, + 20203.3, + 25848.1, + 27255.3 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "G_urls": { + "median_us": 24575.5, + "runs": [ + 19805.8, + 25389.5, + 24002.7, + 25421.5, + 24575.5 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "H_response": { + "median_us": 21727.9, + "runs": [ + 18356.0, + 22750.7, + 20723.4, + 23235.1, + 21727.9 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "I_dbadj": { + "median_us": 20914.6, + "runs": [ + 17887.0, + 23207.7, + 19032.2, + 20914.6, + 21174.2 + ], + "status_bytes": [ + 200, + 13021 + ] + }, + "J_critic": { + "median_us": 21068.3, + "runs": [ + 19567.0, + 21068.3, + 22088.0, + 23480.4, + 18747.6 + ], + "status_bytes": [ + 200, + 13021 + ] + } + } +} \ No newline at end of file diff --git a/perf/request-response/results/inprocess-immutable.json b/perf/request-response/results/inprocess-immutable.json new file mode 100644 index 00000000..02865796 --- /dev/null +++ b/perf/request-response/results/inprocess-immutable.json @@ -0,0 +1,178 @@ +{ + "table_json": { + "base": { + "median_us": 7299.6, + "runs": [ + 6723.1, + 7430.2, + 7790.0, + 7299.6, + 7140.8 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "combined": { + "median_us": 1980.9, + "runs": [ + 2145.7, + 1750.1, + 1857.0, + 1980.9, + 2142.7 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "I_dbadj": { + "median_us": 3146.2, + "runs": [ + 2896.0, + 3146.2, + 2750.5, + 3304.6, + 3362.8 + ], + "status_bytes": [ + 200, + 1273 + ] + } + }, + "row_json": { + "base": { + "median_us": 3385.5, + "runs": [ + 3472.8, + 3343.3, + 4027.8, + 3385.5, + 2479.6 + ], + "status_bytes": [ + 200, + 421 + ] + }, + "combined": { + "median_us": 1094.3, + "runs": [ + 1084.0, + 1003.5, + 1158.9, + 1135.3, + 1094.3 + ], + "status_bytes": [ + 200, + 421 + ] + }, + "I_dbadj": { + "median_us": 1877.5, + "runs": [ + 1877.5, + 1790.0, + 1904.6, + 1619.2, + 1930.1 + ], + "status_bytes": [ + 200, + 421 + ] + } + }, + "table_html": { + "base": { + "median_us": 56531.2, + "runs": [ + 60362.7, + 53480.1, + 60845.5, + 54625.8, + 56531.2 + ], + "status_bytes": [ + 200, + 26551 + ] + }, + "combined": { + "median_us": 28022.1, + "runs": [ + 27050.7, + 26873.7, + 28022.1, + 35357.3, + 33434.1 + ], + "status_bytes": [ + 200, + 26552 + ] + }, + "I_dbadj": { + "median_us": 40504.6, + "runs": [ + 40504.6, + 39669.0, + 46714.0, + 39604.1, + 40924.9 + ], + "status_bytes": [ + 200, + 26552 + ] + } + }, + "database_html": { + "base": { + "median_us": 53347.4, + "runs": [ + 47349.1, + 53347.4, + 54882.2, + 49281.6, + 58689.8 + ], + "status_bytes": [ + 200, + 23141 + ] + }, + "combined": { + "median_us": 23781.4, + "runs": [ + 23541.9, + 24287.6, + 23251.2, + 23781.4, + 24122.2 + ], + "status_bytes": [ + 200, + 23141 + ] + }, + "I_dbadj": { + "median_us": 29426.4, + "runs": [ + 28348.9, + 32079.5, + 30833.4, + 29426.4, + 28495.4 + ], + "status_bytes": [ + 200, + 23141 + ] + } + } +} \ No newline at end of file diff --git a/perf/request-response/results/inprocess-light.json b/perf/request-response/results/inprocess-light.json new file mode 100644 index 00000000..b238a68f --- /dev/null +++ b/perf/request-response/results/inprocess-light.json @@ -0,0 +1,2212 @@ +{ + "versions_json": { + "base": { + "median_us": 1935.0, + "runs": [ + 1998.7, + 2269.3, + 1935.0, + 1830.7, + 1717.1 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "combined": { + "median_us": 596.4, + "runs": [ + 622.4, + 638.7, + 544.2, + 596.4, + 544.2 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "A_request_signing": { + "median_us": 1806.5, + "runs": [ + 1812.5, + 2003.2, + 1688.4, + 1806.5, + 1626.6 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "B_routing": { + "median_us": 2026.5, + "runs": [ + 2060.9, + 2012.4, + 2322.9, + 2026.5, + 1786.2 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "C_middleware": { + "median_us": 1874.8, + "runs": [ + 1874.8, + 1673.5, + 1796.9, + 1899.5, + 1886.4 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "D_static": { + "median_us": 2001.0, + "runs": [ + 2165.6, + 2001.0, + 2003.2, + 1786.5, + 1798.0 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "E_errors": { + "median_us": 1947.0, + "runs": [ + 1836.0, + 1947.0, + 1972.2, + 2096.2, + 1612.3 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "F_templates": { + "median_us": 1894.5, + "runs": [ + 1850.7, + 1894.5, + 1943.8, + 1840.5, + 2020.5 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "G_urls": { + "median_us": 1881.1, + "runs": [ + 2180.7, + 1881.1, + 1946.7, + 1840.0, + 1760.0 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "H_response": { + "median_us": 1958.5, + "runs": [ + 1984.9, + 2214.0, + 1790.7, + 1958.5, + 1852.2 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "I_dbadj": { + "median_us": 975.6, + "runs": [ + 968.9, + 995.7, + 930.8, + 975.6, + 1042.5 + ], + "status_bytes": [ + 200, + 1423 + ] + }, + "J_critic": { + "median_us": 1542.0, + "runs": [ + 1511.9, + 1581.2, + 1542.0, + 1471.0, + 1593.2 + ], + "status_bytes": [ + 200, + 1423 + ] + } + }, + "actor_json_cookie": { + "base": { + "median_us": 205.8, + "runs": [ + 240.4, + 205.8, + 253.1, + 202.1, + 177.8 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "combined": { + "median_us": 80.3, + "runs": [ + 78.2, + 81.5, + 85.7, + 79.5, + 80.3 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "A_request_signing": { + "median_us": 98.7, + "runs": [ + 98.7, + 111.1, + 94.9, + 93.5, + 113.6 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "B_routing": { + "median_us": 204.8, + "runs": [ + 285.4, + 183.6, + 204.8, + 207.0, + 193.2 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "C_middleware": { + "median_us": 191.7, + "runs": [ + 197.6, + 159.6, + 171.5, + 191.7, + 211.0 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "D_static": { + "median_us": 217.3, + "runs": [ + 198.1, + 239.1, + 224.0, + 217.3, + 182.4 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "E_errors": { + "median_us": 222.5, + "runs": [ + 412.5, + 195.5, + 222.5, + 198.6, + 222.9 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "F_templates": { + "median_us": 190.4, + "runs": [ + 179.8, + 195.0, + 244.7, + 190.4, + 179.8 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "G_urls": { + "median_us": 224.8, + "runs": [ + 224.8, + 210.4, + 277.1, + 227.9, + 192.3 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "H_response": { + "median_us": 220.8, + "runs": [ + 225.1, + 250.5, + 220.8, + 218.5, + 184.1 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "I_dbadj": { + "median_us": 195.9, + "runs": [ + 220.8, + 195.9, + 180.5, + 190.6, + 230.5 + ], + "status_bytes": [ + 200, + 37 + ] + }, + "J_critic": { + "median_us": 201.1, + "runs": [ + 201.1, + 208.9, + 245.5, + 193.7, + 185.7 + ], + "status_bytes": [ + 200, + 37 + ] + } + }, + "static_css_304": { + "base": { + "median_us": 274.0, + "runs": [ + 330.1, + 274.0, + 275.9, + 258.7, + 254.5 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "combined": { + "median_us": 119.9, + "runs": [ + 119.9, + 135.3, + 125.5, + 112.6, + 117.1 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "A_request_signing": { + "median_us": 177.9, + "runs": [ + 168.7, + 177.9, + 172.4, + 208.4, + 182.2 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "B_routing": { + "median_us": 286.9, + "runs": [ + 286.9, + 330.4, + 320.4, + 255.8, + 283.7 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "C_middleware": { + "median_us": 243.4, + "runs": [ + 232.2, + 225.4, + 249.1, + 243.4, + 261.5 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "D_static": { + "median_us": 261.0, + "runs": [ + 289.6, + 231.3, + 293.2, + 261.0, + 225.7 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "E_errors": { + "median_us": 303.0, + "runs": [ + 303.0, + 390.1, + 288.9, + 359.2, + 262.1 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "F_templates": { + "median_us": 283.3, + "runs": [ + 270.5, + 345.6, + 321.5, + 271.1, + 283.3 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "G_urls": { + "median_us": 268.4, + "runs": [ + 336.8, + 320.1, + 253.7, + 259.5, + 268.4 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "H_response": { + "median_us": 309.6, + "runs": [ + 306.9, + 314.4, + 328.8, + 309.6, + 274.3 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "I_dbadj": { + "median_us": 277.7, + "runs": [ + 281.4, + 277.7, + 261.7, + 275.6, + 324.1 + ], + "status_bytes": [ + 304, + 0 + ] + }, + "J_critic": { + "median_us": 289.3, + "runs": [ + 293.7, + 281.1, + 289.3, + 311.1, + 280.0 + ], + "status_bytes": [ + 304, + 0 + ] + } + }, + "favicon": { + "base": { + "median_us": 896.9, + "runs": [ + 896.9, + 1023.6, + 1059.2, + 731.5, + 821.9 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "combined": { + "median_us": 31.3, + "runs": [ + 35.5, + 30.6, + 31.1, + 34.6, + 31.3 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "A_request_signing": { + "median_us": 757.7, + "runs": [ + 757.7, + 795.8, + 842.9, + 710.9, + 680.9 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "B_routing": { + "median_us": 967.8, + "runs": [ + 1090.7, + 967.8, + 935.7, + 890.3, + 974.8 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "C_middleware": { + "median_us": 806.5, + "runs": [ + 647.8, + 1004.9, + 806.5, + 769.3, + 840.0 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "D_static": { + "median_us": 73.6, + "runs": [ + 74.8, + 71.3, + 73.6, + 70.2, + 74.7 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "E_errors": { + "median_us": 852.4, + "runs": [ + 732.9, + 852.4, + 973.8, + 875.5, + 843.8 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "F_templates": { + "median_us": 784.1, + "runs": [ + 784.1, + 765.2, + 830.7, + 768.8, + 793.5 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "G_urls": { + "median_us": 868.8, + "runs": [ + 1072.3, + 969.7, + 868.8, + 783.1, + 678.4 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "H_response": { + "median_us": 845.9, + "runs": [ + 927.4, + 961.0, + 712.6, + 845.9, + 815.4 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "I_dbadj": { + "median_us": 806.8, + "runs": [ + 906.1, + 1012.1, + 772.9, + 794.8, + 806.8 + ], + "status_bytes": [ + 200, + 208 + ] + }, + "J_critic": { + "median_us": 883.4, + "runs": [ + 816.9, + 912.3, + 991.4, + 834.3, + 883.4 + ], + "status_bytes": [ + 200, + 208 + ] + } + }, + "redirect_dash": { + "base": { + "median_us": 119.8, + "runs": [ + 119.8, + 114.9, + 133.3, + 122.3, + 114.9 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "combined": { + "median_us": 39.4, + "runs": [ + 44.8, + 37.3, + 37.0, + 53.7, + 39.4 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "A_request_signing": { + "median_us": 72.8, + "runs": [ + 64.9, + 73.8, + 72.8, + 82.6, + 63.9 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "B_routing": { + "median_us": 99.0, + "runs": [ + 114.4, + 109.1, + 99.0, + 86.6, + 86.1 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "C_middleware": { + "median_us": 111.5, + "runs": [ + 95.6, + 119.9, + 101.4, + 130.5, + 111.5 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "D_static": { + "median_us": 125.3, + "runs": [ + 121.1, + 142.5, + 217.4, + 115.9, + 125.3 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "E_errors": { + "median_us": 126.8, + "runs": [ + 117.7, + 121.7, + 138.3, + 126.8, + 170.1 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "F_templates": { + "median_us": 124.5, + "runs": [ + 143.5, + 154.0, + 124.5, + 113.2, + 124.1 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "G_urls": { + "median_us": 123.2, + "runs": [ + 136.7, + 118.1, + 118.3, + 136.6, + 123.2 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "H_response": { + "median_us": 126.0, + "runs": [ + 128.5, + 126.0, + 118.0, + 155.6, + 124.9 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "I_dbadj": { + "median_us": 130.7, + "runs": [ + 126.8, + 130.7, + 148.3, + 119.1, + 156.4 + ], + "status_bytes": [ + 301, + 0 + ] + }, + "J_critic": { + "median_us": 121.5, + "runs": [ + 121.5, + 182.0, + 117.3, + 128.0, + 112.8 + ], + "status_bytes": [ + 301, + 0 + ] + } + }, + "trailing_slash": { + "base": { + "median_us": 102.2, + "runs": [ + 100.9, + 106.8, + 113.9, + 102.2, + 98.2 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "combined": { + "median_us": 50.0, + "runs": [ + 50.3, + 50.0, + 48.5, + 69.9, + 50.0 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "A_request_signing": { + "median_us": 64.1, + "runs": [ + 59.5, + 64.1, + 65.1, + 69.0, + 57.2 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "B_routing": { + "median_us": 110.7, + "runs": [ + 103.8, + 110.7, + 169.9, + 118.8, + 97.3 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "C_middleware": { + "median_us": 92.3, + "runs": [ + 90.7, + 95.9, + 92.3, + 86.5, + 95.0 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "D_static": { + "median_us": 106.5, + "runs": [ + 106.5, + 97.1, + 96.9, + 120.2, + 107.2 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "E_errors": { + "median_us": 104.9, + "runs": [ + 120.3, + 104.9, + 132.2, + 100.4, + 104.1 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "F_templates": { + "median_us": 104.9, + "runs": [ + 108.3, + 121.2, + 102.6, + 102.6, + 104.9 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "G_urls": { + "median_us": 113.0, + "runs": [ + 113.0, + 109.7, + 113.6, + 112.3, + 123.5 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "H_response": { + "median_us": 108.2, + "runs": [ + 108.2, + 136.2, + 104.5, + 102.8, + 128.3 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "I_dbadj": { + "median_us": 104.4, + "runs": [ + 104.4, + 102.0, + 101.1, + 104.5, + 130.9 + ], + "status_bytes": [ + 302, + 0 + ] + }, + "J_critic": { + "median_us": 101.8, + "runs": [ + 104.5, + 101.8, + 98.3, + 98.8, + 158.9 + ], + "status_bytes": [ + 302, + 0 + ] + } + }, + "not_found": { + "base": { + "median_us": 1508.6, + "runs": [ + 1521.0, + 1513.8, + 1430.6, + 1508.6, + 1416.0 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "combined": { + "median_us": 483.8, + "runs": [ + 464.8, + 595.0, + 460.2, + 483.8, + 500.0 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "A_request_signing": { + "median_us": 1359.1, + "runs": [ + 1359.1, + 1400.9, + 1349.6, + 1465.8, + 1336.5 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "B_routing": { + "median_us": 1546.1, + "runs": [ + 1450.6, + 1546.1, + 1682.1, + 1565.0, + 1412.9 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "C_middleware": { + "median_us": 1609.9, + "runs": [ + 1645.7, + 1496.9, + 1523.2, + 1609.9, + 1645.0 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "D_static": { + "median_us": 793.3, + "runs": [ + 832.3, + 793.3, + 771.4, + 811.8, + 774.7 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "E_errors": { + "median_us": 1208.4, + "runs": [ + 1190.7, + 1245.1, + 1208.4, + 1374.9, + 1179.7 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "F_templates": { + "median_us": 1549.1, + "runs": [ + 1549.1, + 1497.1, + 1562.1, + 1500.2, + 1561.9 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "G_urls": { + "median_us": 1500.1, + "runs": [ + 1860.5, + 1491.7, + 1500.1, + 1586.1, + 1452.4 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "H_response": { + "median_us": 1542.0, + "runs": [ + 1497.0, + 1992.7, + 1437.2, + 1542.0, + 1595.4 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "I_dbadj": { + "median_us": 1537.4, + "runs": [ + 1392.7, + 1537.4, + 1373.7, + 1570.6, + 1623.1 + ], + "status_bytes": [ + 404, + 2239 + ] + }, + "J_critic": { + "median_us": 1464.6, + "runs": [ + 1464.6, + 1477.8, + 1515.2, + 1446.0, + 1429.2 + ], + "status_bytes": [ + 404, + 2239 + ] + } + }, + "post_csrf_json": { + "base": { + "median_us": 117.8, + "runs": [ + 127.2, + 145.4, + 117.8, + 112.9, + 116.0 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "combined": { + "median_us": 57.7, + "runs": [ + 55.8, + 60.0, + 55.0, + 57.7, + 95.2 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "A_request_signing": { + "median_us": 65.9, + "runs": [ + 81.0, + 65.9, + 65.6, + 69.8, + 65.8 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "B_routing": { + "median_us": 114.8, + "runs": [ + 114.4, + 114.8, + 128.5, + 118.5, + 110.6 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "C_middleware": { + "median_us": 122.2, + "runs": [ + 122.2, + 105.3, + 124.3, + 106.4, + 123.5 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "D_static": { + "median_us": 119.6, + "runs": [ + 112.1, + 128.9, + 142.6, + 117.6, + 119.6 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "E_errors": { + "median_us": 118.1, + "runs": [ + 130.4, + 118.1, + 126.6, + 116.0, + 111.7 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "F_templates": { + "median_us": 117.5, + "runs": [ + 122.7, + 117.5, + 114.5, + 112.0, + 122.1 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "G_urls": { + "median_us": 120.5, + "runs": [ + 112.7, + 126.7, + 117.1, + 155.0, + 120.5 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "H_response": { + "median_us": 126.0, + "runs": [ + 126.0, + 163.2, + 113.4, + 129.2, + 115.2 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "I_dbadj": { + "median_us": 129.9, + "runs": [ + 136.3, + 133.7, + 119.8, + 129.0, + 129.9 + ], + "status_bytes": [ + 405, + 93 + ] + }, + "J_critic": { + "median_us": 114.9, + "runs": [ + 148.2, + 114.9, + 109.4, + 132.8, + 109.2 + ], + "status_bytes": [ + 405, + 93 + ] + } + }, + "static_css": { + "base": { + "median_us": 4140.6, + "runs": [ + 4140.6, + 4412.1, + 4072.1, + 3609.9, + 4400.7 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "combined": { + "median_us": 147.0, + "runs": [ + 168.4, + 140.7, + 142.3, + 147.0, + 178.7 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "A_request_signing": { + "median_us": 4156.3, + "runs": [ + 4254.3, + 4156.3, + 3717.1, + 4619.8, + 3636.8 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "B_routing": { + "median_us": 4431.9, + "runs": [ + 4839.0, + 4300.7, + 4841.4, + 4431.9, + 3483.3 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "C_middleware": { + "median_us": 4159.3, + "runs": [ + 4927.7, + 4159.3, + 3933.7, + 3718.6, + 4436.2 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "D_static": { + "median_us": 228.0, + "runs": [ + 378.0, + 257.7, + 203.3, + 216.0, + 228.0 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "E_errors": { + "median_us": 4064.1, + "runs": [ + 4596.9, + 4064.1, + 4332.4, + 4041.6, + 3638.2 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "F_templates": { + "median_us": 4281.8, + "runs": [ + 4390.4, + 3883.9, + 4406.5, + 4032.0, + 4281.8 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "G_urls": { + "median_us": 4267.3, + "runs": [ + 4267.3, + 4551.5, + 4219.8, + 4436.8, + 4095.1 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "H_response": { + "median_us": 4120.0, + "runs": [ + 4001.6, + 4178.7, + 3888.7, + 4120.0, + 4177.5 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "I_dbadj": { + "median_us": 3997.6, + "runs": [ + 4287.2, + 4074.6, + 3631.0, + 3997.6, + 3951.6 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "J_critic": { + "median_us": 3989.3, + "runs": [ + 4166.9, + 3989.3, + 3992.4, + 3928.8, + 3943.4 + ], + "status_bytes": [ + 200, + 83458 + ] + } + }, + "static_css_hash": { + "base": { + "median_us": 4386.0, + "runs": [ + 4427.2, + 4458.5, + 4378.2, + 4386.0, + 4235.6 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "combined": { + "median_us": 200.0, + "runs": [ + 159.0, + 205.3, + 197.1, + 200.0, + 260.8 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "A_request_signing": { + "median_us": 3961.6, + "runs": [ + 4789.2, + 4710.3, + 3747.3, + 3765.9, + 3961.6 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "B_routing": { + "median_us": 4498.0, + "runs": [ + 5714.9, + 4498.0, + 4551.3, + 4288.2, + 3952.2 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "C_middleware": { + "median_us": 4471.6, + "runs": [ + 4687.9, + 4471.6, + 4333.6, + 4121.4, + 4663.1 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "D_static": { + "median_us": 228.4, + "runs": [ + 328.0, + 219.0, + 262.0, + 228.4, + 218.0 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "E_errors": { + "median_us": 4423.9, + "runs": [ + 4857.5, + 4220.6, + 4641.4, + 4418.6, + 4423.9 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "F_templates": { + "median_us": 4663.6, + "runs": [ + 4758.9, + 4816.0, + 4508.3, + 4215.3, + 4663.6 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "G_urls": { + "median_us": 4568.6, + "runs": [ + 4357.8, + 4613.4, + 4427.0, + 4568.6, + 4799.3 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "H_response": { + "median_us": 4814.0, + "runs": [ + 4279.7, + 4912.7, + 4814.0, + 4678.4, + 4842.1 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "I_dbadj": { + "median_us": 4457.0, + "runs": [ + 5012.2, + 4651.9, + 4441.9, + 4292.8, + 4457.0 + ], + "status_bytes": [ + 200, + 83458 + ] + }, + "J_critic": { + "median_us": 4217.7, + "runs": [ + 4217.7, + 4407.5, + 4287.9, + 3938.5, + 4191.0 + ], + "status_bytes": [ + 200, + 83458 + ] + } + }, + "versions_html": { + "base": { + "median_us": 3419.7, + "runs": [ + 3275.4, + 3747.9, + 3696.7, + 3419.7, + 3173.3 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "combined": { + "median_us": 1011.2, + "runs": [ + 1162.5, + 1189.1, + 1011.2, + 984.3, + 993.9 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "A_request_signing": { + "median_us": 3540.3, + "runs": [ + 3540.3, + 4355.4, + 3599.3, + 3408.4, + 3324.7 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "B_routing": { + "median_us": 3593.6, + "runs": [ + 3730.0, + 3352.2, + 3829.4, + 3593.6, + 3484.7 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "C_middleware": { + "median_us": 3414.7, + "runs": [ + 3935.5, + 3345.8, + 3630.2, + 3414.7, + 3090.5 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "D_static": { + "median_us": 3225.2, + "runs": [ + 3225.2, + 2473.1, + 3223.9, + 3231.9, + 3854.4 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "E_errors": { + "median_us": 3365.4, + "runs": [ + 3373.5, + 3124.4, + 4223.7, + 3365.4, + 3247.0 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "F_templates": { + "median_us": 3300.5, + "runs": [ + 3346.4, + 3197.3, + 3440.1, + 3251.6, + 3300.5 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "G_urls": { + "median_us": 3367.6, + "runs": [ + 3367.6, + 3279.5, + 3106.8, + 3750.6, + 3654.9 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "H_response": { + "median_us": 3669.1, + "runs": [ + 3669.1, + 3361.1, + 3840.8, + 4380.3, + 3361.6 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "I_dbadj": { + "median_us": 2500.4, + "runs": [ + 2671.4, + 2517.9, + 2467.6, + 2489.4, + 2500.4 + ], + "status_bytes": [ + 200, + 4776 + ] + }, + "J_critic": { + "median_us": 2932.9, + "runs": [ + 2949.2, + 2957.0, + 2932.9, + 2820.0, + 2873.9 + ], + "status_bytes": [ + 200, + 4776 + ] + } + }, + "row_json": { + "base": { + "median_us": 3031.1, + "runs": [ + 2679.5, + 3031.1, + 3237.9, + 3549.8, + 2890.0 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "combined": { + "median_us": 1893.9, + "runs": [ + 1981.3, + 2303.9, + 1797.1, + 1893.9, + 1795.5 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "A_request_signing": { + "median_us": 2949.9, + "runs": [ + 2887.2, + 3516.6, + 3348.5, + 2949.9, + 2842.1 + ], + "status_bytes": [ + 200, + 421 + ] + }, + "B_routing": { + "median_us": 2876.3, + "runs": [ + 2876.3, + 2643.3, + 3608.3, + 3425.7, + 2627.2 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "C_middleware": { + "median_us": 2904.9, + "runs": [ + 3171.6, + 2904.9, + 2891.3, + 3279.7, + 2763.3 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "D_static": { + "median_us": 3117.4, + "runs": [ + 3504.8, + 3024.7, + 4089.2, + 3117.4, + 2991.1 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "E_errors": { + "median_us": 3083.0, + "runs": [ + 3083.0, + 2690.6, + 3481.0, + 2790.0, + 3147.9 + ], + "status_bytes": [ + 200, + 421 + ] + }, + "F_templates": { + "median_us": 2920.7, + "runs": [ + 3211.8, + 3231.6, + 2920.7, + 2849.0, + 2880.8 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "G_urls": { + "median_us": 3136.9, + "runs": [ + 2921.2, + 3280.6, + 2970.9, + 3386.7, + 3136.9 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "H_response": { + "median_us": 3028.9, + "runs": [ + 3148.3, + 3028.9, + 2905.4, + 3518.1, + 2750.7 + ], + "status_bytes": [ + 200, + 419 + ] + }, + "I_dbadj": { + "median_us": 2814.4, + "runs": [ + 3108.4, + 3066.7, + 2636.0, + 2704.7, + 2814.4 + ], + "status_bytes": [ + 200, + 420 + ] + }, + "J_critic": { + "median_us": 2537.2, + "runs": [ + 2912.5, + 2537.2, + 2451.1, + 2516.6, + 2584.2 + ], + "status_bytes": [ + 200, + 419 + ] + } + }, + "table_json": { + "base": { + "median_us": 6793.8, + "runs": [ + 6500.0, + 7041.6, + 6877.5, + 6793.8, + 6056.1 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "combined": { + "median_us": 3635.0, + "runs": [ + 3753.6, + 4162.8, + 3576.0, + 3635.0, + 3028.9 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "A_request_signing": { + "median_us": 6046.1, + "runs": [ + 6691.3, + 6046.1, + 7180.6, + 5700.4, + 5745.3 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "B_routing": { + "median_us": 6435.3, + "runs": [ + 6949.0, + 5846.5, + 6435.3, + 6866.4, + 6331.6 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "C_middleware": { + "median_us": 6367.8, + "runs": [ + 6380.4, + 6201.5, + 6443.0, + 6367.8, + 5616.1 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "D_static": { + "median_us": 6948.9, + "runs": [ + 7034.3, + 6043.5, + 8047.2, + 6533.7, + 6948.9 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "E_errors": { + "median_us": 6268.7, + "runs": [ + 6268.7, + 5769.5, + 6662.4, + 6279.2, + 5968.4 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "F_templates": { + "median_us": 6246.7, + "runs": [ + 7111.9, + 6587.0, + 6246.7, + 6026.6, + 5842.9 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "G_urls": { + "median_us": 6093.0, + "runs": [ + 6007.3, + 7222.4, + 5851.6, + 6812.8, + 6093.0 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "H_response": { + "median_us": 6897.2, + "runs": [ + 7700.9, + 6897.2, + 6149.2, + 6952.2, + 6760.0 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "I_dbadj": { + "median_us": 5327.3, + "runs": [ + 5670.1, + 5327.3, + 4913.4, + 4736.1, + 5343.3 + ], + "status_bytes": [ + 200, + 1273 + ] + }, + "J_critic": { + "median_us": 5597.7, + "runs": [ + 6237.6, + 5984.1, + 5597.7, + 5468.5, + 5518.7 + ], + "status_bytes": [ + 200, + 1273 + ] + } + } +} \ No newline at end of file diff --git a/perf/request-response/results/inprocess-summary.txt b/perf/request-response/results/inprocess-summary.txt new file mode 100644 index 00000000..1d48cdd9 --- /dev/null +++ b/perf/request-response/results/inprocess-summary.txt @@ -0,0 +1,30 @@ +endpoint base combinedA_request_signing B_routing C_middleware D_static E_errors F_templates G_urls H_response I_dbadj J_critic +versions_json 1935.0us 596.4us -69% 1806.5us -7% 2026.5us +5% 1874.8us -3% 2001.0us +3% 1947.0us +1% 1894.5us -2% 1881.1us -3% 1958.5us +1% 975.6us -50% 1542.0us -20% +actor_json_cookie 205.8us 80.3us -61% 98.7us -52% 204.8us -0% 191.7us -7% 217.3us +6% 222.5us +8% 190.4us -7% 224.8us +9% 220.8us +7% 195.9us -5% 201.1us -2% +static_css_304 274.0us 119.9us -56% 177.9us -35% 286.9us +5% 243.4us -11% 261.0us -5% 303.0us +11% 283.3us +3% 268.4us -2% 309.6us +13% 277.7us +1% 289.3us +6% +favicon 896.9us 31.3us -97% 757.7us -16% 967.8us +8% 806.5us -10% 73.6us -92% 852.4us -5% 784.1us -13% 868.8us -3% 845.9us -6% 806.8us -10% 883.4us -2% +redirect_dash 119.8us 39.4us -67% 72.8us -39% 99.0us -17% 111.5us -7% 125.3us +5% 126.8us +6% 124.5us +4% 123.2us +3% 126.0us +5% 130.7us +9% 121.5us +1% +trailing_slash 102.2us 50.0us -51% 64.1us -37% 110.7us +8% 92.3us -10% 106.5us +4% 104.9us +3% 104.9us +3% 113.0us +11% 108.2us +6% 104.4us +2% 101.8us -0% +not_found 1508.6us 483.8us -68% 1359.1us -10% 1546.1us +2% 1609.9us +7% 793.3us -47% 1208.4us -20% 1549.1us +3% 1500.1us -1% 1542.0us +2% 1537.4us +2% 1464.6us -3% +post_csrf_json 117.8us 57.7us -51% 65.9us -44% 114.8us -3% 122.2us +4% 119.6us +2% 118.1us +0% 117.5us -0% 120.5us +2% 126.0us +7% 129.9us +10% 114.9us -2% +static_css 4140.6us 147.0us -96% 4156.3us +0% 4431.9us +7% 4159.3us +0% 228.0us -94% 4064.1us -2% 4281.8us +3% 4267.3us +3% 4120.0us -0% 3997.6us -3% 3989.3us -4% +static_css_hash 4386.0us 200.0us -95% 3961.6us -10% 4498.0us +3% 4471.6us +2% 228.4us -95% 4423.9us +1% 4663.6us +6% 4568.6us +4% 4814.0us +10% 4457.0us +2% 4217.7us -4% +versions_html 3419.7us 1011.2us -70% 3540.3us +4% 3593.6us +5% 3414.7us -0% 3225.2us -6% 3365.4us -2% 3300.5us -3% 3367.6us -2% 3669.1us +7% 2500.4us -27% 2932.9us -14% +row_json 3031.1us 1893.9us -38% 2949.9us -3% 2876.3us -5% 2904.9us -4% 3117.4us +3% 3083.0us +2% 2920.7us -4% 3136.9us +3% 3028.9us -0% 2814.4us -7% 2537.2us -16% +table_json 6793.8us 3635.0us -46% 6046.1us -11% 6435.3us -5% 6367.8us -6% 6948.9us +2% 6268.7us -8% 6246.7us -8% 6093.0us -10% 6897.2us +2% 5327.3us -22% 5597.7us -18% +WARNING: status/bytes differ for row_json: {'base': (200, 420), 'combined': (200, 420), 'A_request_signing': (200, 421), 'B_routing': (200, 420), 'C_middleware': (200, 420), 'D_static': (200, 420), 'E_errors': (200, 421), 'F_templates': (200, 420), 'G_urls': (200, 420), 'H_response': (200, 419), 'I_dbadj': (200, 420), 'J_critic': (200, 419)} +endpoint base combinedA_request_signing B_routing C_middleware D_static E_errors F_templates G_urls H_response I_dbadj J_critic +index_html 62239.6us 49837.1us -20% 53709.0us -14% 55989.5us -10% 53208.8us -15% 60280.6us -3% 56233.2us -10% 57762.9us -7% 56583.0us -9% 59315.1us -5% 59276.0us -5% 57427.3us -8% +index_json 53657.9us 47492.6us -11% 56852.3us +6% 58082.3us +8% 47178.7us -12% 55826.2us +4% 52773.8us -2% 58680.3us +9% 57226.4us +7% 62428.9us +16% 53976.3us +1% 54827.2us +2% +database_html 65521.8us 55694.0us -15% 61789.9us -6% 64556.0us -1% 60600.5us -8% 62105.6us -5% 66711.1us +2% 61229.7us -7% 62698.2us -4% 66549.3us +2% 66807.6us +2% 66002.7us +1% +table_html 58207.3us 42954.1us -26% 58564.8us +1% 57928.2us -0% 56490.5us -3% 55819.1us -4% 58918.9us +1% 54376.2us -7% 58430.1us +0% 62191.9us +7% 55630.1us -4% 54872.0us -6% +table_html_100 48844.6us 32287.4us -34% 50922.8us +4% 45681.2us -6% 46526.3us -5% 46093.2us -6% 45938.2us -6% 40864.0us -16% 49289.5us +1% 50459.4us +3% 45203.7us -7% 49174.1us +1% +table_csv_1000 21580.3us 17162.9us -20% 21607.9us +0% 22777.9us +6% 22975.4us +6% 23429.8us +9% 23105.5us +7% 21913.8us +2% 24575.5us +14% 21727.9us +1% 20914.6us -3% 21068.3us -2% +WARNING: status/bytes differ for table_html: {'base': (200, 26552), 'combined': (200, 26552), 'A_request_signing': (200, 26552), 'B_routing': (200, 26552), 'C_middleware': (200, 26552), 'D_static': (200, 26552), 'E_errors': (200, 26552), 'F_templates': (200, 26552), 'G_urls': (200, 26552), 'H_response': (200, 26552), 'I_dbadj': (200, 26551), 'J_critic': (200, 26552)} +WARNING: status/bytes differ for table_html_100: {'base': (200, 66670), 'combined': (200, 66670), 'A_request_signing': (200, 66670), 'B_routing': (200, 66670), 'C_middleware': (200, 66670), 'D_static': (200, 66670), 'E_errors': (200, 66670), 'F_templates': (200, 66670), 'G_urls': (200, 66670), 'H_response': (200, 66669), 'I_dbadj': (200, 66670), 'J_critic': (200, 66670)} +endpoint base combined I_dbadj +table_json 7299.6us 1980.9us -73% 3146.2us -57% +row_json 3385.5us 1094.3us -68% 1877.5us -45% +table_html 56531.2us 28022.1us -50% 40504.6us -28% +database_html 53347.4us 23781.4us -55% 29426.4us -45% +WARNING: status/bytes differ for table_html: {'base': (200, 26551), 'combined': (200, 26552), 'I_dbadj': (200, 26552)}