From a01d1b1b7bbe2615f652eabca80f933b457042ea Mon Sep 17 00:00:00 2001 From: Hiroshi Shinaoka Date: Sun, 25 Oct 2026 09:37:01 +0900 Subject: [PATCH] rules(rust): CPU parallelism audit items from the placement design Replaces the generic threading principles with audit items that point at tensor4all-meta's placement design (rules 1 to 13, faer and spindle): fan-out only through strided_exec::Exec, no ambient pool, budgets only shrink, vendor libraries through Exec::external, spindle confined to t4a-faer, no Rayon type in public signatures, ledger-backed concurrency claims. Unmigrated repositories report departures as migration gaps. The audit skill and the common performance rule point to the section. Co-Authored-By: Claude Opus 5.5 --- rules/common/performance.md | 3 +- rules/index.md | 3 +- rules/rust/performance.md | 62 +++++++++++++++++++++----- skills/tensor4all-rules-audit/SKILL.md | 2 + 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/rules/common/performance.md b/rules/common/performance.md index 38e736b..b39d2bf 100644 --- a/rules/common/performance.md +++ b/rules/common/performance.md @@ -47,7 +47,8 @@ legacy tradeoff, a reference path, or a known migration target. - GPU reductions must not hide unbounded serial work in one thread. Use a documented strategy and threshold where a unit-thread fallback is allowed. - CPU threading policy has one source of truth per repository or runtime - object. + object. For Rust crates the placement design and its audit items are in + [`../rust/performance.md`](../rust/performance.md), "CPU Parallelism". ## Evidence diff --git a/rules/index.md b/rules/index.md index e98193a..16ac764 100644 --- a/rules/index.md +++ b/rules/index.md @@ -39,7 +39,8 @@ applicable workflow and language rules. - [`rust/index.md`](rust/index.md): entry point; unsafe boundary, unit-test organization, debug/enum hygiene. - [`rust/performance.md`](rust/performance.md): allocation, slicing, linalg, - GPU kernels, uninitialized/scratch acquisition, build profiles, threading. + GPU kernels, uninitialized/scratch acquisition, build profiles, CPU + parallelism (the placement token and the spindle boundary, as audit items). - [`rust/numerical.md`](rust/numerical.md): numerical correctness, AD validation, typed-error classification. - [`rust/build-artifacts.md`](rust/build-artifacts.md): where Cargo and compiler diff --git a/rules/rust/performance.md b/rules/rust/performance.md index 1830d54..af57084 100644 --- a/rules/rust/performance.md +++ b/rules/rust/performance.md @@ -126,15 +126,53 @@ - Add regression coverage for both contracts: the uninitialized path stays unsafe or `MaybeUninit`-typed, and the zeroed path is safe. -## Threading Principles - -- One repository source of truth for parallel thresholds (a declared policy - mechanism), not per-op copies of the same decision. -- Short-circuit to the serial kernel when the effective thread count is one. -- Library kernels must not reach for an ambient global thread pool outside the - declared policy mechanism. Non-provider parallel work executes within the - repository/runtime-owned execution context, so thread policy (including - one-thread serial execution) is decided at that single source of truth. -- Provider-owned threading (BLAS/OpenMP-style) is controlled by provider - variables documented per repository; library code must not derive its own - pool policy from an ambient runtime. +## CPU Parallelism + +The design is +[`tensor4all-meta/docs/design/core-boundary-and-execution-ownership.md`](https://github.com/tensor4all/tensor4all-meta/blob/main/docs/design/core-boundary-and-execution-ownership.md), +section "Where placement is decided" (its rules 1 to 13 and "faer and spindle"), +with the walkthroughs +[`cpu-placement-token.md`](https://github.com/tensor4all/tensor4all-meta/blob/main/docs/design/cpu-placement-token.md) +and [`spindle.md`](https://github.com/tensor4all/tensor4all-meta/blob/main/docs/design/spindle.md). +The design is the source of truth; the items below are what an audit checks. +They apply to code that runs on `strided_exec::Exec`. In a repository not yet +migrated to it, report each departure as a migration gap with its location, +not as a defect. + +- **Fan-out only through the token.** Outside `strided-exec`, no crate calls + `rayon::scope`, `join`, `spawn`, `par_iter` / `par_chunks`, + `ThreadPool::install`, or, in kernel crates, `std::thread::spawn` / + `scope`. Check: `cargo deny` bans `rayon` except through the listed + wrappers, and clippy `disallowed-methods` lists the thread calls. +- **No ambient pool.** A serial token runs on the calling thread and nothing + falls back to the global Rayon pool. Check: each consumer keeps a + `strided_exec::ledger::global_pool_unstarted()` test in its own process. +- **Decide before entering.** Choose `jobs` and `grain` from `budget()` and + run a budget of one inline; never enter a pool to find out whether + parallelism pays. +- **Budgets only shrink.** A token is not cloned or stored in plans and caches, + a callee does not re-derive or raise a budget, and a split hands each child + a token whose budgets add up to the parent's (`fork`, `for_ranges`, + narrowing with `limit`, sequential phases with `reborrow`). +- **One source of truth for policy.** Parallel thresholds, `WorkModel` and + `BatchPolicy` live once, in the provider crate; per-op copies of the same + decision are a finding, and policy code starts no tasks. +- **Vendor libraries through `Exec::external`.** faer and vendor BLAS receive a + plain width from `external`; no adapter derives a pool or thread count from + an ambient runtime. BLAS/OpenMP worker teams stay provider-controlled and + are documented per repository. +- **spindle stays inside t4a-faer.** No other crate depends on spindle or opens + a `with_lock` region (check: `cargo deny` with `wrappers` listing only + `t4a-faer`). A batched caller such as tlinalg gives every slot to the batch + with `Par::Seq` items when the batch is at least the budget, and otherwise + forks the token with one `external` per item. Inside t4a-faer, every + parallel kernel reachable from a region dispatches through spindle; plain + Rayon code there runs on one thread. +- **No Rayon type in a public signature.** Check: `cargo public-api` of + `strided-exec`, `tcontract-exec` and `tenferro-cpu` contains no `rayon::`. +- **Barrier-bearing kernels use `broadcast`.** A refusal takes the + barrier-free path; the participant count is never clamped silently. +- **Concurrency claims need the ledger.** A claim that work stays within a + budget is shown with the `strided-exec` ledger's peak at the root token, or, + inside faer or a vendor library, measured at the kernel boundary. A worker + index or a pool size is not evidence. diff --git a/skills/tensor4all-rules-audit/SKILL.md b/skills/tensor4all-rules-audit/SKILL.md index 8d8522f..127b1df 100644 --- a/skills/tensor4all-rules-audit/SKILL.md +++ b/skills/tensor4all-rules-audit/SKILL.md @@ -15,6 +15,8 @@ An audit request does not by itself authorize implementation, issues or PRs. files. Read applicable rules and referenced decisions, not the entire workspace by default. A local or uncommitted target is valid; describe it accurately. Do not require a push or a GitHub-visible commit to inspect local work. + Code that fans out on the CPU (Rayon, `strided-exec`, faer, spindle) is + audited against `rules/rust/performance.md`, "CPU Parallelism". 2. **Inspect the real path.** Read enough source to understand the contract, callers and existing checks. Use existing API documentation when helpful; do not build release-mode documentation as a default audit prerequisite.