Repository navigation
Conversation
…ensoradd! per subblock Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hold Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
... and 23 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Do you happen to have an idea about whether the speedup is mostly because of the pre-computed linear indices, or just from the chunking, i.e. reducing the overhead? I recently attempted to make the Strided calls a lot less overhead, especially for small tensors, so I'm hoping that just the chunking could suffice. See also the work in #584, where I also introduced some chunking (there the main motivation was multithreading, since for many small blocks the contention on the single atomic Int is quite large). |
The test suite runs with one transformer task, so the path that splits the groups over tasks by destination offset was not covered. Compare `permute` and `permute!` with β ≠ 0 using three tasks against the serial result, for a fermionic U(1) tensor (unique fusion) and an SU(2) tensor with subblocks on both sides of the group threshold (generic fusion, accumulating groups). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I tried to disentangle the two by running the different steps separately on the same transformer data (Strided 2.6.6, so including your recent changes;
It turns out that both matter, but most of the time saved comes from avoiding the per-call overhead. For the five tensors with subblocks of at most 16 entries (two of which are shown above), a single Strided call still costs 440–570 ns, and simply replacing it by a plain loop over each subblock already makes things 3–7× faster. Grouping the subblocks by shape doesn't do much by itself, but it's precisely what makes the precomputed index lists cheap, since we only need one list per shape. Those index lists then give another 2.5–12×. Once the subblocks reach 256 entries, all of the hand-written loops perform about the same. A second run gave the same picture. I haven't tried QuantumKitHub/Strided.jl#84 here. I also ran #584 next to main and this PR on the four fℤ₂ × U(1) tensors from the description (
The node was still busy, so these are noisier than the numbers above, but #584 stays within 2× of main here, since it still calls |
On the CPU,
permute,braidandtransposecalltensoradd!once per fusion-tree subblock, which costs about 0.5–0.7 µs per call independent of the subblock size. For tensors with many small subblocks, such as fermionic U(1)-symmetric PEPS tensors, this dominates:permute!becomes 25–125× slower than copying the data. This is the same per-subblock pattern as #595, and the CPU counterpart of #508.This PR collects the subblocks with at most 1024 entries into groups of equal size and strides when the tree transformer is built, and caches them with it: per group the relative positions of the entries in the destination and the permuted source, per subblock the offsets and the coefficient.
add_transform_kernel!then transforms every group in a single loop, while larger subblocks still go throughtensoradd!one by one. For sectors with multiple fusion channels, the recoupling of small subblocks is applied term by term in the same loops instead of through pack,mul!and unpack. With multiple transformer threads, the tasks handle disjoint ranges of destination offsets.permute!into a preallocated destination, one thread, best of 30, in ms (p1 = ((1, 4, 2), (5, 3, 6))onVχ ⊗ VD ⊗ VD' ← Vχ ⊗ VD ⊗ VD'):copy!Vχcharges ±2, deg. 4;VD±1, deg. 1Vχcharges ±8, deg. 2;VD±1, deg. 1Vχcharges ±2, deg. 16;VD±1, deg. 1Vχcharges ±2, deg. 32;VD±1, deg. 2Vχj ≤ 1, deg. 2;VDj ≤ 1/2, deg. 1Vχj ≤ 1, deg. 16;VDj ≤ 1/2, deg. 2Tensors whose subblocks are all larger than 1024 entries take the same path as before. The existing permutation tests only have subblocks of up to 12 entries, so I added one testset with subblocks on both sides of the threshold.
The groups add about 25 bytes per small subblock to each cached transformer; the GPU extension builds them as well but does not use them.
Reproducer
main:
this PR:
🤖 Generated with Claude Code