Skip to content

perf(packet): wire SIMD Internet checksum for long payloads - #492

Open
dyxushuai wants to merge 2 commits into
arcboxlabs:masterfrom
dyxushuai:perf/packet-checksum-simd
Open

perf(packet): wire SIMD Internet checksum for long payloads#492
dyxushuai wants to merge 2 commits into
arcboxlabs:masterfrom
dyxushuai:perf/packet-checksum-simd

Conversation

@dyxushuai

Copy link
Copy Markdown

checksum_add on long buffers still used the scalar loop even though
NEON / SSSE3 implementations already existed. TCP/UDP full-segment
checksums (and anything else going through checksum_add) pay that
cost on large payloads.

For buffers ≥ 64 bytes, accumulate with the existing SIMD path (return
the ones'-complement sum, then fold as before). Shorter headers stay
scalar so setup cost does not dominate. No new dependencies.

Microbench on x86_64 (release), scalar vs SIMD:

cargo run -p arcbox-packet --example checksum_bench --release
len scalar SIMD speedup
64 B ~148 Gbit/s ~129 0.9× (threshold keeps short path scalar)
1500 B ~204 ~405 ~2.0×
64 KiB ~209 ~413 ~2.0×

Correctness: long buffers match pure scalar sum; existing checksum
tests still pass.

This is orthogonal to inject IRQ coalescing — do not expect multi-flow
iperf to move from this alone.

Route checksum_add through the existing NEON/SSSE3 accumulator for
buffers of 64 bytes or more; short headers stay scalar. Add a small
microbench example for scalar vs SIMD throughput.
Copilot AI review requested due to automatic review settings July 22, 2026 07:34
Mark the legacy NEON entry #[deprecated] and use consistent expression-style
cfg branches in checksum_add_fast.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR speeds up ArcBox’s Internet-checksum hot path by wiring the existing NEON/SSSE3 implementations into checksum_add for long buffers, improving TCP/UDP full-segment checksum throughput while keeping short headers on the scalar loop to avoid SIMD setup overhead.

Changes:

  • Add a length threshold and route checksum_add to a new “fast path” that selects NEON on AArch64 and SSSE3 on x86_64 (with scalar fallback).
  • Refactor the SIMD routines to return the raw ones’-complement sum (u32) and keep folding/complementing centralized in checksum_fold.
  • Add a microbench example plus additional long-buffer correctness tests to guard the new dispatch behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
common/arcbox-packet/src/checksum.rs Adds SIMD dispatch for long buffers, refactors SIMD helpers to return sums, and extends tests to cover long-payload correctness.
common/arcbox-packet/examples/checksum_bench.rs Adds a small throughput microbench comparing scalar vs SIMD checksum computation.

Comment on lines +290 to +293
/// Deprecated name kept for external callers that linked the old NEON entry.
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[inline]
Copilot AI review requested due to automatic review settings July 22, 2026 07:38

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — two minor points inline.

Reviewed changes — wires the already-existing NEON/SSSE3 checksum kernels into the common checksum_add for payloads >= 64 bytes, so TCP/UDP full-segment checksums use SIMD on large buffers.

  • SIMD dispatch in checksum_add — buffers >= SIMD_THRESHOLD (64 B) route to checksum_add_fast; shorter stay scalar. This is the hot path behind checksum, tcp_checksum, udp_checksum.
  • Kernels return raw u32 sumchecksum_add_neon / checksum_add_ssse3 now return the un-folded ones'-complement sum so partial sums compose with the pseudo-header before one final checksum_fold; switched to slice::as_chunks::<16>().
  • API tidychecksum_simd kept as a pub test/bench entry, checksum_simd_neon retained as a pub-unsafe shim, and the old comment noise stripped.
  • Tests + bench — added long-buffer scalar/SIMD equivalence tests (4096 B and a 1500 B TCP segment) and a checksum_bench example.

Correctness checks out: scalar and SIMD both compute the true arithmetic word sum, and the u32 accumulators don't wrap below ~512 KB (NEON) / ~1 MB (SSSE3) — far above any packet — so the final fold is identical. The added tests pin this down.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread common/arcbox-packet/src/checksum.rs Outdated
#[cfg(target_arch = "aarch64")]
{
// SAFETY: NEON is mandatory on AArch64.
return unsafe { checksum_add_neon(data) };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ The #[cfg(target_arch = "aarch64")] arm ends in a bare return that becomes the function's only statement after cfg-stripping, which can trip clippy::needless_return on the aarch64 target. Since that lint is -D warnings and aarch64 (Apple Silicon) is the P0 platform, an x86_64 CI run wouldn't catch it. Please confirm cargo clippy is clean on aarch64, or add #[allow(clippy::needless_return)].

#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[inline]
pub unsafe fn checksum_simd_neon(data: &[u8]) -> u16 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ checksum_simd_neon has no in-repo callers, no #[deprecated] attribute, and the "external callers" rationale is thin for an alpha crate where CLAUDE.md states breaking changes are acceptable. Consider dropping it rather than carrying a dead pub-unsafe shim.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — this run covers commit 82b17fe, which addresses both minor points from the prior review.

  • checksum_add_fast aarch64 arm — the cfg-gated return was dropped in favor of a bare block tail expression, removing the clippy::needless_return risk on the P0 aarch64 target.
  • checksum_simd_neon — kept for backward compat but now carries an explicit #[deprecated] attribute and a doc pointer to checksum / checksum_simd, so the retained shim is intentional rather than stray dead code.

Both resolutions are clean and introduce no new behavior — the SIMD dispatch and correctness properties reviewed previously are unchanged.

Pullfrog  | Fix it ➔View workflow run | Using Claude Opus𝕏

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines 26 to +31
pub fn checksum_add(data: &[u8]) -> u32 {
if data.len() >= SIMD_THRESHOLD {
checksum_add_fast(data)
} else {
checksum_add_scalar(data)
}
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR enables SIMD checksum accumulation for long packet buffers. The main changes are:

  • Routes buffers of at least 64 bytes through NEON or SSSE3 when available.
  • Keeps scalar accumulation for short buffers and unsupported CPUs.
  • Adds long-buffer checksum tests and a throughput benchmark.

Confidence Score: 5/5

This looks safe to merge.

No blocking issues found in the changed code. Architecture gating and the scalar fallback cover supported targets. SIMD remainder and odd-byte handling match the scalar implementation.

T-Rex T-Rex Logs

What T-Rex did

  • Compared the SIMD correctness test logs and confirmed the new long-buffer tests pass.
  • Verified the SIMD benchmarks exist and show a complete, successful release output for all lengths.
  • Validated that the host supports x86_64 with SSSE3 as required for the tests.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
common/arcbox-packet/src/checksum.rs Adds threshold-based SIMD dispatch, exposes scalar accumulation, and makes architecture-specific implementations return an unfolded sum.
common/arcbox-packet/examples/checksum_bench.rs Adds scalar-versus-SIMD throughput measurements with correctness checks across several payload sizes.

Reviews (1): Last reviewed commit: "refactor(packet): deprecate checksum_sim..." | Re-trigger Greptile

@PeronGH PeronGH self-assigned this Jul 31, 2026

@PeronGH PeronGH left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — nice catch. checksum_add really was leaving the existing NEON/SSSE3 code unreachable, and the refactor to have the SIMD paths return the unfolded sum (rather than a folded u16) is the right shape: it composes cleanly with the pseudo-header accumulation in tcp_checksum/udp_checksum, which the old fold-inside-SIMD API couldn't. The example bench is a welcome addition too.

One thing worth measuring before this lands: our P0 target is macOS Apple Silicon, and the numbers look different there. On an M-series host I get (release, 3 runs, very reproducible):

len scalar this PR (NEON)
64 B 89 Gbit/s 130 1.5x
1500 B 127 166 1.3x
9000 B 150 115 0.77x
16 KiB 171 112 0.66x
64 KiB 190 124 0.65x

So it's a win at MTU size but a regression above ~4 KiB. That matters because the hottest consumer is finalize_virtio_net_checksum (virt/arcbox-vmm/src/device/checksum.rs), and we advertise VIRTIO_NET_F_CSUM unconditionally alongside GUEST_TSO4/6 — so guest TX hands us GSO segments up to 64 KiB, which is exactly the range that regresses.

The cause looks like the accumulator rather than the idea: checksum_add_neon chains every vpadalq_u16 onto the previous one, so the loop is latency-bound instead of throughput-bound, while LLVM already auto-vectorizes the scalar loop pretty well on aarch64 (scalar throughput climbs with length, 89 -> 190 Gbit/s, which is the tell). I tried a variant with several independent accumulators summed at the end and it went to ~750 Gbit/s at 64 KiB — 2.7x over scalar, byte-identical results — so there's a lot on the table here. The SSSE3 path has the same shape with 2 accumulators, so it may have headroom as well.

Smaller notes:

  • cargo clippy -p arcbox-packet warns missing_safety_doc on checksum_simd_neon, and we gate on zero warnings. Since rg finds no callers of it anywhere in the workspace and the crate is unpublished, I'd suggest just deleting it rather than deprecating — breaking changes are explicitly fine here (see CLAUDE.md).
  • SIMD_THRESHOLD = 64 with >= means a 64 B buffer does take the SIMD path, so the x86 0.9x at 64 B in your table applies rather than being avoided. Might be worth 65, or measuring where the crossover actually sits.
  • test_checksum_add_fast_matches_scalar_long uses 4096 B, an exact multiple of 16, so the SIMD remainder/odd-byte tail isn't covered (test_checksum_simd at 100 B leaves an even 4-byte remainder). I swept lengths 0..=600 against scalar locally and everything agrees — the code is correct — but a small length-sweep test would lock that in, since the tail is where checksum bugs like to hide.

Happy to help dig into any of this if useful.

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.

3 participants