Skip to content

s3: speed up multi-thread uploads by checksumming chunks in 1 MiB reads - #10022

Open
rbutor wants to merge 1 commit into
rclone:masterfrom
rbutor:pr-s3-chunk-hash-buffer
Open

rbutor wants to merge 1 commit into
rclone:masterfrom
rbutor:pr-s3-chunk-hash-buffer

Conversation

@rbutor

@rbutor rbutor commented Oct 1, 2026

Copy link
Copy Markdown

What does this change do?

Use a 1 MiB scratch buffer for the checksum pass in s3ChunkWriter.WriteChunk when the source does not implement io.WriterTo.

For an unbuffered local range reader, io.Copy(md5, reader) falls back to 32 KiB reads. io.CopyBuffer makes the checksum reads larger; readers with WriterTo, including pool.RW, retain their fast path without a scratch allocation.

The buffer is allocated directly, avoiding a new global-pool wait on --max-buffer-memory. The additional buffer is 1 MiB per concurrent fallback checksum call, not a full part.

Checksums, multipart sizing, concurrency and retries are unchanged. Only checksum reads change, not HTTP body reads or source rereading.

An earlier implementation with the same 1 MiB checksum reads, but a pool-backed scratch buffer, was measured on Windows Server with a local NVMe source and an S3-compatible target:

--multi-thread-streams 32
--s3-chunk-size 256M
--s3-upload-concurrency 32
Measurement Before 1 MiB checksum reads
End-to-end upload throughput 2.44 Gbit/s 6.20 Gbit/s
Read buffer used for checksumming 32 KiB 1 MiB

These measurements are from the earlier pool-backed version, not a benchmark of the final make-based allocation in this PR.

Linked issue

None. This is a small, self-contained performance fix in the existing S3 checksum path; it has no dependency on the proposed crypt changes.

For new or changed backends

go run ./fstest/test_all passed for S3 on this branch against MinIO RELEASE.2025-09-07 on Linux (Ubuntu 24.04, Go 1.27.1), using the upstream TestS3Minio test and ignore lists. This covered backend, operations, sync, bisync, git-annex and VFS tests.

S3 unit tests and FsOpenChunkWriter / FsPutChunked against an S3-compatible service also passed.

Fork CI: https://github.com/rbutor/rclone/actions/runs/36925298046 — successful on the first attempt, commit cc390b351.

Integration-test retry detail

FsPutFiles/FromRoot/ListR needed one retry after a disappearing bucket during root listing. Other test packages create and remove buckets concurrently. Master and the combined branch passed first time. Final result: PASS: All tests finished OK.

Checklist

  • This change is trivial OR it has been discussed and agreed in the linked issue.
  • I have read the contribution guidelines.
  • (If I used AI tools to help write this code) I have read and understood the AI-assisted contributions guidance, and I have tested and take ownership of this change myself.
  • I have added tests for all changes in this PR if appropriate. No new tests; existing S3 unit and integration suites were run.
  • I have added documentation for the changes if appropriate. No new options or user-facing behavior beyond performance.
  • All commit messages are in house style.
  • (Backend changes only) test_all passes for this backend and if submitting a new backend can provide a test account for the integration tester - see CONTRIBUTING.md.
  • This Pull Request is ready for review.

WriteChunk checksums the chunk with io.Copy before sending it. For a
reader that implements neither WriterTo nor a memory backed Read - a
local file opened by a multi-thread copy - io.Copy falls back to its own
32 KiB buffer, so the source is read in 32 KiB pieces and the disk sees
thousands of requests per second where it should see hundreds.

Hand io.CopyBuffer a 1 MiB buffer instead. It is allocated for the
checksum pass rather than taken from the pool, where it could wait for
ever on a --max-buffer-memory below the page size. Readers that do
implement WriterTo, such as pool.RW, keep their fast path and don't get
a buffer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant