Skip to content

Hoist per-chunk config lookups and parallelize deep group traversal#184

Draft
d-v-b wants to merge 7 commits into
mainfrom
claude/misc-perf
Draft

Hoist per-chunk config lookups and parallelize deep group traversal#184
d-v-b wants to merge 7 commits into
mainfrom
claude/misc-perf

Conversation

@d-v-b

@d-v-b d-v-b commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Addresses P4–P6 of #181.

Three independent performance findings were investigated. Two are implemented; one was found to be invalid on inspection and is documented below.

1. Hoist repeated async.concurrency config lookups off the per-chunk codec path (P4)

With the default codec_pipeline.batch_size=1, the read/write/encode/decode paths re-read config.get("async.concurrency") once per chunk (in read_batch/write_batch), once per batch in read/write, and once per codec per chunk via _batching_helper and the partial decode/encode mixins. Each lookup walks Donfig's nested config.

This adds an operation-scoped cache in zarr.core.config:

  • async_concurrency_scope() — a context manager that captures async.concurrency once.
  • get_async_concurrency() — returns the captured value inside a scope, else falls back to a live config.get.

BatchedCodecPipeline.read/write/decode/encode now wrap their bodies in the scope, and all eight per-chunk/per-codec lookups (5 in codec_pipeline.py, 3 in abc/codec.py) read through get_async_concurrency(). No public signatures changed. Config changes between operations are still honored (each operation re-captures on entry); only mid-operation changes are ignored, which is the intended hoisting behavior. Verified manually.

2. Parallelize deep group traversal across sibling subgroups (P5)

_iter_members_deep previously drained each subgroup's recursive iterator in sequence (for prefix, subgroup_iter in to_recurse.items(): async for ...), so sibling subtrees did not overlap latency. It now drives sibling iterators concurrently via per-subgroup drain tasks feeding an asyncio.Queue, yielding members as they arrive. The shared asyncio.Semaphore (already threaded into every recursive call) still bounds total concurrent getitem operations. Member ordering remains unspecified, as documented on Group.members — existing tests already sort before comparing, and the full tests/test_group.py suite passes.

3. (SKIPPED) StorePath.__truediv__ re-normalizing per chunk (P6)

Skipped: the premise does not hold against the current code. StorePath.__truediv__ calls _join_paths([self.path, other]), which is a plain "/".join(filter(...)) with no regex, no split, and no dot-segment scan. It does not call normalize_path. The per-chunk join is already a cheap fast path, so there is nothing to optimize here without a risky micro-optimization of _join_paths for negligible benefit. No change made.

Test results (uv run)

  • uv run pytest tests/test_codecs/ tests/test_group.py tests/test_store/ -q2000 passed, 207 skipped, 2 xfailed
  • uv run pytest tests/test_indexing.py -q424 passed, 1 skipped, 5 xfailed
  • uv run pytest tests/test_config.py -q37 passed
  • uv run mypy src/zarr/core/config.py src/zarr/core/codec_pipeline.py src/zarr/abc/codec.py src/zarr/core/group.pySuccess: no issues found in 4 source files

Note

No towncrier changelog entry was added — one should be added before this is un-drafted.

🤖 Generated with Claude Code

dependabot Bot and others added 7 commits May 31, 2026 19:28
…#176)

Bumps the actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) | `0.9.5` | `0.9.6` |
| [codecov/codecov-action](https://github.com/codecov/codecov-action) | `6.0.0` | `6.0.1` |
| [github/issue-metrics](https://github.com/github/issue-metrics) | `4.2.2` | `4.2.7` |
| [j178/prek-action](https://github.com/j178/prek-action) | `2.0.3` | `2.0.4` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `7.0.0` | `7.0.1` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |
| [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) | `1.13.0` | `1.14.0` |
| [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) | `0.5.3` | `0.5.6` |



Updates `prefix-dev/setup-pixi` from 0.9.5 to 0.9.6
- [Release notes](https://github.com/prefix-dev/setup-pixi/releases)
- [Commits](prefix-dev/setup-pixi@1b2de7f...5185adf)

Updates `codecov/codecov-action` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@57e3a13...e79a696)

Updates `github/issue-metrics` from 4.2.2 to 4.2.7
- [Release notes](https://github.com/github/issue-metrics/releases)
- [Commits](github-community-projects/issue-metrics@c9e9838...1e38d5e)

Updates `j178/prek-action` from 2.0.3 to 2.0.4
- [Release notes](https://github.com/j178/prek-action/releases)
- [Commits](j178/prek-action@6ad8027...bdca6f1)

Updates `actions/upload-artifact` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v7...043fb46)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v7...3e5f45b)

Updates `pypa/gh-action-pypi-publish` from 1.13.0 to 1.14.0
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](pypa/gh-action-pypi-publish@v1.13.0...cef2210)

Updates `zizmorcore/zizmor-action` from 0.5.3 to 0.5.6
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](zizmorcore/zizmor-action@b1d7e1f...5f14fd0)

---
updated-dependencies:
- dependency-name: prefix-dev/setup-pixi
  dependency-version: 0.9.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/issue-metrics
  dependency-version: 4.2.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: j178/prek-action
  dependency-version: 2.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: pypa/gh-action-pypi-publish
  dependency-version: 1.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…rsal

Hoist `async.concurrency` config lookups to once per read/write/encode/
decode operation via an operation-scoped cache (`async_concurrency_scope`
/ `get_async_concurrency` in `zarr.core.config`), instead of re-reading
Donfig config per chunk and per codec stage on the codec pipeline hot
path. Config changes between operations are still honored.

Drive recursion into sibling subgroups concurrently in
`_iter_members_deep` so sibling subtrees overlap their latency, instead
of draining each subgroup iterator sequentially. The shared semaphore
still bounds total concurrent `getitem` calls; member ordering remains
unspecified (as documented).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@d-v-b d-v-b changed the title Hoist per-chunk config lookups, parallelize deep group traversal, fast-path chunk-key joins Hoist per-chunk config lookups and parallelize deep group traversal Jun 12, 2026
@d-v-b d-v-b force-pushed the main branch 2 times, most recently from db473cd to fd6189b Compare June 25, 2026 09:15
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.

1 participant