Skip to content

Record the filesystem comparison with s3fs (GH-1318) - #1321

Merged
laughingman7743 merged 3 commits into
masterfrom
docs/1318-s3fs-comparison
Oct 24, 2026
Merged

laughingman7743 merged 3 commits into
masterfrom
docs/1318-s3fs-comparison

Conversation

@laughingman7743

@laughingman7743 laughingman7743 commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

WHAT

Adds benchmarks/history/2026-10-23-7b20b499-filesystem.md, the measurement of #1318: the filesystem suite (#1316) and the objects suite (#1317) at 7b20b499 on one c8gn.2xlarge Spot host, comparing PyAthena's S3FileSystem and AioS3FileSystem with s3fs 2026.9.0.
A supplementary queue repeated the single-object cases of s3fs with its default block size of 50 MiB, because s3fs uploads and copies parts one at a time and the suite's 8 MiB parts would otherwise understate it.

benchmarks/RESULTS.md lists the entry and adds one recommendation: AioS3FileSystem with batch_size for transfers of many objects, since the synchronous S3FileSystem transfers them one at a time.

Main findings:

  1. Single objects: PyAthena read, wrote, put, and copied faster than s3fs, also against s3fs's 50 MiB default (reads 2.3–8.0×, writes 1.6–11.1×, puts 1.5–3.0×, 6 GiB copies 5.3–33.4×).
  2. get_file(): s3fs was 2.2–3.7× faster with 8 MiB chunks and 5–9× with 50 MiB for objects of 256 MiB and larger; PyAthena reads one block at a time.
  3. Many objects: the synchronous S3FileSystem ran cat/get/put/pipe/copy one object at a time, 7–59× slower than s3fs; AioS3FileSystem matched s3fs at concurrency 8 and was 1.4–2.5× faster at 40, except copies (0.83×) and info() (0.69×).
  4. Process cost: s3fs used 13 threads in every case but deletes and about a third of PyAthena's CPU for reads; its 1 GiB downloads with max_concurrency 40 peaked at 0.9–2.1 GiB against 0.3 GiB for PyAthena.

WHY

Closes #1318, step 3 of #1315.
The recommendation for #1315 is in the entry: the throughput advantage meets #1315's first decision criterion, and two gaps (sequential batch operations of S3FileSystem, sequential get_file()) are to be filed as improvement issues before a standalone package is designed.

TEST

  • Measurement: queue fs-s3fs-1, 66 jobs, 216 cases, five trials each, no failures (2026-10-23 15:46–20:03 UTC); queue fs-s3fs-50m-1, 14 jobs, 56 cases, no failures (2026-10-24 02:00–03:24 UTC). Both stacks were cleaned up with cleanup --execute and deleted; no instances, volumes, buckets, or scratch databases remain.
  • Every number in the entry was derived from the recovered job-summary.csv files by script; the tables are generated from them.
  • just lint and just docs lint pass on the head; markdownlint-cli2 also passes on the history entry, which the docs glob does not include.
  • Markdown-only change: no AWS tests apply, and the Sphinx site does not include benchmarks/.

🤖 Generated with Claude Code

Add the history entry of the filesystem and objects suites at 7b20b49
against s3fs on one c8gn.2xlarge Spot host, with a supplementary run of
s3fs at its default 50 MiB block size, and list it in RESULTS.md with a
recommendation for transfers of many objects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread benchmarks/RESULTS.md Outdated
| 2026-10-13 | `437276e1` | 4.1.0 release commit, filesystem suite | `c8gn.2xlarge` (arm64), one host | Spot | small, medium, large, xlarge (objects) | [2026-10-13-437276e1-filesystem](history/2026-10-13-437276e1-filesystem.md) |
| 2026-10-19 | `9e99caa8` | 4.2.0 (tagged on `baee716c`, which differs only in one test file) | `r7g.2xlarge` (arm64), fleet of 10 | Spot | medium, large, xlarge, xxlarge | [2026-10-19-9e99caa8](history/2026-10-19-9e99caa8.md) |
| 2026-10-19 | `9e99caa8` | 4.2.0 (tagged on `baee716c`, which differs only in one test file), filesystem suite | `c8gn.2xlarge` (arm64), one host | Spot | small, medium, large, xlarge (objects) | [2026-10-19-9e99caa8-filesystem](history/2026-10-19-9e99caa8-filesystem.md) |
| 2026-10-23 | `7b20b499` | unreleased (after 4.2.1), filesystem and objects suites against s3fs | `c8gn.2xlarge` (arm64), one host | Spot | small, medium, large, xlarge (objects); medium (1,000 objects) | [2026-10-23-7b20b499-filesystem](history/2026-10-23-7b20b499-filesystem.md) |

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 one (implementation behavior: tables, generated numbers, links, structure)

Base 7b20b499bb3c5e28d06e15df38746ebceb9e4b56, head 3bcddf66aa76edd399124aeecc6dfff501ccca29. Covered: benchmarks/history/2026-10-23-7b20b499-filesystem.md (environment, selection, run record, findings, recommendation, limits, four result tables and the 50 MiB table) and benchmarks/RESULTS.md (index row, recommendation intro, new recommendation row).

Checks: every table is generated by script from the 66 + 14 recovered job-summary.csv files (216 + 56 cases, all 5 OK / 0 failed / not unsupported, no notes); the prose ranges were recomputed from the same rows; the README anchors #filesystem-suite and #objects-suite exist; the tree/7b20b499... link is the measured commit; S3FileSystem overrides only copy and get among the batch methods, both calling super(), so the "fsspec's sequential loops" statement holds for cat/put/pipe too.

Result: FINDINGS (1)

  1. benchmarks/RESULTS.md:23: the Scales cell "small, medium, large, xlarge (objects); medium (1,000 objects)" reuses "(objects)" for object sizes, as the earlier filesystem rows do, right next to a set of 1,000 objects, so a reader cannot tell the two apart. Repair: name them "single objects" and "object set".

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.

Repaired in 047c8e4f: the Scales cell now reads "small, medium, large, xlarge (single objects); medium (object set)". Round-one follow-up over the range-diff 3bcddf66..047c8e4f: only benchmarks/RESULTS.md changed; the row still matches the entry, and markdownlint passes. CLEAN.

Comment thread benchmarks/RESULTS.md Outdated
| A Wrangler-like pandas read | PandasCursor | 10 million flat rows in 19.8 seconds through UNLOAD and 84.2 seconds through CSV, against 23.4 and 108.9 seconds for Wrangler |
| Asynchronous code | The native asyncio cursors, or the synchronous and ThreadPool APIs offloaded with `asyncio.to_thread()` or an executor; synchronous calls block the event loop while they poll | Single-query timings of the three APIs differed by a median of 1.7% in the 4.0.0 run |
| S3 transfers in asyncio applications | `AioS3FileSystem`; PyAthena keeps `S3FileSystem` registered for fsspec's `s3` and `s3a` protocols, and making `AioS3FileSystem` the default is to be revisited when boto3 supports asyncio natively, because it still runs the blocking boto3 calls in threads | With 40 workers on 8 vCPUs, `AioS3FileSystem` wrote 1 GiB and 6 GiB objects at 0.97 to 1.00 times and copied 6 GiB at 1.01 to 1.05 times the throughput of `S3FileSystem`, and read 1 GiB at 1.00 times; with 8 workers, it copied 6 GiB at 1.36 to 1.49 times (#1159) |
| Transfers of many objects with one call (`cat`, `get`, `put`, `pipe`, or `copy` with a list of paths) | `AioS3FileSystem` with `batch_size`; the synchronous `S3FileSystem` transfers one object at a time | In the s3fs comparison of `7b20b499`, 1,000 objects of 1 MiB at 286 to 484 objects per second with a batch size of 40, against 6.5 to 20.3 for `S3FileSystem` and 184.7 to 235.4 for s3fs, except copies, at 345.1 against 412.7 for s3fs |

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 two (claims, caller/operator/reader perspectives)

Base 7b20b499bb3c5e28d06e15df38746ebceb9e4b56, head 3bcddf66aa76edd399124aeecc6dfff501ccca29. Claims listed from the PR body, the commit message, the entry, and RESULTS.md, each checked against the recovered summaries or the dependency source:

  • s3fs defaults (50 MiB default_block_size, _copy_managed(block=50 * 2**20), chunksize = chunksize or self.default_block_size in _get_file, calculate_chunksize 50 MiB in _put_file, MANAGED_COPY_THRESHOLD 150 MiB, readahead cache): verified in the s3fs 2026.9.0 wheel (the measured version), not only upstream main.
  • "uploads and copies parts one at a time": _copy_managed awaits each upload_part_copy in a list comprehension; S3File._upload_chunk calls upload_part synchronously; 768 = 6 GiB / 8 MiB.
  • "two chunks" for the 64 MiB get with 50 MiB chunks: content_length > chunksize splits by chunksize.
  • Run times, case counts, failures, AZs, AMI, library versions: from done/, hosts/, environment.json, and instances.json of both queues.
  • Measure the filesystem suite against s3fs on c8gn.2xlarge #1318's 20-30 minute estimate: quoted from the issue's Cost section.
  • "The Sphinx site does not include benchmarks/" (PR body): Sphinx's source is docs/; docs only link to GitHub URLs.
  • Operator: both stacks deleted; no instances, volumes, scratch buckets, or pyathena_bench_* databases remain; queue records in the CI bucket expire after a day and were recovered.

Result: FINDINGS (2)

  1. benchmarks/RESULTS.md:40: "against 6.5 to 20.3 for S3FileSystem" compares the aio cat/get/put/pipe rates at batch size 40 with a sync range that includes copy (6.5-6.9) and concurrency 8. The matching sync range for those four operations at 40 is 9.5 to 20.3.
  2. PR body, "its 1 GiB downloads peaked at 0.9-2.1 GiB against 0.3 GiB": true only for max_concurrency 40; with 8, s3fs peaked at 408-410 MiB (8 MiB chunks) and 1,316-1,361 MiB (50 MiB). The entry (line 79 here) already scopes it to 40; the PR body must say so too.

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.

Repaired in 047c8e4f: (1) the row now compares with 9.5 to 20.3 objects per second, the sync cat/get/put/pipe rates at concurrency 40, rechecked against job-summary.csv; (2) the PR body now scopes the download memory to max_concurrency 40 and names PyAthena as the comparison. Follow-up of both perspectives over the repaired hunks: no other claim in the row or body depended on these values. CLEAN.

laughingman7743 and others added 2 commits October 24, 2026 12:39
…H-1318)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s entry (GH-1318)

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

The PyAthena cases ran in the benchmark environment and the s3fs cases in the `s3fs` environment of the same checkout.

The block size of 8 MiB is the filesystem suite's setting for every API, as in the [4.2.0 run](2026-10-19-9e99caa8-filesystem.md); PyAthena's defaults are 5 MiB for files and 5 GiB parts for multipart copies, and s3fs's is 50 MiB.

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): Codex CLI codex exec -s read-only, model gpt-6-astra (OpenAI), session 01a16bfe-6093-7303-a481-5275d239ce2c. Static review of a detached snapshot at head 047c8e4f4d3a23d9086393e3b1782ad00d459963, base 7b20b499bb3c5e28d06e15df38746ebceb9e4b56, with the literal diff, the 80 recovered job-summary.csv/environment.json files, queue/done/host/instance records, and the s3fs 2026.9.0 source; no PR text, commit messages, or prior findings were given. No edits, builds, tests, or GitHub access.

Coverage reported: the two-file diff, all 1,560 numeric table cells (all match the CSVs at displayed precision), harness and filesystem source, README, and the earlier entry. Not independently established: #1318's estimate, cleanup, and stack deletion (no records supplied); fsspec was reasoned from callers.

Result: FINDINGS (6), all verified against the code and data:

  1. [P2] history:40, :100: "PyAthena's default is 5 MiB" conflates file I/O (5 MiB) with multipart copies (S3Core default part MULTIPART_UPLOAD_MAX_PART_SIZE, 5 GiB), and the objects suite uses each filesystem's defaults, not 8 MiB.
  2. [P2] history:65, :76: "its workers do not apply" / "which use no workers": the first bytes-cache fetch requests two blocks, which S3File._fetch_range splits into two parallel ranges.
  3. [P2] RESULTS.md:40: pipe takes a dictionary of paths and bytes, not a list of paths.
  4. [P3] history:76: "Every s3fs process peaked at 13 threads" is false for rm (44, from the harness's 32-thread preparation pool, SETUP_WORKERS).
  5. [P3] history:41: "s3fs uploads and copies parts one at a time" also covers put_file(), which uploads in batches; the 64 MiB supplementary put is one PutObject (64 MiB < 2 x 50 MiB).
  6. [P3] rounding: 2.4 -> 2.3 (2.3487), "at or below 5 ms" -> 5.25 ms (find n8), 4.80 -> 4.81, 23.7 -> 23.8, 0.83 -> 0.84.

Repaired in 83a49efb92b2f9f4f490bc7f05fdf56b3aee4b62 (all six); the PR body and the planned #1315 comment were corrected for the same values (2.3, 0.84, threads except deletes).

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 047c8e4f..83a49efb (range-diff; both objects present), both perspectives:

  • Round one (behavior): only history/2026-10-23-7b20b499-filesystem.md and RESULTS.md:40 prose changed; tables untouched; markdownlint and just lint pass.
  • Round two (claims): S3Core.MULTIPART_UPLOAD_MAX_PART_SIZE = 5 * 2**30 is the copy default when no block_size is given (s3_core.py:517, :1246); the objects suite builds each filesystem without a block size (filesystem.py:239) and copies through fs.copy/fs._copy without one (objects.py:122-123), so "each filesystem's defaults" holds and 1 MiB fits in one range or part (5 MiB, 50 MiB). SETUP_WORKERS = 32 (objects.py:34) and the rm peaks 43/44 for every API match the CSVs. The 5.25 ms bound is the find n8 p95 (5.246 ms). S3File._fetch_range splits the first two-block fetch into two ranges (s3.py:3289). The new 64 MiB PutObject sentence follows size < min(5 GiB, 2 * chunksize) in s3fs 2026.9.0 _put_file.

CLEAN. Independent follow-up requested next.

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 (relayed): Codex CLI codex exec -s read-only, model gpt-6-astra, session 01a16c07-a57b-7383-a9a2-39585464daa9, static review of the snapshot at 83a49efb, given the repair diff 047c8e4f..83a49efb (confirmed identical to the branch), the six findings, and the same evidence. Covered: the changed lines of both files, both runs' configurations and all 272 case summaries, the harness, README, PyAthena filesystem, s3fs 2026.9.0 and fsspec sources. Result: CLEAN; all six findings repaired, no new inaccuracies (unrounded values 2.348712, 5.246169 ms, 4.806528, 23.769719, 0.836232 confirmed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Measure the filesystem suite against s3fs on c8gn.2xlarge

1 participant