From 94d8ac7117e7896f4127d5acd38f0bb0fd89d529 Mon Sep 17 00:00:00 2001 From: acud <12988138+acud@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:32:48 -0600 Subject: [PATCH] perf: bmt simd hasher --- go.mod | 2 +- go.sum | 4 +- pkg/bmt/benchmark_test.go | 26 ++- pkg/bmt/bmt.go | 213 -------------------- pkg/bmt/bmt_simd.go | 181 +++++++++++++++++ pkg/bmt/bmt_test.go | 12 +- pkg/bmt/export_goroutine_test.go | 13 ++ pkg/bmt/export_simd_test.go | 16 ++ pkg/bmt/hasher_goroutine.go | 234 ++++++++++++++++++++++ pkg/bmt/hasher_simd.go | 90 +++++++++ pkg/bmt/{pool.go => pool_goroutine.go} | 77 ++++--- pkg/bmt/pool_simd.go | 209 +++++++++++++++++++ pkg/bmt/proof.go | 5 +- pkg/bmt/proof_test.go | 4 +- pkg/bmtpool/bmtpool.go | 2 +- pkg/keccak/keccak.go | 149 ++++++++++++++ pkg/keccak/keccak_amd64.go | 9 + pkg/keccak/keccak_cpu.go | 14 ++ pkg/keccak/keccak_cpu_other.go | 14 ++ pkg/keccak/keccak_other.go | 11 + pkg/keccak/keccak_times4_amd64.s | 11 + pkg/keccak/keccak_times4_linux_amd64.syso | Bin 0 -> 183192 bytes pkg/keccak/keccak_times8_amd64.s | 13 ++ pkg/keccak/keccak_times8_linux_amd64.syso | Bin 0 -> 137176 bytes pkg/storageincentives/proof.go | 6 +- pkg/storageincentives/soc_mine_test.go | 102 +++++----- pkg/storer/sample.go | 12 +- 27 files changed, 1108 insertions(+), 321 deletions(-) create mode 100644 pkg/bmt/bmt_simd.go create mode 100644 pkg/bmt/export_goroutine_test.go create mode 100644 pkg/bmt/export_simd_test.go create mode 100644 pkg/bmt/hasher_goroutine.go create mode 100644 pkg/bmt/hasher_simd.go rename pkg/bmt/{pool.go => pool_goroutine.go} (71%) create mode 100644 pkg/bmt/pool_simd.go create mode 100644 pkg/keccak/keccak.go create mode 100644 pkg/keccak/keccak_amd64.go create mode 100644 pkg/keccak/keccak_cpu.go create mode 100644 pkg/keccak/keccak_cpu_other.go create mode 100644 pkg/keccak/keccak_other.go create mode 100644 pkg/keccak/keccak_times4_amd64.s create mode 100644 pkg/keccak/keccak_times4_linux_amd64.syso create mode 100644 pkg/keccak/keccak_times8_amd64.s create mode 100644 pkg/keccak/keccak_times8_linux_amd64.syso diff --git a/go.mod b/go.mod index cf602914109..4f3f2999d9e 100644 --- a/go.mod +++ b/go.mod @@ -100,7 +100,7 @@ require ( github.com/ipfs/go-log/v2 v2.6.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 github.com/koron/go-ssdp v0.0.6 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/libdns/libdns v0.2.2 // indirect diff --git a/go.sum b/go.sum index fab9e1c2f69..e90b1b1bd84 100644 --- a/go.sum +++ b/go.sum @@ -527,8 +527,8 @@ github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYW github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= diff --git a/pkg/bmt/benchmark_test.go b/pkg/bmt/benchmark_test.go index aa6cc1703dd..0fe014bb0dc 100644 --- a/pkg/bmt/benchmark_test.go +++ b/pkg/bmt/benchmark_test.go @@ -29,6 +29,9 @@ func BenchmarkBMT(b *testing.B) { b.Run(fmt.Sprintf("%v_size_%v", "BMT", size), func(b *testing.B) { benchmarkBMT(b, size) }) + b.Run(fmt.Sprintf("%v_size_%v", "BMT_NoSIMD", size), func(b *testing.B) { + benchmarkBMTNoSIMD(b, size) + }) } } @@ -87,7 +90,7 @@ func benchmarkBMT(b *testing.B, n int) { testData := testutil.RandBytesWithSeed(b, 4096, seed) - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, testPoolSize)) + pool := bmt.NewPool(bmt.NewConf(testSegmentCount, testPoolSize)) h := pool.Get() defer pool.Put(h) @@ -106,7 +109,7 @@ func benchmarkPool(b *testing.B, poolsize int) { testData := testutil.RandBytesWithSeed(b, 4096, seed) - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, poolsize)) + pool := bmt.NewPool(bmt.NewConf(testSegmentCount, poolsize)) cycles := 100 b.ReportAllocs() @@ -127,6 +130,25 @@ func benchmarkPool(b *testing.B, poolsize int) { } } +// benchmarks BMT Hasher with SIMD disabled +func benchmarkBMTNoSIMD(b *testing.B, n int) { + b.Helper() + + testData := testutil.RandBytesWithSeed(b, 4096, seed) + + pool := bmt.NewPool(bmt.NewConfNoSIMD(testSegmentCount, testPoolSize)) + h := pool.Get() + defer pool.Put(h) + + b.ReportAllocs() + + for b.Loop() { + if _, err := syncHash(h, testData[:n]); err != nil { + b.Fatalf("seed %d: %v", seed, err) + } + } +} + // benchmarks the reference hasher func benchmarkRefHasher(b *testing.B, n int) { b.Helper() diff --git a/pkg/bmt/bmt.go b/pkg/bmt/bmt.go index ae3c5e95421..b21584a046c 100644 --- a/pkg/bmt/bmt.go +++ b/pkg/bmt/bmt.go @@ -18,40 +18,6 @@ var ( zerosection = make([]byte, 64) ) -// Hasher is a reusable hasher for fixed maximum size chunks representing a BMT -// It reuses a pool of trees for amortised memory allocation and resource control, -// and supports order-agnostic concurrent segment writes and section (double segment) writes -// as well as sequential read and write. -// -// The same hasher instance must not be called concurrently on more than one chunk. -// -// The same hasher instance is synchronously reusable. -// -// Sum gives back the tree to the pool and guaranteed to leave -// the tree and itself in a state reusable for hashing a new chunk. -type Hasher struct { - *Conf // configuration - bmt *tree // prebuilt BMT resource for flowcontrol and proofs - size int // bytes written to Hasher since last Reset() - pos int // index of rightmost currently open segment - result chan []byte // result channel - errc chan error // error channel - span []byte // The span of the data subsumed under the chunk -} - -// NewHasher gives back an instance of a Hasher struct -func NewHasher(hasherFact func() hash.Hash) *Hasher { - conf := NewConf(hasherFact, swarm.BmtBranches, 32) - - return &Hasher{ - Conf: conf, - result: make(chan []byte), - errc: make(chan error, 1), - span: make([]byte, SpanSize), - bmt: newTree(conf.maxSize, conf.depth, conf.hasher), - } -} - // Capacity returns the maximum amount of bytes that will be processed by this hasher implementation. // since BMT assumes a balanced binary tree, capacity it is always a power of 2 func (h *Hasher) Capacity() int { @@ -91,191 +57,12 @@ func (h *Hasher) BlockSize() int { return 2 * h.segmentSize } -// Hash returns the BMT root hash of the buffer and an error -// using Hash presupposes sequential synchronous writes (io.Writer interface). -func (h *Hasher) Hash(b []byte) ([]byte, error) { - if h.size == 0 { - return doHash(h.hasher(), h.span, h.zerohashes[h.depth]) - } - copy(h.bmt.buffer[h.size:], zerosection) - // write the last section with final flag set to true - go h.processSection(h.pos, true) - select { - case result := <-h.result: - return doHash(h.hasher(), h.span, result) - case err := <-h.errc: - return nil, err - } -} - // Sum returns the BMT root hash of the buffer, unsafe version of Hash func (h *Hasher) Sum(b []byte) []byte { s, _ := h.Hash(b) return s } -// Write calls sequentially add to the buffer to be hashed, -// with every full segment calls processSection in a go routine. -func (h *Hasher) Write(b []byte) (int, error) { - l := len(b) - maxVal := h.maxSize - h.size - if l > maxVal { - l = maxVal - } - copy(h.bmt.buffer[h.size:], b) - secsize := 2 * h.segmentSize - from := h.size / secsize - h.size += l - to := h.size / secsize - if l == maxVal { - to-- - } - h.pos = to - for i := from; i < to; i++ { - go h.processSection(i, false) - } - return l, nil -} - -// Reset prepares the Hasher for reuse -func (h *Hasher) Reset() { - h.pos = 0 - h.size = 0 - copy(h.span, zerospan) -} - -// processSection writes the hash of i-th section into level 1 node of the BMT tree. -func (h *Hasher) processSection(i int, final bool) { - secsize := 2 * h.segmentSize - offset := i * secsize - level := 1 - // select the leaf node for the section - n := h.bmt.leaves[i] - isLeft := n.isLeft - hasher := n.hasher - n = n.parent - // hash the section - section, err := doHash(hasher, h.bmt.buffer[offset:offset+secsize]) - if err != nil { - select { - case h.errc <- err: - default: - } - return - } - // write hash into parent node - if final { - // for the last segment use writeFinalNode - h.writeFinalNode(level, n, isLeft, section) - } else { - h.writeNode(n, isLeft, section) - } -} - -// writeNode pushes the data to the node. -// if it is the first of 2 sisters written, the routine terminates. -// if it is the second, it calculates the hash and writes it -// to the parent node recursively. -// since hashing the parent is synchronous the same hasher can be used. -func (h *Hasher) writeNode(n *node, isLeft bool, s []byte) { - var err error - for { - // at the root of the bmt just write the result to the result channel - if n == nil { - h.result <- s - return - } - // otherwise assign child hash to left or right segment - if isLeft { - n.left = s - } else { - n.right = s - } - // the child-thread first arriving will terminate - if n.toggle() { - return - } - // the thread coming second now can be sure both left and right children are written - // so it calculates the hash of left|right and pushes it to the parent - s, err = doHash(n.hasher, n.left, n.right) - if err != nil { - select { - case h.errc <- err: - default: - } - return - } - isLeft = n.isLeft - n = n.parent - } -} - -// writeFinalNode is following the path starting from the final datasegment to the -// BMT root via parents. -// For unbalanced trees it fills in the missing right sister nodes using -// the pool's lookup table for BMT subtree root hashes for all-zero sections. -// Otherwise behaves like `writeNode`. -func (h *Hasher) writeFinalNode(level int, n *node, isLeft bool, s []byte) { - var err error - for { - // at the root of the bmt just write the result to the result channel - if n == nil { - if s != nil { - h.result <- s - } - return - } - var noHash bool - if isLeft { - // coming from left sister branch - // when the final section's path is going via left child node - // we include an all-zero subtree hash for the right level and toggle the node. - n.right = h.zerohashes[level] - if s != nil { - n.left = s - // if a left final node carries a hash, it must be the first (and only thread) - // so the toggle is already in passive state no need no call - // yet thread needs to carry on pushing hash to parent - noHash = false - } else { - // if again first thread then propagate nil and calculate no hash - noHash = n.toggle() - } - } else { - // right sister branch - if s != nil { - // if hash was pushed from right child node, write right segment change state - n.right = s - // if toggle is true, we arrived first so no hashing just push nil to parent - noHash = n.toggle() - } else { - // if s is nil, then thread arrived first at previous node and here there will be two, - // so no need to do anything and keep s = nil for parent - noHash = true - } - } - // the child-thread first arriving will just continue resetting s to nil - // the second thread now can be sure both left and right children are written - // it calculates the hash of left|right and pushes it to the parent - if noHash { - s = nil - } else { - s, err = doHash(n.hasher, n.left, n.right) - if err != nil { - select { - case h.errc <- err: - default: - } - return - } - } - // iterate to parent - isLeft = n.isLeft - n = n.parent - level++ - } -} - // calculates the Keccak256 SHA3 hash of the data func sha3hash(data ...[]byte) ([]byte, error) { return doHash(swarm.NewHasher(), data...) diff --git a/pkg/bmt/bmt_simd.go b/pkg/bmt/bmt_simd.go new file mode 100644 index 00000000000..b19d2245dfd --- /dev/null +++ b/pkg/bmt/bmt_simd.go @@ -0,0 +1,181 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !purego + +package bmt + +import ( + "github.com/ethersphere/bee/v2/pkg/keccak" +) + +// hashSIMD computes the BMT root hash using SIMD-accelerated Keccak hashing. +// It processes the tree level by level from leaves to root, using batched +// SIMD calls instead of goroutine-per-section. A single thread handles all +// levels since SIMD already provides intra-call parallelism (4-way or 8-way). +func (h *Hasher) hashSIMD() ([]byte, error) { + secsize := 2 * h.segmentSize + bw := h.batchWidth + prefixLen := len(h.prefix) + + // Leaf level: hash each section and write results to parent nodes. + // Single-threaded: SIMD batching (4 or 8 hashes per call) replaces goroutine parallelism. + h.hashLeavesBatch(0, len(h.bmt.levels[0]), bw, secsize, prefixLen) + + // Internal levels: process each level single-threaded (diminishing work). + for lvl := 1; lvl < len(h.bmt.levels)-1; lvl++ { + h.hashNodesBatch(h.bmt.levels[lvl], bw, prefixLen) + } + + // Root level: hash using scalar hasher. + root := h.bmt.levels[len(h.bmt.levels)-1][0] + return doHash(root.hasher, root.left, root.right) +} + +// hashLeavesBatch hashes leaf sections in the range [start, end) using SIMD batches. +func (h *Hasher) hashLeavesBatch(start, end, bw, secsize, prefixLen int) { + buf := h.bmt.buffer + + if bw == 8 { + var inputs [8][]byte + for i := start; i < end; i += 8 { + batch := 8 + if i+batch > end { + batch = end - i + } + for j := 0; j < batch; j++ { + offset := (i + j) * secsize + if prefixLen > 0 { + copy(h.bmt.leafConcat[j][prefixLen:], buf[offset:offset+secsize]) + inputs[j] = h.bmt.leafConcat[j][:prefixLen+secsize] + } else { + inputs[j] = buf[offset : offset+secsize] + } + } + for j := batch; j < 8; j++ { + inputs[j] = nil + } + var outputs [8]keccak.Hash256 + if h.useSIMD { + outputs = keccak.Sum256x8(inputs) + } else { + outputs = keccak.Sum256x8Scalar(inputs) + } + for j := 0; j < batch; j++ { + leaf := h.bmt.levels[0][i+j] + if leaf.isLeft { + copy(leaf.parent.left, outputs[j][:]) + } else { + copy(leaf.parent.right, outputs[j][:]) + } + } + } + } else { + var inputs [4][]byte + for i := start; i < end; i += 4 { + batch := 4 + if i+batch > end { + batch = end - i + } + for j := 0; j < batch; j++ { + offset := (i + j) * secsize + if prefixLen > 0 { + copy(h.bmt.leafConcat[j][prefixLen:], buf[offset:offset+secsize]) + inputs[j] = h.bmt.leafConcat[j][:prefixLen+secsize] + } else { + inputs[j] = buf[offset : offset+secsize] + } + } + for j := batch; j < 4; j++ { + inputs[j] = nil + } + var outputs [4]keccak.Hash256 + if h.useSIMD { + outputs = keccak.Sum256x4(inputs) + } else { + outputs = keccak.Sum256x4Scalar(inputs) + } + for j := 0; j < batch; j++ { + leaf := h.bmt.levels[0][i+j] + if leaf.isLeft { + copy(leaf.parent.left, outputs[j][:]) + } else { + copy(leaf.parent.right, outputs[j][:]) + } + } + } + } +} + +// hashNodesBatch hashes a level of internal nodes using SIMD batches. +// Each node's left||right (64 bytes) is hashed to produce the input for its parent. +func (h *Hasher) hashNodesBatch(nodes []*node, bw, prefixLen int) { + count := len(nodes) + segSize := h.segmentSize + concat := &h.bmt.concat + + if bw == 8 { + var inputs [8][]byte + for i := 0; i < count; i += 8 { + batch := 8 + if i+batch > count { + batch = count - i + } + for j := 0; j < batch; j++ { + n := nodes[i+j] + copy(concat[j][prefixLen:prefixLen+segSize], n.left) + copy(concat[j][prefixLen+segSize:], n.right) + inputs[j] = concat[j][:prefixLen+2*segSize] + } + for j := batch; j < 8; j++ { + inputs[j] = nil + } + var outputs [8]keccak.Hash256 + if h.useSIMD { + outputs = keccak.Sum256x8(inputs) + } else { + outputs = keccak.Sum256x8Scalar(inputs) + } + for j := 0; j < batch; j++ { + n := nodes[i+j] + if n.isLeft { + copy(n.parent.left, outputs[j][:]) + } else { + copy(n.parent.right, outputs[j][:]) + } + } + } + } else { + var inputs [4][]byte + for i := 0; i < count; i += 4 { + batch := 4 + if i+batch > count { + batch = count - i + } + for j := 0; j < batch; j++ { + n := nodes[i+j] + copy(concat[j][prefixLen:prefixLen+segSize], n.left) + copy(concat[j][prefixLen+segSize:], n.right) + inputs[j] = concat[j][:prefixLen+2*segSize] + } + for j := batch; j < 4; j++ { + inputs[j] = nil + } + var outputs [4]keccak.Hash256 + if h.useSIMD { + outputs = keccak.Sum256x4(inputs) + } else { + outputs = keccak.Sum256x4Scalar(inputs) + } + for j := 0; j < batch; j++ { + n := nodes[i+j] + if n.isLeft { + copy(n.parent.left, outputs[j][:]) + } else { + copy(n.parent.right, outputs[j][:]) + } + } + } + } +} diff --git a/pkg/bmt/bmt_test.go b/pkg/bmt/bmt_test.go index c82fb896a6f..e22538eb3f2 100644 --- a/pkg/bmt/bmt_test.go +++ b/pkg/bmt/bmt_test.go @@ -67,7 +67,7 @@ func TestHasherEmptyData(t *testing.T) { if err != nil { t.Fatal(err) } - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, count, 1)) + pool := bmt.NewPool(bmt.NewConf(count, 1)) h := pool.Get() resHash, err := syncHash(h, nil) if err != nil { @@ -92,7 +92,7 @@ func TestSyncHasherCorrectness(t *testing.T) { maxValue := count * hashSize var incr int capacity := 1 - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, count, capacity)) + pool := bmt.NewPool(bmt.NewConf(count, capacity)) for n := 0; n <= maxValue; n += incr { h := pool.Get() incr = 1 + rand.Intn(5) @@ -125,7 +125,7 @@ func TestHasherReuse(t *testing.T) { func testHasherReuse(t *testing.T, poolsize int) { t.Helper() - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, poolsize)) + pool := bmt.NewPool(bmt.NewConf(testSegmentCount, poolsize)) h := pool.Get() defer pool.Put(h) @@ -145,7 +145,7 @@ func TestBMTConcurrentUse(t *testing.T) { t.Parallel() testData := testutil.RandBytesWithSeed(t, 4096, seed) - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, testPoolSize)) + pool := bmt.NewPool(bmt.NewConf(testSegmentCount, testPoolSize)) cycles := 100 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -178,7 +178,7 @@ func TestBMTWriterBuffers(t *testing.T) { t.Run(fmt.Sprintf("%d_segments", count), func(t *testing.T) { t.Parallel() - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, count, testPoolSize)) + pool := bmt.NewPool(bmt.NewConf(count, testPoolSize)) h := pool.Get() defer pool.Put(h) @@ -275,7 +275,7 @@ func testHasherCorrectness(h *bmt.Hasher, data []byte, n, count int) (err error) func TestUseSyncAsOrdinaryHasher(t *testing.T) { t.Parallel() - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, testSegmentCount, testPoolSize)) + pool := bmt.NewPool(bmt.NewConf(testSegmentCount, testPoolSize)) h := pool.Get() defer pool.Put(h) data := []byte("moodbytesmoodbytesmoodbytesmoodbytes") diff --git a/pkg/bmt/export_goroutine_test.go b/pkg/bmt/export_goroutine_test.go new file mode 100644 index 00000000000..26f48b88739 --- /dev/null +++ b/pkg/bmt/export_goroutine_test.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux || !amd64 || purego + +package bmt + +// NewConfNoSIMD on the goroutine path just returns a regular Conf +// since there is no SIMD to disable. +func NewConfNoSIMD(segmentCount, capacity int) *Conf { + return NewConf(segmentCount, capacity) +} diff --git a/pkg/bmt/export_simd_test.go b/pkg/bmt/export_simd_test.go new file mode 100644 index 00000000000..f03e52d788f --- /dev/null +++ b/pkg/bmt/export_simd_test.go @@ -0,0 +1,16 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !purego + +package bmt + +// NewConfNoSIMD creates a Conf identical to NewConf but with SIMD disabled, +// useful for benchmarking the non-SIMD path. +func NewConfNoSIMD(segmentCount, capacity int) *Conf { + c := NewConf(segmentCount, capacity) + c.useSIMD = false + c.batchWidth = 8 // use 8-wide batching with scalar fallback + return c +} diff --git a/pkg/bmt/hasher_goroutine.go b/pkg/bmt/hasher_goroutine.go new file mode 100644 index 00000000000..d18728dfee7 --- /dev/null +++ b/pkg/bmt/hasher_goroutine.go @@ -0,0 +1,234 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux || !amd64 || purego + +package bmt + +import ( + "github.com/ethersphere/bee/v2/pkg/swarm" +) + +// Hasher is a reusable hasher for fixed maximum size chunks representing a BMT. +// This implementation uses goroutine-based concurrent tree traversal. +// +// It reuses a pool of trees for amortised memory allocation and resource control, +// and supports order-agnostic concurrent segment writes and section (double segment) writes +// as well as sequential read and write. +// +// The same hasher instance must not be called concurrently on more than one chunk. +// +// The same hasher instance is synchronously reusable. +// +// Sum gives back the tree to the pool and guaranteed to leave +// the tree and itself in a state reusable for hashing a new chunk. +type Hasher struct { + *Conf // configuration + bmt *tree // prebuilt BMT resource for flowcontrol and proofs + size int // bytes written to Hasher since last Reset() + pos int // index of rightmost currently open segment + result chan []byte // result channel + errc chan error // error channel + span []byte // The span of the data subsumed under the chunk +} + +// NewHasher gives back an instance of a Hasher struct +func NewHasher() *Hasher { + return newHasherWithConf(NewConf(swarm.BmtBranches, 32)) +} + +// NewPrefixHasher gives back an instance of a Hasher struct with the given prefix +// prepended to every hash operation. +func NewPrefixHasher(prefix []byte) *Hasher { + return newHasherWithConf(NewConfWithPrefix(prefix, swarm.BmtBranches, 32)) +} + +func newHasherWithConf(conf *Conf) *Hasher { + return &Hasher{ + Conf: conf, + result: make(chan []byte), + errc: make(chan error, 1), + span: make([]byte, SpanSize), + bmt: newTree(conf.maxSize, conf.depth, conf.hasherFunc), + } +} + +// Write calls sequentially add to the buffer to be hashed, +// with every full segment calls processSection in a go routine. +func (h *Hasher) Write(b []byte) (int, error) { + l := len(b) + maxVal := h.maxSize - h.size + if l > maxVal { + l = maxVal + } + copy(h.bmt.buffer[h.size:], b) + secsize := 2 * h.segmentSize + from := h.size / secsize + h.size += l + to := h.size / secsize + if l == maxVal { + to-- + } + h.pos = to + for i := from; i < to; i++ { + go h.processSection(i, false) + } + return l, nil +} + +// Hash returns the BMT root hash of the buffer and an error +// using Hash presupposes sequential synchronous writes (io.Writer interface). +func (h *Hasher) Hash(b []byte) ([]byte, error) { + if h.size == 0 { + return doHash(h.baseHasher(), h.span, h.zerohashes[h.depth]) + } + copy(h.bmt.buffer[h.size:], zerosection) + // write the last section with final flag set to true + go h.processSection(h.pos, true) + select { + case result := <-h.result: + return doHash(h.baseHasher(), h.span, result) + case err := <-h.errc: + return nil, err + } +} + +// Reset prepares the Hasher for reuse +func (h *Hasher) Reset() { + h.pos = 0 + h.size = 0 + copy(h.span, zerospan) +} + +// processSection writes the hash of i-th section into level 1 node of the BMT tree. +func (h *Hasher) processSection(i int, final bool) { + secsize := 2 * h.segmentSize + offset := i * secsize + level := 1 + // select the leaf node for the section + n := h.bmt.leaves[i] + isLeft := n.isLeft + hasher := n.hasher + n = n.parent + // hash the section + section, err := doHash(hasher, h.bmt.buffer[offset:offset+secsize]) + if err != nil { + select { + case h.errc <- err: + default: + } + return + } + // write hash into parent node + if final { + // for the last segment use writeFinalNode + h.writeFinalNode(level, n, isLeft, section) + } else { + h.writeNode(n, isLeft, section) + } +} + +// writeNode pushes the data to the node. +// if it is the first of 2 sisters written, the routine terminates. +// if it is the second, it calculates the hash and writes it +// to the parent node recursively. +// since hashing the parent is synchronous the same hasher can be used. +func (h *Hasher) writeNode(n *node, isLeft bool, s []byte) { + var err error + for { + // at the root of the bmt just write the result to the result channel + if n == nil { + h.result <- s + return + } + // otherwise assign child hash to left or right segment + if isLeft { + n.left = s + } else { + n.right = s + } + // the child-thread first arriving will terminate + if n.toggle() { + return + } + // the thread coming second now can be sure both left and right children are written + // so it calculates the hash of left|right and pushes it to the parent + s, err = doHash(n.hasher, n.left, n.right) + if err != nil { + select { + case h.errc <- err: + default: + } + return + } + isLeft = n.isLeft + n = n.parent + } +} + +// writeFinalNode is following the path starting from the final datasegment to the +// BMT root via parents. +// For unbalanced trees it fills in the missing right sister nodes using +// the pool's lookup table for BMT subtree root hashes for all-zero sections. +// Otherwise behaves like `writeNode`. +func (h *Hasher) writeFinalNode(level int, n *node, isLeft bool, s []byte) { + var err error + for { + // at the root of the bmt just write the result to the result channel + if n == nil { + if s != nil { + h.result <- s + } + return + } + var noHash bool + if isLeft { + // coming from left sister branch + // when the final section's path is going via left child node + // we include an all-zero subtree hash for the right level and toggle the node. + n.right = h.zerohashes[level] + if s != nil { + n.left = s + // if a left final node carries a hash, it must be the first (and only thread) + // so the toggle is already in passive state no need no call + // yet thread needs to carry on pushing hash to parent + noHash = false + } else { + // if again first thread then propagate nil and calculate no hash + noHash = n.toggle() + } + } else { + // right sister branch + if s != nil { + // if hash was pushed from right child node, write right segment change state + n.right = s + // if toggle is true, we arrived first so no hashing just push nil to parent + noHash = n.toggle() + } else { + // if s is nil, then thread arrived first at previous node and here there will be two, + // so no need to do anything and keep s = nil for parent + noHash = true + } + } + // the child-thread first arriving will just continue resetting s to nil + // the second thread now can be sure both left and right children are written + // it calculates the hash of left|right and pushes it to the parent + if noHash { + s = nil + } else { + s, err = doHash(n.hasher, n.left, n.right) + if err != nil { + select { + case h.errc <- err: + default: + } + return + } + } + // iterate to parent + isLeft = n.isLeft + n = n.parent + level++ + } +} diff --git a/pkg/bmt/hasher_simd.go b/pkg/bmt/hasher_simd.go new file mode 100644 index 00000000000..ef3396482d7 --- /dev/null +++ b/pkg/bmt/hasher_simd.go @@ -0,0 +1,90 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !purego + +package bmt + +import ( + "github.com/ethersphere/bee/v2/pkg/swarm" +) + +// Hasher is a reusable hasher for fixed maximum size chunks representing a BMT. +// This implementation buffers all data and defers hashing to Hash(), +// using SIMD-accelerated Keccak for multi-level trees. +// +// The same hasher instance must not be called concurrently on more than one chunk. +// +// The same hasher instance is synchronously reusable. +type Hasher struct { + *Conf // configuration + bmt *tree // prebuilt BMT resource for flowcontrol and proofs + size int // bytes written to Hasher since last Reset() + span []byte // The span of the data subsumed under the chunk +} + +// NewHasher gives back an instance of a Hasher struct +func NewHasher() *Hasher { + return newHasherWithConf(NewConf(swarm.BmtBranches, 32)) +} + +// NewPrefixHasher gives back an instance of a Hasher struct with the given prefix +// prepended to every hash operation. +func NewPrefixHasher(prefix []byte) *Hasher { + return newHasherWithConf(NewConfWithPrefix(prefix, swarm.BmtBranches, 32)) +} + +func newHasherWithConf(conf *Conf) *Hasher { + return &Hasher{ + Conf: conf, + span: make([]byte, SpanSize), + bmt: newTree(conf.maxSize, conf.depth, conf.baseHasher, conf.prefix), + } +} + +// Write calls sequentially add to the buffer to be hashed. +// All hashing is deferred to Hash(). +func (h *Hasher) Write(b []byte) (int, error) { + l := len(b) + maxVal := h.maxSize - h.size + if l > maxVal { + l = maxVal + } + copy(h.bmt.buffer[h.size:], b) + h.size += l + return l, nil +} + +// Hash returns the BMT root hash of the buffer and an error +// using Hash presupposes sequential synchronous writes (io.Writer interface). +func (h *Hasher) Hash(b []byte) ([]byte, error) { + if h.size == 0 { + return doHash(h.baseHasher(), h.span, h.zerohashes[h.depth]) + } + // zero-fill remainder so all sections have deterministic input + for i := h.size; i < h.maxSize; i++ { + h.bmt.buffer[i] = 0 + } + if len(h.bmt.levels) == 1 { + // single-level tree: hash the only section directly + secsize := 2 * h.segmentSize + root := h.bmt.levels[0][0] + rootHash, err := doHash(root.hasher, h.bmt.buffer[:secsize]) + if err != nil { + return nil, err + } + return doHash(h.baseHasher(), h.span, rootHash) + } + rootHash, err := h.hashSIMD() + if err != nil { + return nil, err + } + return doHash(h.baseHasher(), h.span, rootHash) +} + +// Reset prepares the Hasher for reuse +func (h *Hasher) Reset() { + h.size = 0 + copy(h.span, zerospan) +} diff --git a/pkg/bmt/pool.go b/pkg/bmt/pool_goroutine.go similarity index 71% rename from pkg/bmt/pool.go rename to pkg/bmt/pool_goroutine.go index a7f7245c40d..9a325ee57a9 100644 --- a/pkg/bmt/pool.go +++ b/pkg/bmt/pool_goroutine.go @@ -2,26 +2,37 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !linux || !amd64 || purego + package bmt import ( "hash" "sync/atomic" + + "github.com/ethersphere/bee/v2/pkg/swarm" ) -// BaseHasherFunc is a hash.Hash constructor function used for the base hash of the BMT. -// implemented by Keccak256 SHA3 sha3.NewLegacyKeccak256 -type BaseHasherFunc func() hash.Hash +const SEGMENT_SIZE = 32 // configuration type Conf struct { - segmentSize int // size of leaf segments, stipulated to be = hash size - segmentCount int // the number of segments on the base level of the BMT - capacity int // pool capacity, controls concurrency - depth int // depth of the bmt trees = int(log2(segmentCount))+1 - maxSize int // the total length of the data (count * size) - zerohashes [][]byte // lookup table for predictable padding subtrees for all levels - hasher BaseHasherFunc // base hasher to use for the BMT levels + segmentSize int // size of leaf segments, stipulated to be = hash size + segmentCount int // the number of segments on the base level of the BMT + capacity int // pool capacity, controls concurrency + depth int // depth of the bmt trees = int(log2(segmentCount))+1 + maxSize int // the total length of the data (count * size) + zerohashes [][]byte // lookup table for predictable padding subtrees for all levels + prefix []byte // optional prefix prepended to every hash operation + hasherFunc func() hash.Hash +} + +// baseHasher returns a new base hasher instance, optionally with prefix. +func (c *Conf) baseHasher() hash.Hash { + if len(c.prefix) > 0 { + return swarm.NewPrefixHasher(c.prefix) + } + return swarm.NewHasher() } // Pool provides a pool of trees used as resources by the BMT Hasher. @@ -32,29 +43,49 @@ type Pool struct { *Conf // configuration } -func NewConf(hasher BaseHasherFunc, segmentCount, capacity int) *Conf { +func NewConf(segmentCount, capacity int) *Conf { + return newConf(nil, segmentCount, capacity) +} + +func NewConfWithPrefix(prefix []byte, segmentCount, capacity int) *Conf { + return newConf(prefix, segmentCount, capacity) +} + +func newConf(prefix []byte, segmentCount, capacity int) *Conf { count, depth := sizeToParams(segmentCount) - segmentSize := hasher().Size() + segmentSize := SEGMENT_SIZE + + hasherFunc := func() hash.Hash { + if len(prefix) > 0 { + return swarm.NewPrefixHasher(prefix) + } + return swarm.NewHasher() + } + + c := &Conf{ + segmentSize: segmentSize, + segmentCount: segmentCount, + capacity: capacity, + maxSize: count * segmentSize, + depth: depth, + prefix: prefix, + hasherFunc: hasherFunc, + } + zerohashes := make([][]byte, depth+1) zeros := make([]byte, segmentSize) zerohashes[0] = zeros var err error // initialises the zerohashes lookup table for i := 1; i < depth+1; i++ { - if zeros, err = doHash(hasher(), zeros, zeros); err != nil { + if zeros, err = doHash(c.baseHasher(), zeros, zeros); err != nil { panic(err.Error()) } zerohashes[i] = zeros } - return &Conf{ - hasher: hasher, - segmentSize: segmentSize, - segmentCount: segmentCount, - capacity: capacity, - maxSize: count * segmentSize, - depth: depth, - zerohashes: zerohashes, - } + c.zerohashes = zerohashes + + return c } // NewPool creates a tree pool with hasher, segment size, segment count and capacity @@ -65,7 +96,7 @@ func NewPool(c *Conf) *Pool { c: make(chan *tree, c.capacity), } for i := 0; i < c.capacity; i++ { - p.c <- newTree(p.maxSize, p.depth, p.hasher) + p.c <- newTree(p.maxSize, p.depth, c.hasherFunc) } return p } diff --git a/pkg/bmt/pool_simd.go b/pkg/bmt/pool_simd.go new file mode 100644 index 00000000000..78c7061c6ac --- /dev/null +++ b/pkg/bmt/pool_simd.go @@ -0,0 +1,209 @@ +// Copyright 2021 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !purego + +package bmt + +import ( + "hash" + + "github.com/ethersphere/bee/v2/pkg/keccak" + "github.com/ethersphere/bee/v2/pkg/swarm" +) + +const SEGMENT_SIZE = 32 + +// configuration +type Conf struct { + segmentSize int // size of leaf segments, stipulated to be = hash size + segmentCount int // the number of segments on the base level of the BMT + capacity int // pool capacity, controls concurrency + depth int // depth of the bmt trees = int(log2(segmentCount))+1 + maxSize int // the total length of the data (count * size) + zerohashes [][]byte // lookup table for predictable padding subtrees for all levels + prefix []byte // optional prefix prepended to every hash operation + useSIMD bool // whether SIMD keccak is available + batchWidth int // 4 (AVX2), 8 (AVX-512), or 0 +} + +// baseHasher returns a new base hasher instance, optionally with prefix. +func (c *Conf) baseHasher() hash.Hash { + if len(c.prefix) > 0 { + return swarm.NewPrefixHasher(c.prefix) + } + return swarm.NewHasher() +} + +// Pool provides a pool of trees used as resources by the BMT Hasher. +// A tree popped from the pool is guaranteed to have a clean state ready +// for hashing a new chunk. +type Pool struct { + c chan *tree // the channel to obtain a resource from the pool + *Conf // configuration +} + +func NewConf(segmentCount, capacity int) *Conf { + return newConf(nil, segmentCount, capacity) +} + +func NewConfWithPrefix(prefix []byte, segmentCount, capacity int) *Conf { + return newConf(prefix, segmentCount, capacity) +} + +func newConf(prefix []byte, segmentCount, capacity int) *Conf { + count, depth := sizeToParams(segmentCount) + segmentSize := SEGMENT_SIZE + + c := &Conf{ + segmentSize: segmentSize, + segmentCount: segmentCount, + capacity: capacity, + maxSize: count * segmentSize, + depth: depth, + prefix: prefix, + useSIMD: keccak.HasSIMD(), + } + + bw := keccak.BatchWidth() + if bw == 0 { + bw = 8 // use 4-wide batching with scalar fallback + } + c.batchWidth = bw + + zerohashes := make([][]byte, depth+1) + zeros := make([]byte, segmentSize) + zerohashes[0] = zeros + var err error + // initialises the zerohashes lookup table + for i := 1; i < depth+1; i++ { + if zeros, err = doHash(c.baseHasher(), zeros, zeros); err != nil { + panic(err.Error()) + } + zerohashes[i] = zeros + } + c.zerohashes = zerohashes + + return c +} + +// NewPool creates a tree pool with hasher, segment size, segment count and capacity +// it reuses free trees or creates a new one if capacity is not reached. +func NewPool(c *Conf) *Pool { + p := &Pool{ + Conf: c, + c: make(chan *tree, c.capacity), + } + for i := 0; i < c.capacity; i++ { + p.c <- newTree(p.maxSize, p.depth, c.baseHasher, c.prefix) + } + return p +} + +// Get returns a BMT hasher possibly reusing a tree from the pool +func (p *Pool) Get() *Hasher { + t := <-p.c + return &Hasher{ + Conf: p.Conf, + span: make([]byte, SpanSize), + bmt: t, + } +} + +// Put is called after using a bmt hasher to return the tree to a pool for reuse +func (p *Pool) Put(h *Hasher) { + p.c <- h.bmt +} + +// tree is a reusable control structure representing a BMT +// organised in a binary tree +// +// Hasher uses a Pool to obtain a tree for each chunk hash +// the tree is 'locked' while not in the pool. +type tree struct { + leaves []*node // leaf nodes of the tree, other nodes accessible via parent links + levels [][]*node // levels[0]=leaves, levels[1]=parents of leaves, ..., levels[depth-1]=root + buffer []byte + concat [8][]byte // reusable concat buffers for SIMD node hashing + leafConcat [8][]byte // reusable concat buffers for SIMD leaf hashing +} + +// node is a reusable segment hasher representing a node in a BMT. +type node struct { + isLeft bool // whether it is left side of the parent double segment + parent *node // pointer to parent node in the BMT + left, right []byte // this is where the two children sections are written + hasher hash.Hash // preconstructed hasher on nodes +} + +// newNode constructs a segment hasher node in the BMT (used by newTree). +func newNode(index int, parent *node, hasher hash.Hash) *node { + return &node{ + parent: parent, + isLeft: index%2 == 0, + hasher: hasher, + left: make([]byte, hasher.Size()), + right: make([]byte, hasher.Size()), + } +} + +// newTree initialises a tree by building up the nodes of a BMT +func newTree(maxsize, depth int, hashfunc func() hash.Hash, prefix []byte) *tree { + prefixLen := len(prefix) + n := newNode(0, nil, hashfunc()) + prevlevel := []*node{n} + // collect levels top-down during construction, then reverse + allLevels := [][]*node{prevlevel} + // iterate over levels and creates 2^(depth-level) nodes + // the 0 level is on double segment sections so we start at depth - 2 + count := 2 + for level := depth - 2; level >= 0; level-- { + nodes := make([]*node, count) + for i := 0; i < count; i++ { + parent := prevlevel[i/2] + nodes[i] = newNode(i, parent, hashfunc()) + } + allLevels = append(allLevels, nodes) + prevlevel = nodes + count *= 2 + } + // reverse so levels[0]=leaves, levels[len-1]=root + for i, j := 0, len(allLevels)-1; i < j; i, j = i+1, j-1 { + allLevels[i], allLevels[j] = allLevels[j], allLevels[i] + } + // pre-allocate concat buffers for SIMD hashing (with space for optional prefix) + segSize := hashfunc().Size() + bufSize := prefixLen + 2*segSize + var concat [8][]byte + for i := range concat { + concat[i] = make([]byte, bufSize) + if prefixLen > 0 { + copy(concat[i][:prefixLen], prefix) + } + } + var leafConcat [8][]byte + for i := range leafConcat { + leafConcat[i] = make([]byte, prefixLen+2*segSize) + if prefixLen > 0 { + copy(leafConcat[i][:prefixLen], prefix) + } + } + // the datanode level is the nodes on the last level + return &tree{ + leaves: prevlevel, + levels: allLevels, + buffer: make([]byte, maxsize), + concat: concat, + leafConcat: leafConcat, + } +} + +// sizeToParams calculates the depth (number of levels) and segment count in the BMT tree. +func sizeToParams(n int) (c, d int) { + c = 2 + for ; c < n; c *= 2 { + d++ + } + return c, d + 1 +} diff --git a/pkg/bmt/proof.go b/pkg/bmt/proof.go index b08017b43cd..4b1eda7351f 100644 --- a/pkg/bmt/proof.go +++ b/pkg/bmt/proof.go @@ -17,7 +17,8 @@ type Proof struct { Index int } -// Hash overrides base hash function of Hasher to fill buffer with zeros until chunk length +// Hash overrides base hash function of Hasher to fill buffer with zeros until chunk length. +// It always pads with zero sections so that the proof tree is fully populated. func (p Prover) Hash(b []byte) ([]byte, error) { for i := p.size; i < p.maxSize; i += len(zerosection) { _, err := p.Write(zerosection) @@ -67,7 +68,7 @@ func (p Prover) Verify(i int, proof Proof) (root []byte, err error) { } i = i / 2 n := p.bmt.leaves[i] - hasher := p.hasher() + hasher := p.baseHasher() isLeft := n.isLeft root, err = doHash(hasher, section) if err != nil { diff --git a/pkg/bmt/proof_test.go b/pkg/bmt/proof_test.go index 5b3625acefb..edf91c192a7 100644 --- a/pkg/bmt/proof_test.go +++ b/pkg/bmt/proof_test.go @@ -46,7 +46,7 @@ func TestProofCorrectness(t *testing.T) { } } - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, 128, 128)) + pool := bmt.NewPool(bmt.NewConf(128, 128)) hh := pool.Get() t.Cleanup(func() { pool.Put(hh) @@ -191,7 +191,7 @@ func TestProof(t *testing.T) { t.Fatal(err) } - pool := bmt.NewPool(bmt.NewConf(swarm.NewHasher, 128, 128)) + pool := bmt.NewPool(bmt.NewConf(128, 128)) hh := pool.Get() t.Cleanup(func() { pool.Put(hh) diff --git a/pkg/bmtpool/bmtpool.go b/pkg/bmtpool/bmtpool.go index 88c1ad32dba..06bdcaa04b4 100644 --- a/pkg/bmtpool/bmtpool.go +++ b/pkg/bmtpool/bmtpool.go @@ -17,7 +17,7 @@ var instance *bmt.Pool // nolint:gochecknoinits func init() { - instance = bmt.NewPool(bmt.NewConf(swarm.NewHasher, swarm.BmtBranches, Capacity)) + instance = bmt.NewPool(bmt.NewConf(swarm.BmtBranches, Capacity)) } // Get a bmt Hasher instance. diff --git a/pkg/keccak/keccak.go b/pkg/keccak/keccak.go new file mode 100644 index 00000000000..24b3ba58727 --- /dev/null +++ b/pkg/keccak/keccak.go @@ -0,0 +1,149 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package keccak provides legacy Keccak-256 (Ethereum-compatible) hashing +// with SIMD acceleration via XKCP. +// +// On amd64, the package automatically selects between AVX-512 (8-way parallel) +// and AVX2 (4-way parallel) based on the CPU's capabilities. +package keccak + +import ( + "encoding/hex" + "sync" + + "golang.org/x/crypto/sha3" +) + +// Hash256 represents a 32-byte Keccak-256 hash +type Hash256 [32]byte + +// HexString returns the hash as a hexadecimal string +func (h Hash256) HexString() string { + return hex.EncodeToString(h[:]) +} + +// HasAVX512 reports whether the CPU supports AVX-512 (F + VL) and the +// AVX-512 code path is available. +func HasAVX512() bool { + return hasAVX512 +} + +// HasSIMD reports whether any SIMD-accelerated Keccak path is available +// (AVX2 or AVX-512). +func HasSIMD() bool { + return hasAVX2 +} + +// BatchWidth returns the SIMD batch width: 8 for AVX-512, 4 for AVX2, or 0 +// if no SIMD acceleration is available. +func BatchWidth() int { + if hasAVX512 { + return 8 + } + if hasAVX2 { + return 4 + } + return 0 +} + +// Sum256 computes a single Keccak-256 hash (legacy, Ethereum-compatible). +// Uses the best available implementation. +func Sum256(data []byte) Hash256 { + return sum256Scalar(data) +} + +// Sum256x4 computes 4 Keccak-256 hashes in parallel using AVX2. +// Falls back to scalar if AVX2 is not available. +func Sum256x4(inputs [4][]byte) [4]Hash256 { + if !hasAVX2 { + return sum256x4Scalar(inputs) + } + var outputs [4]Hash256 + var inputsCopy [4][]byte + copy(inputsCopy[:], inputs[:]) + keccak256x4(&inputsCopy, &outputs) + return outputs +} + +// Sum256x4Scalar computes 4 Keccak-256 hashes using the scalar path, +// bypassing SIMD detection. Used by callers that explicitly want non-SIMD. +func Sum256x4Scalar(inputs [4][]byte) [4]Hash256 { + return sum256x4Scalar(inputs) +} + +// Sum256x8 computes 8 Keccak-256 hashes in parallel using AVX-512. +// Falls back to scalar if AVX-512 is not available. +func Sum256x8(inputs [8][]byte) [8]Hash256 { + if !hasAVX512 { + return sum256x8Scalar(inputs) + } + var outputs [8]Hash256 + var inputsCopy [8][]byte + copy(inputsCopy[:], inputs[:]) + keccak256x8(&inputsCopy, &outputs) + return outputs +} + +// Sum256x8Scalar computes 8 Keccak-256 hashes using the scalar path, +// bypassing SIMD detection. Used by callers that explicitly want non-SIMD. +func Sum256x8Scalar(inputs [8][]byte) [8]Hash256 { + return sum256x8Scalar(inputs) +} + +func sum256Scalar(data []byte) Hash256 { + var out Hash256 + h := sha3.NewLegacyKeccak256() + h.Write(data) + copy(out[:], h.Sum(nil)) + return out +} + +func sum256x4Scalar(inputs [4][]byte) [4]Hash256 { + var outputs [4]Hash256 + var wg sync.WaitGroup + var mu sync.Mutex + for i := 0; i < 4; i++ { + if inputs[i] == nil { + continue + } + wg.Add(1) + + go func() { + defer wg.Done() + h := sha3.NewLegacyKeccak256() + h.Write(inputs[i]) + result := h.Sum(nil) + mu.Lock() + copy(outputs[i][:], result) + mu.Unlock() + }() + } + wg.Wait() + return outputs +} + +func sum256x8Scalar(inputs [8][]byte) [8]Hash256 { + var outputs [8]Hash256 + var wg sync.WaitGroup + var mu sync.Mutex + + for i := 0; i < 8; i++ { + if inputs[i] == nil { + continue + } + wg.Add(1) + go func() { + defer wg.Done() + h := sha3.NewLegacyKeccak256() + h.Write(inputs[i]) + result := h.Sum(nil) + mu.Lock() + copy(outputs[i][:], result) + mu.Unlock() + }() + } + wg.Wait() + return outputs +} diff --git a/pkg/keccak/keccak_amd64.go b/pkg/keccak/keccak_amd64.go new file mode 100644 index 00000000000..2894b54080c --- /dev/null +++ b/pkg/keccak/keccak_amd64.go @@ -0,0 +1,9 @@ +//go:build linux && amd64 && !purego + +package keccak + +//go:noescape +func keccak256x4(inputs *[4][]byte, outputs *[4]Hash256) + +//go:noescape +func keccak256x8(inputs *[8][]byte, outputs *[8]Hash256) diff --git a/pkg/keccak/keccak_cpu.go b/pkg/keccak/keccak_cpu.go new file mode 100644 index 00000000000..7ee62db10e8 --- /dev/null +++ b/pkg/keccak/keccak_cpu.go @@ -0,0 +1,14 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && amd64 && !purego + +package keccak + +import cpuid "github.com/klauspost/cpuid/v2" + +var ( + hasAVX2 = cpuid.CPU.Supports(cpuid.AVX2) + hasAVX512 = cpuid.CPU.Supports(cpuid.AVX512F, cpuid.AVX512VL) +) diff --git a/pkg/keccak/keccak_cpu_other.go b/pkg/keccak/keccak_cpu_other.go new file mode 100644 index 00000000000..0e0966aad73 --- /dev/null +++ b/pkg/keccak/keccak_cpu_other.go @@ -0,0 +1,14 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !linux || !amd64 || purego + +package keccak + +// No SIMD Keccak implementations are available on this platform; +// Sum256x4/Sum256x8 will fall back to the scalar goroutine path. +var ( + hasAVX2 = false + hasAVX512 = false +) diff --git a/pkg/keccak/keccak_other.go b/pkg/keccak/keccak_other.go new file mode 100644 index 00000000000..37e5b29caac --- /dev/null +++ b/pkg/keccak/keccak_other.go @@ -0,0 +1,11 @@ +//go:build !linux || !amd64 || purego + +package keccak + +func keccak256x4(_ *[4][]byte, _ *[4]Hash256) { + panic("keccak: SIMD not available on this platform") +} + +func keccak256x8(_ *[8][]byte, _ *[8]Hash256) { + panic("keccak: SIMD not available on this platform") +} diff --git a/pkg/keccak/keccak_times4_amd64.s b/pkg/keccak/keccak_times4_amd64.s new file mode 100644 index 00000000000..6d6f385f45f --- /dev/null +++ b/pkg/keccak/keccak_times4_amd64.s @@ -0,0 +1,11 @@ +//go:build linux && amd64 && !purego + +#include "textflag.h" + +// func keccak256x4(inputs *[4][]byte, outputs *[4]Hash256) +TEXT ·keccak256x4(SB), $16384-16 + MOVQ inputs+0(FP), DI + MOVQ outputs+8(FP), SI + CALL go_keccak256x4(SB) + VZEROUPPER + RET diff --git a/pkg/keccak/keccak_times4_linux_amd64.syso b/pkg/keccak/keccak_times4_linux_amd64.syso new file mode 100644 index 0000000000000000000000000000000000000000..06c7bc1db5f27b2febcb4188112d418114938141 GIT binary patch literal 183192 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 literal 0 HcmV?d00001 diff --git a/pkg/keccak/keccak_times8_amd64.s b/pkg/keccak/keccak_times8_amd64.s new file mode 100644 index 00000000000..8108924205a --- /dev/null +++ b/pkg/keccak/keccak_times8_amd64.s @@ -0,0 +1,13 @@ +//go:build linux && amd64 && !purego + +#include "textflag.h" + +// func keccak256x8(inputs *[8][]byte, outputs *[8]Hash256) +// Frame size 16384: AVX-512 state is larger (25 x 64 bytes = 1600 bytes) and +// the permutation uses more stack. Generous headroom provided. +TEXT ·keccak256x8(SB), $16384-16 + MOVQ inputs+0(FP), DI + MOVQ outputs+8(FP), SI + CALL go_keccak256x8(SB) + VZEROUPPER + RET diff --git a/pkg/keccak/keccak_times8_linux_amd64.syso b/pkg/keccak/keccak_times8_linux_amd64.syso new file mode 100644 index 0000000000000000000000000000000000000000..5d9f9fa77c264723f0c7343c8defcaac845b3a08 GIT binary patch literal 137176 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 literal 0 HcmV?d00001 diff --git a/pkg/storageincentives/proof.go b/pkg/storageincentives/proof.go index 86dd115c67e..b26f5dc54a5 100644 --- a/pkg/storageincentives/proof.go +++ b/pkg/storageincentives/proof.go @@ -7,7 +7,6 @@ package storageincentives import ( "errors" "fmt" - "hash" "math/big" "github.com/ethersphere/bee/v2/pkg/bmt" @@ -55,10 +54,7 @@ func makeInclusionProofs( require2++ } - prefixHasherFactory := func() hash.Hash { - return swarm.NewPrefixHasher(anchor1) - } - prefixHasherPool := bmt.NewPool(bmt.NewConf(prefixHasherFactory, swarm.BmtBranches, 8)) + prefixHasherPool := bmt.NewPool(bmt.NewConfWithPrefix(anchor1, swarm.BmtBranches, 8)) // Sample chunk proofs rccontent := bmt.Prover{Hasher: bmtpool.Get()} diff --git a/pkg/storageincentives/soc_mine_test.go b/pkg/storageincentives/soc_mine_test.go index 0265a9a21f7..b3d71077165 100644 --- a/pkg/storageincentives/soc_mine_test.go +++ b/pkg/storageincentives/soc_mine_test.go @@ -9,12 +9,10 @@ import ( "encoding/binary" "encoding/hex" "fmt" - "hash" "math/big" "os" "sync" "testing" - "testing/synctest" "github.com/ethersphere/bee/v2/pkg/bmt" "github.com/ethersphere/bee/v2/pkg/cac" @@ -33,59 +31,55 @@ import ( // to generate uploads using the input // cat socs.txt | tail 19 | head 16 | perl -pne 's/([a-f0-9]+)\t([a-f0-9]+)\t([a-f0-9]+)\t([a-f0-9]+)/echo -n $4 | xxd -r -p | curl -X POST \"http:\/\/localhost:1633\/soc\/$1\/$2?sig=$3\" -H \"accept: application\/json, text\/plain, \/\" -H \"content-type: application\/octet-stream\" -H \"swarm-postage-batch-id: 14b26beca257e763609143c6b04c2c487f01a051798c535c2f542ce75a97c05f\" --data-binary \@-/' func TestSocMine(t *testing.T) { - synctest.Test(t, func(t *testing.T) { - // the anchor used in neighbourhood selection and reserve salt for sampling - prefix, err := hex.DecodeString("3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff") - if err != nil { - t.Fatal(err) - } - // the transformed address hasher factory function - prefixhasher := func() hash.Hash { return swarm.NewPrefixHasher(prefix) } - // Create a pool for efficient hasher reuse - trHasherPool := bmt.NewPool(bmt.NewConf(prefixhasher, swarm.BmtBranches, 8)) - // the bignum cast of the maximum sample value (upper bound on transformed addresses as a 256-bit article) - // this constant is for a minimum reserve size of 2 million chunks with sample size of 16 - // = 1.284401 * 10^71 = 1284401 + 66 0-s - mstring := "1284401" - for range 66 { - mstring = mstring + "0" - } - n, ok := new(big.Int).SetString(mstring, 10) - if !ok { - t.Fatalf("SetString: error setting to '%s'", mstring) - } - // the filter function on the SOC address - // meant to make sure we pass check for proof of retrievability for - // a node of overlay 0x65xxx with a reserve depth of 1, i.e., - // SOC address must start with zero bit - filterSOCAddr := func(a swarm.Address) bool { - return a.Bytes()[0]&0x80 != 0x00 - } - // the filter function on the transformed address using the density estimation constant - filterTrAddr := func(a swarm.Address) (bool, error) { - m := new(big.Int).SetBytes(a.Bytes()) - return m.Cmp(n) < 0, nil - } - // setup the signer with a private key from a fixture - data, err := hex.DecodeString("634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd") - if err != nil { - t.Fatal(err) - } - privKey, err := crypto.DecodeSecp256k1PrivateKey(data) - if err != nil { - t.Fatal(err) - } - signer := crypto.NewDefaultSigner(privKey) + // the anchor used in neighbourhood selection and reserve salt for sampling + prefix, err := hex.DecodeString("3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff") + if err != nil { + t.Fatal(err) + } + // Create a pool for efficient hasher reuse + trHasherPool := bmt.NewPool(bmt.NewConfWithPrefix(prefix, swarm.BmtBranches, 8)) + // the bignum cast of the maximum sample value (upper bound on transformed addresses as a 256-bit article) + // this constant is for a minimum reserve size of 2 million chunks with sample size of 16 + // = 1.284401 * 10^71 = 1284401 + 66 0-s + mstring := "1284401" + for range 66 { + mstring = mstring + "0" + } + n, ok := new(big.Int).SetString(mstring, 10) + if !ok { + t.Fatalf("SetString: error setting to '%s'", mstring) + } + // the filter function on the SOC address + // meant to make sure we pass check for proof of retrievability for + // a node of overlay 0x65xxx with a reserve depth of 1, i.e., + // SOC address must start with zero bit + filterSOCAddr := func(a swarm.Address) bool { + return a.Bytes()[0]&0x80 != 0x00 + } + // the filter function on the transformed address using the density estimation constant + filterTrAddr := func(a swarm.Address) (bool, error) { + m := new(big.Int).SetBytes(a.Bytes()) + return m.Cmp(n) < 0, nil + } + // setup the signer with a private key from a fixture + data, err := hex.DecodeString("634fb5a872396d9693e5c9f9d7233cfa93f395c093371017ff44aa9ae6564cdd") + if err != nil { + t.Fatal(err) + } + privKey, err := crypto.DecodeSecp256k1PrivateKey(data) + if err != nil { + t.Fatal(err) + } + signer := crypto.NewDefaultSigner(privKey) - sampleSize := 16 - // for sanity check: given a filterSOCAddr requiring a 0 leading bit (chance of 1/2) - // we expect an overall rough 4 million chunks to be mined to create this sample - // for 8 workers that is half a million round on average per worker - err = makeChunks(t, signer, sampleSize, filterSOCAddr, filterTrAddr, trHasherPool) - if err != nil { - t.Fatal(err) - } - }) + sampleSize := 16 + // for sanity check: given a filterSOCAddr requiring a 0 leading bit (chance of 1/2) + // we expect an overall rough 4 million chunks to be mined to create this sample + // for 8 workers that is half a million round on average per worker + err = makeChunks(t, signer, sampleSize, filterSOCAddr, filterTrAddr, trHasherPool) + if err != nil { + t.Fatal(err) + } } func makeChunks(t *testing.T, signer crypto.Signer, sampleSize int, filterSOCAddr func(swarm.Address) bool, filterTrAddr func(swarm.Address) (bool, error), trHasherPool *bmt.Pool) error { diff --git a/pkg/storer/sample.go b/pkg/storer/sample.go index 43999429300..ae4b88c66a6 100644 --- a/pkg/storer/sample.go +++ b/pkg/storer/sample.go @@ -9,7 +9,6 @@ import ( "context" "encoding/binary" "fmt" - "hash" "math/big" "runtime" "sort" @@ -121,16 +120,12 @@ func (db *DB) ReserveSample( // Phase 2: Get the chunk data and calculate transformed hash sampleItemChan := make(chan SampleItem, 3*workers) - prefixHasherFactory := func() hash.Hash { - return swarm.NewPrefixHasher(anchor) - } - db.logger.Debug("reserve sampler workers", "count", workers) for range workers { g.Go(func() error { wstat := SampleStats{} - hasher := bmt.NewHasher(prefixHasherFactory) + hasher := bmt.NewPrefixHasher(anchor) defer func() { addStats(wstat) }() @@ -407,12 +402,9 @@ func RandSample(t *testing.T, anchor []byte) Sample { // MakeSampleUsingChunks returns Sample constructed using supplied chunks. func MakeSampleUsingChunks(chunks []swarm.Chunk, anchor []byte) (Sample, error) { - prefixHasherFactory := func() hash.Hash { - return swarm.NewPrefixHasher(anchor) - } items := make([]SampleItem, len(chunks)) for i, ch := range chunks { - tr, err := transformedAddress(bmt.NewHasher(prefixHasherFactory), ch, getChunkType(ch)) + tr, err := transformedAddress(bmt.NewPrefixHasher(anchor), ch, getChunkType(ch)) if err != nil { return Sample{}, err }