storage: zero-copy BidiReadObjectResponse parsing and off-loop CRC32C verification for async multi-range reads
メンテナーはふだん 1 日以内に返信
@chandra-siri がすでに取り組んでいます。
2026年10月12日 から。
評価
この issue はまだ評価されていません。
説明
Summary of the feature request
AsyncMultiRangeDownloader reads from Rapid (zonal) buckets over the BidiReadObject gRPC stream. Per process, throughput is bound by the asyncio event-loop thread: every 2 MiB BidiReadObjectResponse is processed on that thread, under the GIL, before the next message can be received, and all streams of a process queue behind it.
Profiling one process reading 16 streams at 2.0 GB/s (c4-standard-192, bucket in the same zone, py-spy on the loop thread) gives 318 ms of loop-thread time per GB:
| Step on the loop thread | ms per GB |
|---|---|
| gRPC receive copy | 123 |
BidiReadObjectResponse.deserialize (upb copies the payload: copy 1) |
69 |
checksummed_data.content (proto-plus returns a new bytes: copy 2) |
58 |
google_crc32c.value() inline |
38 |
| asyncio overhead | 30 |
Two of the three large items are avoidable:
- The payload is copied twice by the generated parser. A parser that reads the message in place over the
bytesobject gRPC hands to the deserializer can give the read path amemoryviewof the received message instead (257 us per 2 MiB message today, 5 us in place). - CRC32C verification runs on the loop thread even though
google-crc32calready releases the GIL for inputs of 1 MiB or more; hashing on a small worker pool overlaps it with the next receive.
Both need google_crc32c.value() to accept a memoryview; today it parses its argument with y# and raises TypeError: argument 1 must be read-only bytes-like object, so a view into the gRPC buffer cannot be verified without copying it back to bytes.
Proposed changes (three PRs, independent except that the parser depends on the google-crc32c release):
- #18626
google-crc32c:value()/extend()/Checksum.update()accept any contiguous bytes-like object; the GIL is released forbytes-owned memory of 1 MiB or more (abytesor amemoryviewof one). - #18627
google-cloud-storage: opt-in zero-copy parser,create_mrd(..., zero_copy=True)(orGOOGLE_CLOUD_STORAGE_ZERO_COPY_BIDI_READ=1); only active when the installedgoogle-crc32ccan hash amemoryview, otherwise the generated parser is used. With it on,buffer.write()receives a read-onlymemoryviewof the received message. - #18628
google-cloud-storage: chunks of 1 MiB or more are hashed on a 2-thread pool and written to their buffer only after the checksum matches, in arrival order; on by default,GOOGLE_CLOUD_STORAGE_CRC32C_OFFLOAD_MIN_BYTES=0restores inline verification.
fsspec/gcsfs is the main consumer of this path and would opt in; its sinks copy from the view into the caller's buffer.
Desired code experience
import io
from google.cloud.storage.asyncio.async_grpc_client import AsyncGrpcClient
from google.cloud.storage.asyncio.async_multi_range_downloader import AsyncMultiRangeDownloader
client = AsyncGrpcClient()
mrd = await AsyncMultiRangeDownloader.create_mrd(
client, "my-rapid-bucket", "my-object", zero_copy=True
)
buffers = [io.BytesIO() for _ in range(4)]
# buffer.write() receives memoryview chunks that alias the received gRPC
# message; BytesIO, binary files and bytearray sinks accept them unchanged.
await mrd.download_ranges([(i * 16 << 20, 16 << 20, buf) for i, buf in enumerate(buffers)])
Off-loop verification needs no code change; it applies to download_ranges with enable_checksum=True.
Expected results
Same machine and bucket, 16 streams per process, 4 x 16 MiB per download_ranges call into preallocated buffers, CRC32C verification on, aggregate GB/s:
| Processes | main |
zero-copy parser (#18627, median of 3 interleaved runs) | off-loop CRC32C (#18628, median of 9) |
|---|---|---|---|
| 1 | 1.55 | 2.62 | about 2.0 for both arms (unchanged) |
| 8 | 9.76 | 11.29 | 8.17 -> 9.59 (+17%, led in 7 of 9 paired runs) |
With all three changes together, one process goes from 1.6 to 3.5 GB/s and eight processes from 9.8 to 13.5 GB/s; loop-thread time per GB drops from 318 ms to 195 ms.
Environment: google-cloud-storage at 053dbc9872ad, google-crc32c 1.9.0 (main arm), grpcio 1.84.0, protobuf 7.36 (upb backend), Python 3.13, c4-standard-192 in us-central1-c.
- 主要言語
- Python
- スター
- 5.4k
- フォーク
- 1.8k
- 平均マージ
- 2日 7時間
- マージ済み PR(30日)
- 173
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
googleapis/google-cloud-python のほかの issue
-
firestore: field path with a quoted name ending in a backslash is misparsed対応中かも @sebxt が 25 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
googleapis/google-cloud-python#18771 ·
メンテナーはふだん 1 日以内に返信
-
api: spanner type: feature request
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
googleapis/google-cloud-python#18584 ·
メンテナーはふだん 1 日以内に返信
-
auth: `impersonated_credentials` loses `subject` on copy, so `with_scopes()` and `with_quota_project()` turn off domain wide delegation対応中かも @Om-singhaI が 55 日前に担当しました。 オープンauth priority: p2 type: bug
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
googleapis/google-cloud-python#18428 ·
メンテナーはふだん 1 日以内に返信
-
A client called `run_in_transaction` be stuck if Spanner is down対応中かも @founderinaction が 7 日前に担当しました。 オープンpriority: p2 type: bug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
googleapis/google-cloud-python#18375 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
ci: enable --strict flag in presubmit twine_check再び着手できるかも @ohmayr が 44 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 76/100
googleapis/google-cloud-python#18339 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
googleapis/google-cloud-python の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
cpp-best-practices/project_validator#267 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
Project-N-E-K-O/N.E.K.O#3725 ·
メンテナーはふだん 1 日以内に返信
-
[Bug] Sending a Telegram message chain twice duplicates mentions対応中かも @123XieC が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
AstrBotDevs/AstrBot#11125 ·
メンテナーはふだん 1 日以内に返信
-
backend bug priority: high
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
nooriegarg/ContribIQ#73 ·
メンテナーはふだん 1 日以内に返信
-
bug p3 security
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信