Repository navigation
Record the PyAthena 4.2.0 benchmark runs - #1310
Conversation
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. |
There was a problem hiding this comment.
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:
- Both entries and the RESULTS index called
9e99caa8the release commit, "taggedv4.2.0". The release run of9e99caa8failed 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 onbaee716c. That commit differs from9e99caa8only intests/pyathena/filesystem/test_s3_async.py; verified withgit diff --stat 9e99caa8 baee716c. Repaired: the titles and introductions now say "the commit planned for the PyAthena 4.2.0 release" and namebaee716cand Count only the file's ResourceWarning in test_open_async_cancelled #1311; the index rows say "4.2.0 (tagged onbaee716c, which differs only in one test file)". The version-string sentence now gives the same reason as the 4.1.0 entry. - 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-tableands3 rmofruns/c71b3f72…/) beforedelete-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.
| | 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 | |
There was a problem hiding this comment.
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:
- Closing statements in the PR body: the Size the asyncio filesystem's parallel requests by max_workers instead of the default executor #1159 done-when criteria (aio/sync at 40 workers: writes 0.97–1.00, copies 1.01–1.05; aio threads 52–53, following
max_workers; 1 GiB reads 0.96–1.06; 8-worker copies 1.36–1.49) match the fs-1 summaries. Benchmark chunked reads of a result larger than host memory #1162/larger than memory queries? #61: the 100M chunked peaks are 462–1,051 MiB. SQLAlchemy + Pandas very slow when compared to AWS Wrangler #254: 7.02 / 50.71 s against Wrangler CSV 12.37 / 108.88 s and UNLOAD 3.89 / 23.44 s. Benchmark the temporal type conversion changes of 4.2.0 #1287: the temporal workload ran in both environments. - Maintainer decisions recorded as given: no default change for
AioS3FileSystem, revisit when boto3 supports asyncio natively; close larger than memory queries? #61 and SQLAlchemy + Pandas very slow when compared to AWS Wrangler #254 through this PR. - PolarsCursor holds a copy of the CSV result file while reading it whole with Polars 1.x #1307 cause statement:
_read_csv()passesfsspec.open(...).read()bytes topl.read_csv()(since Read CSV results through fsspec for Polars 2.0 #1169). This is consistent with a local check: bytes add about the file size on Polars 1.44.2 and not on 2.0.0. - RESULTS reader path: every recommendation row cites this run unless it says "in the 4.0.0 run". The Python rows row keeps the 4.0.0 ArrowCursor number with that label.
- CI claim:
test.yamlhaspaths-ignore: '**.md'for pull requests, so no AWS job runs for this PR. The Benchmark tooling workflow triggers onbenchmarks/**.
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.** |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 | |
There was a problem hiding this comment.
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.
…e 4.1.0 exception (GH-1288) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 onbaee716c, which differs from it only in one test file (#1311), so the measured package code is that of 4.2.0.benchmarks/history/2026-10-19-9e99caa8.md: the cursor run on ten Spotr7g.2xlargehosts, 167 jobs: the 4.1.0 selection, thetemporalworkload (Benchmark the temporal type conversion changes of 4.2.0 #1287), 100 million rows (Benchmark chunked reads of a result larger than host memory #1162), and every Polars job again with Polars 2.0 (Measure PolarsCursor on both Polars 1.x and 2.x in the benchmark harness #1292). Findings:read_sql_query()through the pandas and Polars drivers is faster than in 4.1.0:awsathena+pandaswithunload=truetook 50.71 seconds for 10 million flat rows instead of 162.60 (Return PandasCursor rows as tuples without a dictionary per row #1167, Return PolarsCursor rows as tuples without a dictionary per row #1177).temporalworkload's CSV reads and row output cost several times the flat workload's client time (Reduce the cost of the temporal conversion in row output and PandasCursor CSV reads #1308).benchmarks/history/2026-10-19-9e99caa8-filesystem.md: the filesystem suite on one Spotc8gn.2xlargehost, compared with 4.1.0. With 40 workers,AioS3FileSystemwrites and copies now run at 0.97 to 1.05 timesS3FileSystem's throughput (0.30 to 0.83 in 4.1.0), and its thread counts followmax_workers; writes no longer hold the whole object in memory (Bound the parts in flight of S3File multipart writes #1165). The entry records the maintainer's decision:S3FileSystemstays registered fors3/s3a,AioS3FileSystemis recommended for asyncio applications, and making it the default is to be revisited when boto3 supports asyncio natively.benchmarks/RESULTS.md: the two index rows, and the recommendations updated to these runs, with rows for results larger than memory, whole CSV reads in PolarsCursor (PolarsCursor holds a copy of the CSV result file while reading it whole with Polars 1.x #1307), all three result-file drivers forread_sql_query(), andAioS3FileSystemfor asyncio applications.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+pandaswithunload=truereads 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 fromread_sql_query()building the DataFrame from rows.TEST
just docs lintandmise exec -- markdownlint-cli2 benchmarks/history/2026-10-19-*.md benchmarks/RESULTS.md: 0 errors (the docs lint glob coversbenchmarks/*.mdbut notbenchmarks/history/).just lint: passed.**.mdpaths, so no AWS job runs; the Benchmark tooling workflow runs forbenchmarks/**.🤖 Generated with Claude Code