Skip to content

perf(backend-cpu): dense-FP32 fast path for split - #1361

Merged
michalharakal merged 1 commit into
developfrom
perf/split-dense-float
Oct 19, 2026
Merged

michalharakal merged 1 commit into
developfrom
perf/split-dense-float

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

Problem

DefaultCpuOpsBase.split built every output element through dataFactory.init { outIdx -> val inIdx = outIdx.copyOf(); …; tensor.data.get(*inIdx) }. That's two IntArray copies (the copyOf and the vararg spread) plus a boxed value per element. RoPE.applyRoPESplitHalfFull calls split every decode step, and once the Vector API fallback was gone (#1360) this was the top decode allocation site in a BitNet 2B4T JVM profile: about 7.8 GB in a 45 s window.

Change

A dense-FP32 fast path, using the same floatWindowOf / floatResult funnel as the other hot ops (plain and slab-backed data, scope-aware adoption):

  • each chunk is outer contiguous runs of size × inner floats, copied with copyInto;
  • other data keeps the generic path unchanged.

Tests

  • New split_rank4_every_dim_matches_element_mapping: a 2×3×5×4 tensor split on every axis with a remainder chunk. Every element of every part is checked against the source mapping; it also checks negative dim.
  • :skainet-backends:skainet-backend-cpu:jvmTest 491/491 pass; compileKotlinLinuxX64 passes.

Measured

Arduino Ventuno Q, 4× Cortex-A78C, JVM, BitNet 2B4T I2_S, 128 greedy tokens. The patch was backported onto 0.61.0 together with #1360 and the mmap bulk-copy PR:

  • the Arrays.copyOf(int[]) ← split ← RoPE allocation site disappears from the JFR allocation-by-site list;
  • greedy output is byte-identical (md5 52f57d1d…);
  • decode goes 2.34 → 2.39 tok/s (≈ +2 %; on the JVM this mostly reduces allocation, not wall time).

Refs #1357

🤖 Generated with Claude Code

DefaultCpuOpsBase.split built every output element through dataFactory.init with
outIdx.copyOf() plus a vararg-spread get — two IntArray copies and a boxed value
per element. RoPE's split-half rotation calls it every step, which made it the top
decode allocation site once the Vector API fallback was gone (#1357). Dense FP32
(floatWindowOf: plain or slab-backed) now copies each chunk as outer contiguous runs
with copyInto and adopts the result through floatResult; other data keeps the
generic path.

Refs #1357

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michalharakal
michalharakal merged commit adff8ee into develop Oct 19, 2026
16 checks passed
@michalharakal
michalharakal deleted the perf/split-dense-float branch October 19, 2026 10:26
michalharakal added a commit that referenced this pull request Oct 21, 2026
CHANGELOG [0.62.0] - 2026-10-21 with the parity headline over #1363, #1364,
#1368, #1369, #1426, #1427, #1428 and #1430, plus entries for the operator
coverage matrix (#1370), the BitNet decode allocation and threading fixes
(#1359, #1360, #1361, #1362), the ClassificationSupport extraction (#1429)
and the toolchain bumps (Kotlin 2.4.21, kotest 6.2.6, Gradle 9.8.1).
VERSION_NAME, CITATION, Antora version, README bom coordinate, What's New
and contributors, Android tutorial coordinates; regenerated operator and
kernel-support matrices; SKEEP-007 rollout step 3 marked shipped.
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.

2 participants