Repository navigation
Conversation
…426) `compute_block_sizes` took one element size for all operands and the callers passed the largest one, so a mixed-size operation under-used the budget for its smaller operand: the footprint was overstated and the cache-line rounding was computed at the wrong size. Strided.jl forms byte strides per array (`map((s, stride) -> s .* stride, sizeof.(eltype.(arrays)), strides)`). - `compute_block_sizes`, `reduced_block_sizes`, `memory_region`, `region_in` and `Rows` take `elem_sizes: &[usize]`, one per entry of `strides_list`, and form each operand's byte strides, region and cache-line rounding from its own size. Stride orders and costs stay on the element strides, as the reference does (orders are unchanged by a positive multiplier; a zero size gives order 1 on every axis). - The exit that keeps every axis now needs every element size positive: a zero-size operand's constant order 1 is the global minimum at every recursion level, so another operand's larger order no longer matches it. All zero sizes still keep the full dimensions. - `execution::build_plan_fused(..., elem_size: usize)` stays as the compatibility wrapper and fills a uniform slice, so `strided-fused` is unchanged and nothing public is added. - Typed callers pass `size_of` of each operand in `strides_list` order through one `kernel::pointwise_plan`; `raw_ops` passes both sizes to `tile_blocks` and `exits_before_metadata`, which now agree with the no-reuse check they sit beside. Tests: `model_blocks_sized` is the per-operand oracle; the sweep runs again with per-operand sizes, the same-size sweep is unchanged, and three ZST cases pin the contract. Checked with Strided.jl 2.6.6 itself (per-array `sizeof`): 2x4096x8 f64<-u8 gives (2, 1816, 1), 128x128 gives (57, 56), 128x128 c64<-f64 gives (40, 32), matching the implementation.
Repository rules reviewRepository rules review (edcf71fafb91f949553fb3f7d6df94af6a0d6e04...2b0ef866361f2d5e83487c8725d67449a325cc56) Verdict: pass Findings: - [warn] llm-skipped (External LLM Review) <unknown>: External LLM review was skipped External LLM review is permanently disabled; deterministic checks only. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #426.
block::compute_block_sizestook one element size for every operand, and the callers passed the largest one (size_of::<D>().max(size_of::<A>())). A mixed-size operation therefore overstated its smaller operand's footprint, so theBLOCK_MEMORY_SIZEcomparison and the cache-line rounding were computed at the wrong size. Strided.jl forms byte strides per array:map((s, stride) -> s .* stride, sizeof.(eltype.(arrays)), strides)(src/mapreduce.jl:201, 2.6.6).What changed
strided-basic/src/block.rs):compute_block_sizes,reduced_block_sizes,memory_region,region_inandRowstakeelem_sizes: &[usize], one per entry ofstrides_list, and form each operand's byte strides, region term and cache-line rounding from its own size. Stride orders and costs stay on the element strides, as the reference does: a positive size is an exact positive multiple, so the order is unchanged, and a zero size makes every byte stride 0, hence order1on every axis.1is the global minimum at every recursion level, so another operand's larger order stops matching it and the keep-first recursion continues; the exit would be unsound for mixed zero/non-zero sizes. All-zero sizes still keep the full dimensions (all orders1everywhere). This was caught in the pre-implementation review and is pinned below.execution::build_plan_fused(..., elem_size: usize)keeps its signature and fills a uniformInlineVec(inline, no heap allocation), sostrided-fusedis untouched and no public item is added.map_view.rs(5 sites),ops_view.rs(5),reduce_view.rs(2),update_view.rs(run_updatetakeselem_sizes),raw_ops.rs(tile_blocks,exits_before_metadata) passsize_ofper operand instrides_listorder, through onekernel::pointwise_planthat owns the small/large branch instead of 13 copies.raw_ops's tiling decision now agrees with the no-reuse decline it sits beside (the issue's point). The reduce entry inops_view.rsno longer includes the accumulator typeR, which has no strides.Zero-element-size contract (now explicit)
A zero-size operand contributes one cache line to the region and stride order
1on every axis. It never shrinks a block by itself, but it is not neutral in a mixed operation: it is the global minimum on every axis, so the other operands' increasing orders no longer satisfy the keep-first condition. All-zero-size operand sets keep the full dimensions. Three tests pin this (a_zero_sized_source_stops_the_increasing_strides_exit,a_zero_sized_destination_leaves_the_reduction_to_the_source,a_zero_element_size_keeps_every_axis_for_any_operand_count).The all-
1-orders convention for a zero-size operand is a deliberate Rust decision, not Julia parity: Strided.jl ranks element strides (map(indexorder, strides)), so it would rank a zero-size operand's element strides instead. The pre-existinga_zero_element_size_keeps_every_axis_for_any_operand_counttest already pinned that convention for the all-zero case; this change makes it per operand and states it.Cross-check against Strided.jl 2.6.6 itself
Loading the installed package (
~/.julia/packages/Strided/gJBvl) and calling its own_computeblockswith per-arraysizeofgives exactly the block vectors of this implementation:The pre-#426 wiring gave
(2, 1024, 1),(64, 32)and(32, 32)for the three mixed rows.Verification
Red-first, both pasted from real runs on this branch:
The second red is the one that matters for "the model is not the only defect class": with the size seam reverted, the end-to-end mixed
map_intotest still passes (values are correct under either tiling), and only the plan-level test fails. A caller-side&[max; N]literal is not observable through the public entries at all, which is why the pin sits on the shared seam every typed entry feeds verbatim.Measurement
Shared host
primerose(64 cores, other users' jobs running; broccoli was handed over to Terasaki-san on 2026-11-03 and is not dispatched). One L3 domain: the harness is pinned withtaskset -c 24, and the agent process was pinned to0-15withtaskset -apcso it never shared the measured core.Exec::serial()(1 thread, verified: the harness builds onlyExec::serial()), release build, identical harness source for both revisions (only thestrided-basicpath dependency differs), 3 cycles of ABBA (6 samples per arm), medians,boost=1, governorschedutil, load average 3.7-8.9 during the runs.map_intof64<-u8 mixed, 128x128 (block 64x32 -> 57x56)map_intof64<-u8 mixed, 2x4096x8 (block 2x1024x1 -> 2x1816x1)replay_map_rawf64<-u8 mixed, 2x4096x8map_intof64<-f64 same, 128x128map_intof64<-f64 same, 2x4096x8replay_map_rawf64<-f64 same, 2x4096x8The three same-size rows run provably identical code (the sweep test proves the block vectors are unchanged), so their +1.2% to +5.0% spread is the harness's drift/alignment floor between two independently compiled binaries. Only the mixed 128x128 map is outside it; the two mixed 2x4096x8 rows are inside it.
copy_intois single-dtype by contract (dest: &mut StridedViewMut<T>, src: &StridedView<T, Op>), so its mixed-size form is the genericmap_intorow above;replay_copy_rawis single-dtype too.Review
review: gpt-6.1-sol pre-review (plan) — 1 blocker, 2 important, 1 minor: the mixed-ZST exit rule was wrong (fixed: the increasing-strides exit now requires every size positive),
copy_intois single-dtype so the mixed row is stated asmap_into, crossing the budget alone does not change blocks so the fixtures use non-fusing transposed strides, and the five-helper family became one non-generic helper. Nothing left unfixed.review: gpt-6.1-sol post-review (diff) — 1 important, 3 minor: the important one (public rustdoc linking to a private item,
-D warningsdocs gate) was already fixed before the report was read; the three minors are applied in this commit (two test names that claimed more than they assert, and stale "larger element size" wording inraw_opscomments). Nothing left unfixed.