mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package parallel
|
|
|
|
import (
|
|
"hash"
|
|
"runtime"
|
|
"sync"
|
|
|
|
"github.com/luxfi/geth/common"
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
// keccakState wraps hash.Hash with Read for extracting the digest without
|
|
// copying internal state. sha3.NewLegacyKeccak256() satisfies this.
|
|
type keccakState interface {
|
|
hash.Hash
|
|
Read([]byte) (int, error)
|
|
}
|
|
|
|
// Hasher computes Keccak-256 hashes for trie node batches.
|
|
// The CPU implementation is the only one in Go; GPU keccak lives in
|
|
// luxcpp (Metal/CUDA kernels) and is reached through the luxgpu cgo bridge,
|
|
// not through a Go-native hasher.
|
|
type Hasher interface {
|
|
// BatchHash computes Keccak-256 hashes for a batch of inputs.
|
|
// Returns one hash per input, in the same order.
|
|
BatchHash(inputs [][]byte) []common.Hash
|
|
|
|
// Backend returns the name of the compute backend ("Metal", "CUDA", "CPU").
|
|
Backend() string
|
|
}
|
|
|
|
// cpuHasher computes Keccak-256 in parallel across CPU cores.
|
|
type cpuHasher struct{}
|
|
|
|
var keccakPool = sync.Pool{
|
|
New: func() any {
|
|
return sha3.NewLegacyKeccak256()
|
|
},
|
|
}
|
|
|
|
// NewCPUHasher returns a Hasher that uses parallel CPU goroutines.
|
|
func NewCPUHasher() Hasher {
|
|
return &cpuHasher{}
|
|
}
|
|
|
|
// BatchHash computes Keccak-256 for each input using parallel goroutines.
|
|
// Splits the work across GOMAXPROCS workers for large batches.
|
|
func (c *cpuHasher) BatchHash(inputs [][]byte) []common.Hash {
|
|
n := len(inputs)
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
results := make([]common.Hash, n)
|
|
|
|
// For small batches, hash sequentially to avoid goroutine overhead.
|
|
if n <= 16 {
|
|
for i, inp := range inputs {
|
|
d := keccakPool.Get().(keccakState)
|
|
d.Reset()
|
|
d.Write(inp)
|
|
d.Read(results[i][:])
|
|
keccakPool.Put(d)
|
|
}
|
|
return results
|
|
}
|
|
|
|
// Parallel hashing across CPU cores.
|
|
numWorkers := runtime.GOMAXPROCS(0)
|
|
if numWorkers > n {
|
|
numWorkers = n
|
|
}
|
|
chunkSize := (n + numWorkers - 1) / numWorkers
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(numWorkers)
|
|
|
|
for w := 0; w < numWorkers; w++ {
|
|
start := w * chunkSize
|
|
end := start + chunkSize
|
|
if end > n {
|
|
end = n
|
|
}
|
|
if start >= n {
|
|
wg.Done()
|
|
continue
|
|
}
|
|
|
|
go func(start, end int) {
|
|
defer wg.Done()
|
|
d := keccakPool.Get().(keccakState)
|
|
defer keccakPool.Put(d)
|
|
|
|
for i := start; i < end; i++ {
|
|
d.Reset()
|
|
d.Write(inputs[i])
|
|
d.Read(results[i][:])
|
|
}
|
|
}(start, end)
|
|
}
|
|
|
|
wg.Wait()
|
|
return results
|
|
}
|
|
|
|
// Backend returns "CPU".
|
|
func (c *cpuHasher) Backend() string {
|
|
return "CPU"
|
|
}
|