Skip to content

perf(crypto): keccak256 is single-lane scalar, no SIMD batching #6947

Description

@edg-l

keccak256 on x86_64/aarch64 is the CRYPTOGAMS/OpenSSL single-lane scalar keccak-f1600 (crates/common/crypto/keccak/mod.rs, keccak1600-*.s). Every block hashes many independent inputs: trie nodes during root computation, the KECCAK256 opcode, address derivation, tx/receipt hashing. There's no multi-lane path.

1. 4-way AVX2 batched keccak (main win)

A KeccakP1600times4 kernel runs 4 permutations in ~1.3-1.6x one scalar permutation, so ~2.5-3.5x throughput on independent hashes.

  • AVX2 is already forced on the prod target (.cargo/config.toml), so no runtime dispatch.
  • Trie hashing batches cleanly: siblings are hash-independent. Buffering points already exist in trie_sorted.rs (write-queue) and node.rs (commit / compute_hash_no_alloc).
  • Add keccak256_batch(&[&[u8]]) next to Crypto::keccak256. Prototype a portable u64x4 kernel (~200 lines) before vendoring XKCP asm.
  • Trie already parallelizes across subtries, so this multiplies per-thread throughput. Microbench first (item 5).

2. ARM FEAT_SHA3 (nearly free)

keccak1600-armv8-*.s already contains SHA3_absorb_cext / SHA3_squeeze_cext (EOR3/RAX1/XAR/BCAX), but Rust only calls the scalar SHA3_absorb. Wire it behind is_aarch64_feature_detected!("sha3"): ~1.5-2x on Apple Silicon / Graviton.

3. Devirtualize the hot call

NodeHash::from_encoded goes through &dyn Crypto::keccak256 (provider.rs:176), paying a vtable call per node and blocking inlining. trie_sorted.rs hard-codes &NativeCrypto but still routes through dyn Crypto. ~1-3%.

4. Hygiene / latent build breaks

  • SHA3_absorb / SHA3_squeeze / KeccakF1600 are unprefixed globals, same symbols as static OpenSSL libcrypto. Any static openssl-sys dep gives a duplicate-symbol link error. Prefix with ethrex_.
  • x86_64 asm is ELF-only (.type, .size, .note.gnu.property) but cfg'd for all x86_64, so darwin/windows builds fail. Gate to target_os = "linux", fall back elsewhere.
  • aarch64 asm is gated linux|macos but imp is arch-only, so aarch64 android/windows/freebsd fail to link.
  • Keccak wrapper uses get_unchecked relying on SHA3_absorb's rem < r return. Use checked indexing.
  • Keccak256::update(&mut self) -> Self returns a ~344 B self.clone() per call. Return &mut Self or ().
  • Restore the CRYPTOGAMS BSD/GPL attribution stripped from the armv8 files.

5. Tests / benches (prerequisite)

No keccak known-answer tests for the asm path and no keccak bench (benches/ has only bls_single_op.rs). Add NIST KATs and a criterion bench before landing the above.

Found during an asm review while profiling BAL merkleization. Note: sstore_bloated merkle is insert-bound, not hash-bound, so batched keccak does not help that case; it helps the KECCAK256 opcode and normal state-heavy blocks.

Metadata

Metadata

Assignees

Labels

performanceBlock execution throughput and performance in general

Type

No type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions