Skip to content

Benchmark the S3 filesystem against s3fs and decide on a standalone package #1315

Open
3 of 6 issues completed

Description

@laughingman7743

Summary

Decide, from measurements, whether PyAthena's S3 filesystem (S3FileSystem and AioS3FileSystem) has an advantage over s3fs that justifies offering it as an s3fs alternative, and whether it should then be developed and published as a standalone package that PyAthena depends on.

Background

The filesystem is built on the synchronous boto3 client and thread pools; s3fs is built on aiobotocore. The two differ in three ways that matter to users:

  1. Dependencies. s3fs 2026.9.0 requires aiobotocore>=2.19,<4 and pins fsspec to its own version (>=2026.9.0,<2026.9.1). aiobotocore 3.10.0 requires botocore>=1.43.115,<1.43.118, while the latest boto3 is 1.43.118. PyAthena requires only boto3>=1.43.31, botocore>=1.43.31, fsspec>=2026.9.0, and tenacity. The filesystem will not depend on aiobotocore (Separate an S3 core from the fsspec adapter in pyathena.filesystem #1053).
  2. Concurrency model. One PyAthena file reads its ranges and uploads its parts in parallel through an S3Executor sized by max_workers; the aio filesystem runs the blocking boto3 calls in its own thread pools (Size the asyncio filesystem's parallel requests by max_workers instead of the default executor #1159). s3fs runs every request as a coroutine on fsspec's IO loop, so batch operations over many objects (cat, get, put, rm, copy) run with batch_size concurrency. The synchronous S3FileSystem inherits fsspec's sequential loops for these batch operations (copy and get in pyathena/filesystem/s3.py fall through to AbstractFileSystem), and AioS3FileSystem gets its batch concurrency from AsyncFileSystem with threads.
  3. Design. Typed models (S3Object, S3Core, S3Path, S3PathPairing), per-operation request parameters, error translation, and the write session (Give multipart writes a single owner of their parts and an explicit state #1211) are PyAthena's own; the s3fs API surface is kept for migration (Improve s3fs / fsspec API parity for the built-in S3FileSystem #722).

The filesystem suite of the benchmark project measures one-object reads, writes, and copies through the two PyAthena filesystems only. The 4.2.0 run found the aio paths at the sync throughput for writes and copies, but nothing in the history compares either with s3fs, and the suite has no scenario with many objects, where s3fs's coroutine concurrency should matter most.

Plan

  1. Add s3fs to the filesystem suite as a third API, in its own benchmark environment because aiobotocore pins botocore: Add s3fs as an API of the filesystem benchmark suite #1316.
  2. Add many-object scenarios to the suite: batch reads, writes, deletes, listing, and metadata fan-out over hundreds of small objects: Add many-object scenarios to the filesystem benchmark suite #1317.
  3. Measure the extended suite on one c8gn.2xlarge Spot host and record a history entry with the comparison and a recommendation: Measure the filesystem suite against s3fs on c8gn.2xlarge #1318.
  4. Decide from the results. Expected outcomes, to be confirmed or refuted by step 3:
    • One large object: PyAthena's parallel range reads should match or beat s3fs's single-stream open().read(); writes and server-side copies should be close, since both are multipart.
    • Many small objects: s3fs is likely ahead for the synchronous filesystem, which runs its batch operations sequentially, and may be ahead of the aio filesystem at high concurrency, where threads and the connection pool cap PyAthena. If so, parallel batch operations for S3FileSystem are the first improvement to file.
    • Process cost: peak RSS, thread count, and CPU time per transferred byte for each implementation.
  5. Standalone package, only if step 4 finds an advantage worth offering to users without Athena. The design question is filed separately after the measurement; the inventory below is what it has to resolve.

Coupling inventory for step 5

pyathena.filesystem is 10,971 lines in 11 modules with 374 commits since 2022-09. Its imports from the rest of PyAthena:

Import Used by Note
pyathena.util.override, RetryConfig, retry_api_call, stop_retries_on, unstoppable_retries s3_core.py, s3.py, s3_async.py, s3_executor.py, s3_writer.py, reader.py, s3_path.py, s3_object.py Retry policy and stop scopes shared with the cursors; would move with the package or be duplicated
pyathena.aio.util.create_in_thread s3_async.py Cancellation-safe construction (#1208)
pyathena.connection.Connection s3.py, s3_async.py The connection= constructor argument and the shared S3 client (#1011); would become a PyAthena-side factory
pyathena.user_agent_extra s3.py User agent of the S3 client

PyAthena modules that import the filesystem: the pandas, Polars, Arrow, and S3FS result sets, pyathena.aio.s3fs.cursor, and the pyathena.pandas / pyathena.polars packages, which register the s3 and s3a protocols. A standalone package would register through fsspec's fsspec.specs entry point instead, and pyathena.filesystem would stay as a compatibility re-export for 4.x.

fsspec ships no benchmark (pytest-benchmark is a test dependency of fsspec without a test that uses it), and s3fs none either, so the comparison has to come from the suite here. fsspec does ship the conformance tests fsspec.tests.abstract (copy, get, put, mv, open, pipe), which s3fs runs in s3fs/tests/derived; running them against PyAthena's filesystem is a precondition of step 5, since an s3fs alternative is judged by that contract.

A uv workspace member in this repository (as benchmarks/ is) published as its own distribution would keep the AWS test infrastructure, CI, and docs shared; a separate repository is a later option if the release cadence diverges.

Decision criteria

  • A measured throughput, memory, or CPU advantage in at least one common scenario, or parity with the dependency advantage above, supports the alternative.
  • Parity everywhere with higher process cost means the filesystem stays an internal part of PyAthena, and step 5 is not filed.

Activity

  1. laughingman7743 commented on Oct 24, 2026

    @laughingman7743
    MemberAuthor

    Results of step 3 (#1318), recorded in the history entry (PR #1321): one c8gn.2xlarge Spot host, commit 7b20b499, s3fs 2026.9.0 with aiobotocore 3.9.2, five trials per case, no failures. The suite used 8 MiB blocks for every API; because that understates s3fs's sequential uploads and copies, a supplementary run repeated s3fs's single-object cases with its default of 50 MiB.

    Against the expected outcomes

    1. One large object.
      • Reads: confirmed. PyAthena's parallel range reads ran at 2.3 to 8.0 times s3fs's single-stream open().read(), which stayed at 75 to 93 MiB/s.
      • Writes and copies: refuted; they were not close. s3fs uploads parts and copies them one part at a time, so with its default 50 MiB it wrote at 75 to 97 MiB/s and copied 6 GiB at 47 to 53 MiB/s. PyAthena wrote at 1.6 to 11.1 times and copied at 5.3 to 33.4 times these rates. put_file() ran at 1.5 to 3.0 times.
      • Not expected: s3fs's get_file() was 2.2 to 3.7 times faster with 8 MiB chunks, and 5 to 9 times with 50 MiB from 256 MiB up, because PyAthena's get_file() reads one block at a time.
    2. Many small objects (1,000 × 1 MiB).
      • Synchronous S3FileSystem: confirmed. cat/get/put/pipe/copy ran one object at a time, 7 to 59 times slower than s3fs.
      • AioS3FileSystem at high concurrency: mostly refuted. At batch size 8 it matched s3fs; at 40 it was 1.4 to 2.5 times faster for cat/get/put/pipe, while s3fs was 1.20 times faster for copy and 1.45 times for info().
      • rm and find ran at the same rate with every API.
    3. Process cost.
      • Threads: s3fs used 13 in every case but rm, whose harness preparation adds 32 threads to every API; PyAthena 19 to 22 with 8 workers and up to 54 with 40.
      • CPU: s3fs's reads took about a third of PyAthena's CPU time; writes, puts, downloads, and copies took 0.94 to 1.45 times as much in PyAthena.
      • Memory: s3fs's 1 GiB downloads with max_concurrency 40 peaked at 0.9 to 2.1 GiB, against 0.3 GiB.

    Against the decision criteria

    The first criterion is met: PyAthena has a measured throughput advantage in common scenarios (single-object reads, writes, puts, and copies with either API, and AioS3FileSystem batch transfers at high concurrency), with the dependency advantage described above.
    Two gaps would meet a user migrating from s3fs, and are filed as sub-issues of this issue:

    Proposed next steps: fix these two, re-measure the affected cases (sync objects cases, get cases), and then file the design issue for step 5, including the fsspec fsspec.tests.abstract conformance tests as its precondition.
    The copy and info() differences of AioS3FileSystem at batch size 40 (0.84 and 0.69 times s3fs) are small and unexplained; they can be looked at during the re-measurement rather than filed now.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions