Skip to content

Record the PyAthena 4.2.0 benchmark runs - #1310

Merged
laughingman7743 merged 5 commits into
masterfrom
docs/1288-benchmark-results-4.2.0
Oct 19, 2026
Merged

laughingman7743 merged 5 commits into
masterfrom
docs/1288-benchmark-results-4.2.0

Conversation

@laughingman7743

@laughingman7743 laughingman7743 commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

WHAT

Records the combined benchmark of 9e99caa8, the commit planned for PyAthena 4.2.0, run on 2026-10-19. 4.2.0 was tagged on baee716c, which differs from it only in one test file (#1311), so the measured package code is that of 4.2.0.

The tables are generated from the recovered reports with a script outside the repository; the 4.1.0 values it reads match the published 4.1.0 entry in all 214 case rows.

WHY

Part of #1288: measure the 4.2.0 release candidate before tagging, then record the results.

Closes #1159: with 40 workers, aio writes and copies run at the sync throughput, and their thread counts follow max_workers; for objects of 256 MiB and larger, aio reads and 8-worker writes run at 0.95 to 1.06 times sync, and 8-worker copies at 1.36 to 1.49 times. Against 4.1.0, the 8-worker aio writes of 256 MiB fell from 1.08–1.09 to 0.95 times sync, and the 64 MiB aio reads ran slower in three of four settings; the entry records both. The default decision is recorded.
Closes #1162: chunked reads at 100 million rows were measured.
Closes #1287: the temporal conversion of 4.2.0 was measured; the improvement candidates are #1308.
Closes #61: chunked reads keep results larger than memory at about 1 GiB at 100 million rows.
Closes #254: awsathena+pandas with unload=true reads 1 million flat rows in 7.02 seconds (Wrangler CSV 12.37, UNLOAD 3.89) and 10 million in 50.71 seconds (Wrangler CSV 108.88, UNLOAD 23.44); the remaining difference comes from read_sql_query() building the DataFrame from rows.

TEST

  • Tested commit: this branch's head.
  • just docs lint and mise exec -- markdownlint-cli2 benchmarks/history/2026-10-19-*.md benchmarks/RESULTS.md: 0 errors (the docs lint glob covers benchmarks/*.md but not benchmarks/history/).
  • just lint: passed.
  • Relative links and anchors in the new and changed files checked by hand.
  • Markdown only: the Test workflow ignores **.md paths, so no AWS job runs; the Benchmark tooling workflow runs for benchmarks/**.

🤖 Generated with Claude Code

laughingman7743 and others added 2 commits October 19, 2026 22:01
Add the history entries of the cursor run and the filesystem suite of
the 4.2.0 release commit 9e99caa, and update RESULTS.md: the index,
and the recommendations from the new measurements, including chunked
reads of 100 million rows, read_sql_query() through the pandas and
Polars drivers, and AioS3FileSystem for asyncio applications.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

The release run of 9e99caa failed a test on Python 3.13, fixed in #1311,
so v4.2.0 was tagged on its merge commit, which differs from the measured
commit only in that test file. Also say that the snapshots of the lost
preparation were removed before the stack was deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Cursor benchmark of PyAthena 4.2.0 on 2026-10-19

This entry records a bounded large-data run of the [benchmark harness](../README.md) at commit [`9e99caa8`](https://github.com/pyathena-dev/PyAthena/tree/9e99caa84857f8d29abe63435461fc412e040c43/benchmarks), the commit planned for the PyAthena 4.2.0 release.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review round 1 (content and data correctness). Base 9e99caa84857f8d29abe63435461fc412e040c43; initial head ce8212dbead1da7575677fdc6d3a089bf5b7b32f, repaired at 32a00e2129f39b8956e91606952dac3d9975ec0e. Result: FINDINGS (2), repaired in 32a00e2.

Inventory: the two new history entries (prose, generated tables, links) and the RESULTS.md index and recommendations. Numbers were spot-checked against my own scripts on the recovered reports: the 100 million rows table, the temporal table, the read_sql_query() totals, the Polars 1.44.2 / 2.0.0 peaks, the filesystem ratios and thread counts, and the REST median of 226.99 s from the three merged single-repetition jobs (224.7 / 227.0 / 231.3).

Findings:

  1. Both entries and the RESULTS index called 9e99caa8 the release commit, "tagged v4.2.0". The release run of 9e99caa8 failed a Python 3.13 test, which Count only the file's ResourceWarning in test_open_async_cancelled #1311 fixed, so v4.2.0 is tagged on baee716c. That commit differs from 9e99caa8 only in tests/pyathena/filesystem/test_s3_async.py; verified with git diff --stat 9e99caa8 baee716c. Repaired: the titles and introductions now say "the commit planned for the PyAthena 4.2.0 release" and name baee716c and Count only the file's ResourceWarning in test_open_async_cancelled #1311; the index rows say "4.2.0 (tagged on baee716c, which differs only in one test file)". The version-string sentence now gives the same reason as the 4.1.0 entry.
  2. The run record said the snapshot tables of the lost preparation "were removed when the stack was deleted". They were removed by hand (Glue batch-delete-table and s3 rm of runs/c71b3f72…/) before delete-stack. Repaired.

Verified, no change needed: all ten host records list us-west-2d and lifecycle spot; no workflow ran between 08:52 and 12:05 UTC (gh run list), and the database sweep ran from 08:38 to 08:39 UTC.

Validation after the repair: just docs lint (27 files) and markdownlint on the two history files (29 files): 0 errors.

Comment thread benchmarks/RESULTS.md
| A Wrangler-like pandas read | PandasCursor | 10 million flat rows in 19.0 seconds through UNLOAD and 74.4 seconds through CSV, against 21.4 and 95.3 seconds for Wrangler |
| A DataFrame or Table of a large result | `unload=True` with ArrowCursor, PolarsCursor, or PandasCursor | 10 million flat rows in 18.2 to 19.8 seconds through UNLOAD, against 55.2 to 84.2 seconds through CSV; nested rows in 33.8 to 36.4 seconds with ArrowCursor, PolarsCursor, or PandasCursor with pyarrow dtypes, against 211.3 to 280.3 seconds through CSV |
| A pandas DataFrame with nested columns | PandasCursor with `unload=True` and `dtype_backend="pyarrow"` | 10 million nested rows in 34.2 seconds and 5,820 MiB, against 88.0 seconds and 19,498 MiB with the default dtypes |
| Bounded memory, including results larger than memory | `chunksize` with PandasCursor or PolarsCursor, with CSV or UNLOAD results | 100 million flat rows, about 29 GB of CSV, at 462 to 1,051 MiB with 100,000-row chunks; 10 million rows at 362 to 917 MiB with 10,000-row chunks, against 3,765 to 19,498 MiB without chunks; larger chunks use more memory |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review round 2 (claims, readers, operations). Base 9e99caa84857f8d29abe63435461fc412e040c43, head 32a00e2129f39b8956e91606952dac3d9975ec0e. Result: CLEAN after round one's repair. The PR body's first sentence was corrected in the same way (it said "tagged v4.2.0").

Claims checked against evidence:

Limits: the generated tables were not re-derived row by row. The fork's renderer cross-checked its 4.1.0 reader against all 214 published 4.1.0 case rows, and I spot-checked the sections listed in round one.

…GH-1288)

The 64 MiB aio reads ran slower than in the 4.1.0 run in three of four
settings, the temporal UNLOAD reads through read_sql_query() took longer
than the native reads, and the xxlarge CSV execution time came from the
unchunked trial instead of the chunked cases that the caption names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For the 6 GiB object, sync wrote 1,050 to 1,074 MiB/s and aio 1,047 to 1,052 MiB/s (0.98 to 1.00 times sync), against 0.75 to 0.77 times in the 4.1.0 run; for the 1 GiB object, 0.97 to 0.99 times, against 0.81 to 0.83.
Sync copied the 6 GiB object at 1,835 to 1,879 MiB/s and aio at 1,889 to 1,925 MiB/s (1.01 to 1.05 times), against 521 to 540 MiB/s (0.30 to 0.35 times) in the 4.1.0 run.
With the 1 GiB and 6 GiB objects, the aio processes peaked at 52 to 53 threads, against 24 to 25 in the 4.1.0 run and 51 for sync, so their thread counts follow `max_workers`.
2. **Reads of 256 MiB and larger and the 8-worker cases did not regress.**

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review (relayed). Reviewer: Codex CLI 0.163.0 (codex exec -s read-only, default model). Static review with read-only access to the recovered raw reports; no edits, builds, or network. Reviewed head 32a00e2129f39b8956e91606952dac3d9975ec0e, base 9e99caa84857f8d29abe63435461fc412e040c43. Coverage: the three changed files, the environment and selection records, the findings and recommendations, all 367 complete-case rows, the Polars comparisons, the temporal and 100-million-row tables, the 4.1.0 comparisons, and the filesystem tables. Relative links and anchors resolve. Result: FINDINGS (3), all verified against the raw reports and repaired in 5185fd69a435810f2430fc7c5e8bf30f987874c0. The reviewer could not establish the pre-queue loss and cleanup or the workflow-exclusion claims from the supplied artifacts. Both are verified in self-review round 1 (gh run list, AWS cleanup log).

P2, finding 1 of 3. "Reads and the 8-worker cases did not regress" overreached. The 64 MiB aio reads ran at 253 / 239 / 230 MiB/s against 304 / 309 / 312 MiB/s in the 4.1.0 run (settings 8/10, 8/40, 40/10), 17 to 26% slower; the fourth setting, 40/40, was faster (266 against 202). Verified with fs_table.py on both recovered runs.
Repair: the finding now covers reads of 256 MiB and larger, and adds the 64 MiB aio and sync read ranges for both runs, with no cause claimed. The PR body's #1159 sentence is qualified in the same way. This does not change the #1159 done-when, which names the 1 GiB and 6 GiB 40-worker cases plus reads and 8-worker cases generally; the 64 MiB transfers already had no consistent order in the 4.1.0 run (#685).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review of the repair 32a00e21..5185fd69 (rounds 1 and 2, narrow).

Round 1 (content): the new numbers were re-derived from the raw reports. 64 MiB reads: aio 230–266 vs 202–312 MiB/s, sync 283–321 vs 215–274 MiB/s. The temporal read_sql_query() UNLOAD deltas are +0.67 to +0.97 s for the pandas and Polars drivers (both Polars environments) and +2.65 to +2.75 s for Arrow. The xxlarge chunked CSV median is 318.645 s. just docs lint and markdownlint on the history files: 0 errors. CLEAN.

Round 2 (claims): searched the changed files and the PR body for other statements that the repair makes inaccurate. The RESULTS AioS3FileSystem row cites only 1 GiB reads and the 40- and 8-worker large-object cases, so it stays accurate. The PR body's #1159 sentence was updated. No cause is claimed for the 64 MiB difference. CLEAN.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent follow-up 1 (relayed). Codex CLI 0.163.0, read-only; reviewed 32a00e21..5185fd69 against the raw reports. Result: FINDINGS (1). Repairs 2 and 3 are correct: the native temporal differences stay within 0.264 s, the SQLAlchemy ranges match, and the chunked xxlarge median is 318.645 s. Repair 1 was incomplete: the heading still said "the 8-worker cases did not regress", but the 64 MiB aio reads with 8 workers fell 304.12 → 253.38 (8/10) and 309.22 → 238.73 MiB/s (8/40).

Repair in 08175179: the heading now reads "Reads and 8-worker transfers of 256 MiB and larger did not regress.", which matches the body sentences (1 GiB reads, 8-worker writes from 256 MiB up, 6 GiB copies). The PR body's #1159 sentence was changed the same way. Self-review of this one-line repair: content and claims checked against the follow-up's numbers and the existing paragraph; markdownlint 0 errors; CLEAN.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent follow-up 2 (relayed). Codex CLI 0.163.0, read-only; reviewed 5185fd69..08175179 against the raw reports. Result: FINDINGS (1). "Did not regress" was still unsupported for the 8-worker aio writes of 256 MiB: 329.00 → 329.31 MiB/s (pool 10) and 345.62 → 327.47 MiB/s (pool 40, −5.25%), and aio/sync fell from 1.08–1.09 to 0.95. The reviewer checked all 16 covered settings: every other covered aio median rose, and all sync medians rose.

Repair in dd357365: the reviewer's suggested heading was adopted ("For objects of 256 MiB and larger, aio reads and 8-worker writes ran at 0.95 to 1.06 times sync, and 8-worker copies at 1.36 to 1.49 times sync."), and a sentence now states the 256 MiB 8-worker exception against 4.1.0. The PR body's #1159 sentence states the same. Self-review of the repair: the range 0.95–1.06 was re-derived (reads 0.96–1.06; 8-worker writes 0.95, 0.95, 0.97, 1.02, 1.01, 1.01); markdownlint 0 errors; CLEAN.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent follow-up 3 (relayed). Codex CLI 0.163.0, read-only; reviewed 08175179..dd357365. Result: CLEAN. The reviewer recomputed all 16 covered settings from both runs' raw trials.jsonl; the medians match job-summary.csv. The heading (line 51) and the added sentence about the 256 MiB exception (line 53) are supported: 329.001 → 329.306 MiB/s (pool 10) and 345.617 → 327.472 MiB/s (pool 40), both current ratios 0.95. The heading and the exception agree with the body sentences, including the separate 64 MiB observations. The independent review of this PR is complete at dd357365.

PandasCursor's native CSV read took 24.04 seconds against 3.80 seconds for the flat workload, and Wrangler's 29.30 against 6.02 seconds.
At 10 million rows, PandasCursor's CSV read with 100,000-row chunks took 163.71 seconds of client result time, against 76.50 seconds without chunks; Wrangler took 264.93 and 154.22 seconds.
The native UNLOAD and CTAS reads, which add only `event_time` and `event_date`, took within 0.3 seconds of the flat workload's client result time at 1 million rows.
Through `read_sql_query()`, whose row output converts those columns, the UNLOAD reads took 0.67 to 0.97 seconds longer with the pandas and Polars drivers and 2.65 to 2.75 seconds longer with the Arrow driver.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2, finding 2 of 3 (independent review, relayed). "UNLOAD and CTAS reads ... took within 0.3 seconds" did not hold for read_sql_query(). The temporal UNLOAD cases took 0.67 to 0.97 s longer with the pandas and Polars drivers and 2.65 to 2.75 s longer with awsathena+arrow (for example sqla_arrow unload c0: 3.60 → 6.24 s). Verified from the job summaries.
Repair: the sentence is limited to the native reads, and a new sentence gives the read_sql_query() deltas.


| Transport | Shape | medium (s) | large (s) | xlarge (s) | xlarge, 4.1.0 (s) | xxlarge (s) |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| CSV | flat | 1.27 | 5.54 | 48.85 | 28.80 | 318.65 |

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3, finding 3 of 3 (independent review, relayed). The xxlarge CSV execution time, 335.38 s, came from the single unchunked trial, while the caption says xxlarge uses the 100,000-row-chunk cases. The chunked job's three trials ran 323.562, 315.337, and 318.645 s (median 318.645). Verified from trials.jsonl.
Repair: the cell is now 318.65.

laughingman7743 and others added 2 commits October 19, 2026 23:06
)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e 4.1.0 exception (GH-1288)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant