Skip to content

perf(NEGGIA-001): worker pool replacing GLOBAL_HANDLE singleton#28

Open
maxburian wants to merge 7 commits into
masterfrom
feature/NEGGIA-001-worker-pool
Open

perf(NEGGIA-001): worker pool replacing GLOBAL_HANDLE singleton#28
maxburian wants to merge 7 commits into
masterfrom
feature/NEGGIA-001-worker-pool

Conversation

@maxburian

Copy link
Copy Markdown
Contributor

Summary

  • Stage 1 of Tier-1 perf overhaul per XDS-037 RFC (docs/plans/tier-1-performance.md).
  • Replaces GLOBAL_HANDLE singleton with NUM_WORKERS=16 H5DataCache pool; each worker owns its own H5File (per-worker mmap = per-worker kernel readahead state — load-bearing for the GeeseFS-S3 concurrency win).
  • plugin_get_data dispatches by frame_number % NUM_WORKERS; lock-free hot path; per-worker state thread-confined.
  • The 4 sacred plugin_* C ABI symbols retain byte-identical signatures (downstream XDS-fork tools/neggia-version.txt bump stays clean).
  • External single-open contract preserved.

Concurrency-safety argument

Per audit Inv-A (see docs/audits/NEGGIA-001.md): all dataCache->* writes happen during plugin_open + plugin_get_header; plugin_get_data reads only. After header phase, each worker's H5DataCache is effectively immutable. Per-worker ownership + frame-modulo dispatch means zero shared mutable state on the hot path — no mutex / atomic / condvar needed for plugin_get_data.

Local verification (Mac arm64)

AT Test Result
AT-1 ABI parity (nm vs docs/abi-baseline.txt) ✓ PASS
AT-2 Existing 8 ctests + new concurrent ✓ PASS (9/9)
AT-3 Test_XdsPluginConcurrent (16 threads × 100 calls, bit-equality vs single-threaded reference) ✓ PASS
AT-4 TSan clean on Test_XdsPluginConcurrent ✓ PASS (0 reports)
AT-5 Helgrind clean ⏸ DEFERRED (no valgrind on macOS arm64; verify on Linux CI)
AT-6 Bit-exact regression vs master baseline ✓ PASS (9 fixtures byte-identical: eiger1+eiger2; uint8/uint16/uint32; BSLZ4/LZ4/uncompressed; 0/2 data-file variants)
AT-7 Source-line cap 67 / 50 (reporter override; see ticket Notes — precedent XDS-036)
AT-8 Scientists-in-cloud GeeseFS-S3 benchmark (≥1.3× wall-clock) ⏸ DEFERRED to NEGGIA-005 release-time validation

Reporter-direct cap exemption

Authorized 2026-05-29 by Max Burian. Rationale: worker-pool introduction is structurally one logical change. The framework-discipline split into NEGGIA-001 (struct prep) + NEGGIA-006 (K-expansion) would introduce a no-op intermediate commit (K=1 pool that's a vector-wrapped singleton with identical behaviour); cleanest delivery is a single PR. Cap reaffirmed for subsequent NEGGIA-NNN tickets. Sibling housekeeping ticket pending to update neggia-deep-audit skill body's cap-projection methodology (net delta → sum).

Precedent: XDS-036 (2026-05-05, 155 cap units / 3.1× cap).

Files changed (8)

  • src/dectris/neggia/plugin/H5ToXds.cpp (EDIT, +60/-20 src lines)
  • src/dectris/neggia/test/Test_XdsPluginConcurrent.cpp (NEW, ~130 lines; test-cap-exempt)
  • src/dectris/neggia/test/CMakeLists.txt (EDIT, +14 lines; framework-exempt)
  • tools/regress_bitexact.sh (NEW +x, ~110 lines; framework-exempt)
  • CHANGELOG.md (EDIT)
  • docs/specs/NEGGIA-001.md (NEW, full spec)
  • docs/audits/NEGGIA-001.md (NEW, full audit + Minimal Patch Proposal section)
  • tickets/open/NEGGIA-001-…md (workflow log updates + reporter-override Notes)

Test plan

  • CI green (whatever upstream neggia CI exists; will surface after PR open)
  • Helgrind verification on Linux (separate session if needed)
  • Eventually: scientists-in-cloud GeeseFS-S3 benchmark per NEGGIA-005

Workflow

Per the cross-repo agent framework (docs/architecture/agent-framework.md); driven from the sibling xds-fork Claude session. Closes NEGGIA-001.

🤖 Generated with Claude Code

maxburian and others added 7 commits May 29, 2026 21:22
Stage 1 of the Tier-1 perf overhaul per XDS-037 RFC and the plan at
docs/plans/tier-1-performance.md. The plugin layer now holds a pool of
NUM_WORKERS=16 H5DataCache instances, each owning its own H5File
construction (own mmap = own kernel readahead state — load-bearing for
the GeeseFS-S3 concurrency win). plugin_get_data dispatches by
frame_number % NUM_WORKERS for a lock-free hot path; per-worker state
is thread-confined and never mutated post-header.

ABI preservation: the 4 sacred plugin_* C symbols (plugin_open,
plugin_close, plugin_get_header, plugin_get_data) retain
byte-identical signatures. XDS-fork's tools/neggia-version.txt bump
path stays clean. nm parity verified against docs/abi-baseline.txt.

External single-open contract preserved: refuses second plugin_open
while pool active, with the same stderr message ("CAN ONLY OPEN ONE
FILE AT A TIME") verbatim.

Concurrency-safety argument (audit Inv-A): H5DataCache is
write-once-then-immutable after plugin_get_header (all dataCache->*
writes happen during open + header phase; plugin_get_data reads only).
Per-worker ownership gives each thread a private cache; dispatch by
frame_number % K eliminates shared mutable state on the hot path. No
mutex, no atomic, no synchronization required for get_data.

New Test_XdsPluginConcurrent (16 threads x 100 plugin_get_data calls
with randomised frame numbers; bit-equality assertion against a
single-threaded reference computed up-front) verifies the concurrency
surface. Test-cap-exempt; wired into src/dectris/neggia/test/
CMakeLists.txt with dl + pthread + gtest.

tools/regress_bitexact.sh (framework-exempt): inline-compiles a small
dlopen+plugin_* runner; runs against baseline + candidate .so;
byte-compares every frame of every fixture under given fixture dirs.
Used for AT-6 verification.

Local verification (Mac arm64, M-series):
  - AT-1 ABI parity:                                PASS
  - AT-2 Existing 8 ctests:                         PASS (9/9 incl. new)
  - AT-3 Test_XdsPluginConcurrent baseline:         PASS
  - AT-4 TSan clean on Test_XdsPluginConcurrent:    PASS (0 reports)
  - AT-5 Helgrind:                                  DEFERRED to Linux CI
                                                    (valgrind unavailable
                                                    on macOS arm64)
  - AT-6 Bit-exact regression vs master baseline:   PASS (9 fixtures
                                                    byte-identical:
                                                    eiger1+eiger2;
                                                    uint8/uint16/uint32;
                                                    BSLZ4/LZ4/uncompressed)
  - AT-7 Cap units:                                 67 / 50 (reporter
                                                    override; see ticket
                                                    Notes; precedent
                                                    XDS-036)
  - AT-8 Benchmark (>=1.3x GeeseFS-S3):             DEFERRED to NEGGIA-005
                                                    release-time
                                                    validation by
                                                    scientists-in-cloud

Reporter-direct cap exemption (Max Burian, 2026-05-29): 67/50
~= 1.3x cap. Rationale documented in ticket Notes: worker-pool
introduction is structurally one logical change; the framework-
discipline split would introduce a no-op intermediate commit (K=1
pool that's a vector-wrapped singleton); cleanest delivery is a
single ticket. Cap reaffirmed in force for all subsequent
NEGGIA-NNN tickets. Sibling housekeeping: update
neggia-deep-audit skill body's OQ cap-projection methodology to
use sum (not net delta) — to be filed post-NEGGIA-001 close.

Spec: docs/specs/NEGGIA-001.md
Audit: docs/audits/NEGGIA-001.md (+ Minimal Patch Proposal section)
Closes NEGGIA-001.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #28 CI failed at "Configure CMake" on every lane (~18s fast-fail).
Two pre-existing issues in the neggia repo combined with cmake 4.x
runner toolchain:

1. .github/workflows/main.yml targets RETIRED runner images:
   ubuntu-18.04 (retired 2023), ubuntu-20.04 (retired 2025-04),
   macos-10.15 (retired long ago), ubuntu-16.04 (gone).
2. Vendored googletest's CMakeLists declares
   cmake_minimum_required(VERSION 2.6.4) which cmake 4.x (shipped on
   GitHub macos-14+ runners) has dropped support for.

Both are the same issues dectris-cloud/xds XDS-039 hit when wrapping
neggia in its CI gate, fixed there via tools/build_neggia.sh. Fixing
at the neggia-side here:

- Replace runner labels:
  - macos-10.15 → macos-14
  - macos-latest → keep (currently macos-14)
  - ubuntu-18.04 → ubuntu-22.04
  - ubuntu-20.04 → ubuntu-24.04
  - ubuntu-16.04 → ubuntu-22.04
- Update gcc lanes: 4.8 → 11, 10 → 13 (modern bounds)
- Add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to cmake configure (cmake 4.x
  policy workaround for the vendored googletest's old
  cmake_minimum_required declaration; safe no-op on cmake 3.x)
- Upgrade actions/checkout@v2 → @v4
- Add fail-fast: false so a single-lane fail doesn't kill the matrix
- macOS Intel lane added (macos-15-intel) to keep the Intel coverage
  that was previously only available via macos-10.15

Also preempts an issue XDS-039 hit on Linux: modern gcc-13+ no
longer transitively includes <cstdint> via the gtest header chain,
breaking DatasetsFixture.h's use of uint16_t (line 24). Adding
#include <cstdint> directly to the header makes the test build
robust against stricter toolchains.

Scope expansion on NEGGIA-001 acknowledged: the existing CI was
unable to validate the PR. Fixing the workflow is a precondition
for merge. If the strict-discipline path had been taken, this would
have been a sibling ticket merged first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… Linux

PR #28 macOS lanes now green; Linux lanes (ubuntu-22.04/24.04 +
gcc-11/13) fail at build time on:

  third_party/googletest/googletest/src/gtest-death-test.cc:1224:24:
  error: 'dummy' may be used uninitialized [-Werror=maybe-uninitialized]
  cc1plus: all warnings being treated as errors

The vendored googletest version is too old for modern gcc's
stricter maybe-uninitialized analysis; gtest's own CMakeLists
enables -Werror which then trips the build break. Apple clang
on macOS-14/15 doesn't enable this warning by default — hence
the macOS lanes pass.

Fix: set CXXFLAGS=-Wno-error=maybe-uninitialized as a job-level
env var (Linux lanes only; macOS unchanged). Demotes the
maybe-uninitialized check from error back to warning;
applies globally to the build but is targeted at the gtest
TU (neggia's own code doesn't trip the warning, verified by
local Mac arm64 build).

Alternative considered: bump the googletest submodule to a newer
version. Rejected: changes the submodule pin which is out of
NEGGIA-001 scope and risks other behaviour changes. The flag
override is the surgical fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…emset on Linux

Two std::memset calls (line 60 SetUp + line 112 worker thread_info init)
need an explicit #include <cstring> on Linux gcc-11+/13+. Apple clang
on macOS-14/15 pulls it in transitively via the gtest header chain
and didn't catch this; modern Linux STL is stricter.

Same pattern as the cstdint preempt landed earlier in this PR —
both are "stricter modern toolchain doesn't transitively include
what older ones did" issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
So that downstream consumers (XDS-fork CI bumping tools/neggia-version.txt
to this SHA; scientists-in-cloud validation nodes per AT-8) can download
a pre-built plugin without rebuilding, add actions/upload-artifact@v4
to each lane.

Artifact naming: dectris-neggia-<os>-<build-type>-{cc|gcc<ver>}
  e.g. dectris-neggia-ubuntu-22.04-Release-cc
       dectris-neggia-macos-14-Release-cc
       dectris-neggia-ubuntu-24.04-Release-gcc13

12 artifacts per CI run (matching the 12 lane combinations). 90-day
retention. if-no-files-found: error so a missing build is loud.

Downloads available from PR #28's Actions runs page:
  https://github.com/dectris-cloud/neggia/actions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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