mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-01 08:35:24 -05:00
Compare commits
5 Commits
e2e-debugg
...
hashtree-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b00cacef4 | ||
|
|
6f89cb5330 | ||
|
|
e315f78b97 | ||
|
|
536df4b682 | ||
|
|
1b98d8c202 |
31
BUILD.bazel
31
BUILD.bazel
@@ -1,8 +1,9 @@
|
|||||||
|
load("@prysm//tools/go:def.bzl", "go_library")
|
||||||
load("@bazel_gazelle//:def.bzl", "gazelle")
|
load("@bazel_gazelle//:def.bzl", "gazelle")
|
||||||
load("@com_github_atlassian_bazel_tools//gometalinter:def.bzl", "gometalinter")
|
load("@com_github_atlassian_bazel_tools//gometalinter:def.bzl", "gometalinter")
|
||||||
load("@com_github_atlassian_bazel_tools//goimports:def.bzl", "goimports")
|
load("@com_github_atlassian_bazel_tools//goimports:def.bzl", "goimports")
|
||||||
load("@io_kubernetes_build//defs:run_in_workspace.bzl", "workspace_binary")
|
load("@io_kubernetes_build//defs:run_in_workspace.bzl", "workspace_binary")
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "nogo")
|
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "nogo")
|
||||||
load("@bazel_skylib//rules:common_settings.bzl", "string_setting")
|
load("@bazel_skylib//rules:common_settings.bzl", "string_setting")
|
||||||
load("@prysm//tools/nogo_config:def.bzl", "nogo_config_exclude")
|
load("@prysm//tools/nogo_config:def.bzl", "nogo_config_exclude")
|
||||||
|
|
||||||
@@ -282,3 +283,31 @@ sh_binary(
|
|||||||
srcs = ["prysm.sh"],
|
srcs = ["prysm.sh"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"compare_states.go",
|
||||||
|
"reproduce_bug.go",
|
||||||
|
"verify_fix.go",
|
||||||
|
],
|
||||||
|
importpath = "github.com/OffchainLabs/prysm/v6",
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
deps = [
|
||||||
|
"//beacon-chain/core/helpers:go_default_library",
|
||||||
|
"//beacon-chain/core/transition:go_default_library",
|
||||||
|
"//beacon-chain/state:go_default_library",
|
||||||
|
"//beacon-chain/state/state-native:go_default_library",
|
||||||
|
"//consensus-types/blocks:go_default_library",
|
||||||
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
|
"//time/slots:go_default_library",
|
||||||
|
"@com_github_pkg_errors//:go_default_library",
|
||||||
|
"@in_gopkg_d4l3k_messagediff_v1//:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_binary(
|
||||||
|
name = "v6",
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|||||||
@@ -415,6 +415,10 @@ load("@prysm//third_party/herumi:herumi.bzl", "bls_dependencies")
|
|||||||
|
|
||||||
bls_dependencies()
|
bls_dependencies()
|
||||||
|
|
||||||
|
load("@prysm//third_party/hashtree:hashtree.bzl", "hashtree_dependencies")
|
||||||
|
|
||||||
|
hashtree_dependencies()
|
||||||
|
|
||||||
load("@prysm//testing/endtoend:deps.bzl", "e2e_deps")
|
load("@prysm//testing/endtoend:deps.bzl", "e2e_deps")
|
||||||
|
|
||||||
e2e_deps()
|
e2e_deps()
|
||||||
|
|||||||
2
changelog/potuz_add_hashtree.md
Normal file
2
changelog/potuz_add_hashtree.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
### Added
|
||||||
|
- Add optional `--use-hashtree` flag to beacon-chain and validator clients for using the hashtree library instead of gohashtree for vectorized SHA-256 merkle tree hashing. The hashtree library provides optimized assembly implementations for x86_64 and ARM64 architectures without CGO overhead.
|
||||||
@@ -14,6 +14,7 @@ go_library(
|
|||||||
"//cmd:go_default_library",
|
"//cmd:go_default_library",
|
||||||
"//cmd/beacon-chain/sync/backfill/flags:go_default_library",
|
"//cmd/beacon-chain/sync/backfill/flags:go_default_library",
|
||||||
"//config/params:go_default_library",
|
"//config/params:go_default_library",
|
||||||
|
"//crypto/hash/htr:go_default_library",
|
||||||
"//encoding/bytesutil:go_default_library",
|
"//encoding/bytesutil:go_default_library",
|
||||||
"@com_github_sirupsen_logrus//:go_default_library",
|
"@com_github_sirupsen_logrus//:go_default_library",
|
||||||
"@com_github_urfave_cli_v2//:go_default_library",
|
"@com_github_urfave_cli_v2//:go_default_library",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/OffchainLabs/prysm/v6/cmd"
|
"github.com/OffchainLabs/prysm/v6/cmd"
|
||||||
"github.com/OffchainLabs/prysm/v6/config/params"
|
"github.com/OffchainLabs/prysm/v6/config/params"
|
||||||
|
"github.com/OffchainLabs/prysm/v6/crypto/hash/htr"
|
||||||
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
@@ -275,6 +276,13 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
|||||||
cfg.ForceHead = ctx.String(forceHeadFlag.Name)
|
cfg.ForceHead = ctx.String(forceHeadFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.Bool(UseHashtreeFlag.Name) {
|
||||||
|
logEnabled(UseHashtreeFlag)
|
||||||
|
htr.SetUseHashtree(true)
|
||||||
|
} else {
|
||||||
|
log.Info("Using gohashtree library for vectorized SHA-256 hashing")
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.IsSet(blacklistRoots.Name) {
|
if ctx.IsSet(blacklistRoots.Name) {
|
||||||
logEnabled(blacklistRoots)
|
logEnabled(blacklistRoots)
|
||||||
cfg.BlacklistedRoots = parseBlacklistedRoots(ctx.StringSlice(blacklistRoots.Name))
|
cfg.BlacklistedRoots = parseBlacklistedRoots(ctx.StringSlice(blacklistRoots.Name))
|
||||||
@@ -331,6 +339,12 @@ func ConfigureValidator(ctx *cli.Context) error {
|
|||||||
logEnabled(EnableBeaconRESTApi)
|
logEnabled(EnableBeaconRESTApi)
|
||||||
cfg.EnableBeaconRESTApi = true
|
cfg.EnableBeaconRESTApi = true
|
||||||
}
|
}
|
||||||
|
if ctx.Bool(UseHashtreeFlag.Name) {
|
||||||
|
logEnabled(UseHashtreeFlag)
|
||||||
|
htr.SetUseHashtree(true)
|
||||||
|
} else {
|
||||||
|
log.Info("Using gohashtree library for vectorized SHA-256 hashing")
|
||||||
|
}
|
||||||
if ctx.Bool(DisableDutiesV2.Name) {
|
if ctx.Bool(DisableDutiesV2.Name) {
|
||||||
logEnabled(DisableDutiesV2)
|
logEnabled(DisableDutiesV2)
|
||||||
cfg.DisableDutiesV2 = true
|
cfg.DisableDutiesV2 = true
|
||||||
|
|||||||
@@ -197,6 +197,13 @@ var (
|
|||||||
Usage: "(Work in progress): Enables the web portal for the validator client.",
|
Usage: "(Work in progress): Enables the web portal for the validator client.",
|
||||||
Value: false,
|
Value: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseHashtreeFlag enables using the hashtree library instead of gohashtree for vectorized hashing.
|
||||||
|
UseHashtreeFlag = &cli.BoolFlag{
|
||||||
|
Name: "use-hashtree",
|
||||||
|
Usage: "Uses the hashtree library instead of gohashtree for vectorized SHA-256 merkle tree hashing.",
|
||||||
|
Value: false,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// devModeFlags holds list of flags that are set when development mode is on.
|
// devModeFlags holds list of flags that are set when development mode is on.
|
||||||
@@ -219,6 +226,7 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
|
|||||||
EnableBeaconRESTApi,
|
EnableBeaconRESTApi,
|
||||||
DisableDutiesV2,
|
DisableDutiesV2,
|
||||||
EnableWebFlag,
|
EnableWebFlag,
|
||||||
|
UseHashtreeFlag,
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
// E2EValidatorFlags contains a list of the validator feature flags to be tested in E2E.
|
// E2EValidatorFlags contains a list of the validator feature flags to be tested in E2E.
|
||||||
@@ -257,6 +265,7 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
|
|||||||
enableExperimentalAttestationPool,
|
enableExperimentalAttestationPool,
|
||||||
forceHeadFlag,
|
forceHeadFlag,
|
||||||
blacklistRoots,
|
blacklistRoots,
|
||||||
|
UseHashtreeFlag,
|
||||||
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
|
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
|
||||||
|
|
||||||
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {
|
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
|
# gazelle:resolve go github.com/prysmaticlabs/hashtree //third_party/hashtree:go_default_library
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = ["hashtree.go"],
|
srcs = ["hashtree.go"],
|
||||||
importpath = "github.com/OffchainLabs/prysm/v6/crypto/hash/htr",
|
importpath = "github.com/OffchainLabs/prysm/v6/crypto/hash/htr",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = ["@com_github_prysmaticlabs_gohashtree//:go_default_library"],
|
deps = [
|
||||||
|
"//third_party/hashtree:go_default_library",
|
||||||
|
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
|
|||||||
@@ -5,27 +5,52 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/prysmaticlabs/gohashtree"
|
"github.com/prysmaticlabs/gohashtree"
|
||||||
|
hashtreelib "github.com/prysmaticlabs/hashtree"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minSliceSizeToParallelize = 5000
|
const minSliceSizeToParallelize = 5000
|
||||||
|
|
||||||
|
// HashFunc defines the interface for vectorized hash implementations
|
||||||
|
type HashFunc func(output [][32]byte, input [][32]byte) error
|
||||||
|
|
||||||
|
var (
|
||||||
|
// currentHashFunc holds the active hash implementation
|
||||||
|
currentHashFunc HashFunc = gohashtree.Hash
|
||||||
|
|
||||||
|
// useHashtree flag determines which implementation to use
|
||||||
|
useHashtree bool = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetUseHashtree configures whether to use the hashtree library (true) or gohashtree (false)
|
||||||
|
func SetUseHashtree(use bool) {
|
||||||
|
useHashtree = use
|
||||||
|
if use {
|
||||||
|
currentHashFunc = hashtreelib.Hash
|
||||||
|
} else {
|
||||||
|
currentHashFunc = gohashtree.Hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUseHashtree returns the current hashtree usage setting
|
||||||
|
func GetUseHashtree() bool {
|
||||||
|
return useHashtree
|
||||||
|
}
|
||||||
|
|
||||||
func hashParallel(inputList [][32]byte, outputList [][32]byte, wg *sync.WaitGroup) {
|
func hashParallel(inputList [][32]byte, outputList [][32]byte, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
err := gohashtree.Hash(outputList, inputList)
|
err := currentHashFunc(outputList, inputList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // lint:nopanic -- This should never panic.
|
panic(err) // lint:nopanic -- This should never panic.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VectorizedSha256 takes a list of roots and hashes them using CPU
|
// VectorizedSha256 takes a list of roots and hashes them using CPU
|
||||||
// specific vector instructions. Depending on host machine's specific
|
// specific vector instructions. Uses either gohashtree or hashtree
|
||||||
// hardware configuration, using this routine can lead to a significant
|
// implementation based on the current configuration.
|
||||||
// performance improvement compared to the default method of hashing
|
|
||||||
// lists.
|
|
||||||
func VectorizedSha256(inputList [][32]byte) [][32]byte {
|
func VectorizedSha256(inputList [][32]byte) [][32]byte {
|
||||||
outputList := make([][32]byte, len(inputList)/2)
|
outputList := make([][32]byte, len(inputList)/2)
|
||||||
if len(inputList) < minSliceSizeToParallelize {
|
if len(inputList) < minSliceSizeToParallelize {
|
||||||
err := gohashtree.Hash(outputList, inputList)
|
err := currentHashFunc(outputList, inputList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // lint:nopanic -- This should never panic.
|
panic(err) // lint:nopanic -- This should never panic.
|
||||||
}
|
}
|
||||||
@@ -38,7 +63,7 @@ func VectorizedSha256(inputList [][32]byte) [][32]byte {
|
|||||||
for j := 0; j < n; j++ {
|
for j := 0; j < n; j++ {
|
||||||
go hashParallel(inputList[j*2*groupSize:(j+1)*2*groupSize], outputList[j*groupSize:], &wg)
|
go hashParallel(inputList[j*2*groupSize:(j+1)*2*groupSize], outputList[j*groupSize:], &wg)
|
||||||
}
|
}
|
||||||
err := gohashtree.Hash(outputList[n*groupSize:], inputList[n*2*groupSize:])
|
err := currentHashFunc(outputList[n*groupSize:], inputList[n*2*groupSize:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // lint:nopanic -- This should never panic.
|
panic(err) // lint:nopanic -- This should never panic.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package htr
|
package htr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -25,3 +26,247 @@ func Test_VectorizedSha256(t *testing.T) {
|
|||||||
require.Equal(t, r, hash2[i])
|
require.Equal(t, r, hash2[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generateTestData creates random test data for hashing tests
|
||||||
|
func generateTestData(size int) [][32]byte {
|
||||||
|
data := make([][32]byte, size)
|
||||||
|
for i := range data {
|
||||||
|
_, err := rand.Read(data[i][:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // This should never happen in tests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test_GohashtreeVsHashtree verifies both implementations produce identical results
|
||||||
|
func Test_GohashtreeVsHashtree(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
size int
|
||||||
|
}{
|
||||||
|
{"small", 100},
|
||||||
|
{"medium", 1000},
|
||||||
|
{"large", 10000},
|
||||||
|
{"very_large", 50000},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Generate test data (must be even number for hash pairs)
|
||||||
|
input := generateTestData(tt.size * 2)
|
||||||
|
|
||||||
|
// Test with gohashtree (default)
|
||||||
|
SetUseHashtree(false)
|
||||||
|
gohashtreeResult := VectorizedSha256(input)
|
||||||
|
|
||||||
|
// Test with hashtree
|
||||||
|
SetUseHashtree(true)
|
||||||
|
hashtreeResult := VectorizedSha256(input)
|
||||||
|
|
||||||
|
// Reset to default
|
||||||
|
SetUseHashtree(false)
|
||||||
|
|
||||||
|
// Results should be identical
|
||||||
|
require.Equal(t, len(gohashtreeResult), len(hashtreeResult), "Result lengths should match")
|
||||||
|
for i := range gohashtreeResult {
|
||||||
|
require.Equal(t, gohashtreeResult[i], hashtreeResult[i], "Hash results should be identical at index %d", i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test_GohashtreeImplementation tests gohashtree specifically
|
||||||
|
func Test_GohashtreeImplementation(t *testing.T) {
|
||||||
|
// Force gohashtree
|
||||||
|
SetUseHashtree(false)
|
||||||
|
defer SetUseHashtree(false) // Reset after test
|
||||||
|
|
||||||
|
require.Equal(t, false, GetUseHashtree(), "Should be using gohashtree")
|
||||||
|
|
||||||
|
// Test small input (non-parallel path)
|
||||||
|
smallInput := generateTestData(10)
|
||||||
|
smallResult := VectorizedSha256(smallInput)
|
||||||
|
require.Equal(t, 5, len(smallResult), "Small input should produce correct number of hashes")
|
||||||
|
|
||||||
|
// Test large input (parallel path)
|
||||||
|
largeInput := generateTestData(minSliceSizeToParallelize + 100)
|
||||||
|
largeResult := VectorizedSha256(largeInput)
|
||||||
|
expectedLen := (minSliceSizeToParallelize + 100) / 2
|
||||||
|
require.Equal(t, expectedLen, len(largeResult), "Large input should produce correct number of hashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test_HashtreeImplementation tests hashtree specifically
|
||||||
|
func Test_HashtreeImplementation(t *testing.T) {
|
||||||
|
// Force hashtree
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false) // Reset after test
|
||||||
|
|
||||||
|
require.Equal(t, true, GetUseHashtree(), "Should be using hashtree")
|
||||||
|
|
||||||
|
// Test small input
|
||||||
|
smallInput := generateTestData(10)
|
||||||
|
smallResult := VectorizedSha256(smallInput)
|
||||||
|
require.Equal(t, 5, len(smallResult), "Small input should produce correct number of hashes")
|
||||||
|
|
||||||
|
// Test large input
|
||||||
|
largeInput := generateTestData(minSliceSizeToParallelize + 100)
|
||||||
|
largeResult := VectorizedSha256(largeInput)
|
||||||
|
expectedLen := (minSliceSizeToParallelize + 100) / 2
|
||||||
|
require.Equal(t, expectedLen, len(largeResult), "Large input should produce correct number of hashes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test_ThreadSafety verifies both implementations work correctly with concurrent access
|
||||||
|
func Test_ThreadSafety(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
useHashtree bool
|
||||||
|
}{
|
||||||
|
{"gohashtree_concurrent", false},
|
||||||
|
{"hashtree_concurrent", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
SetUseHashtree(tt.useHashtree)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
|
||||||
|
const numGoroutines = 10
|
||||||
|
const inputSize = 1000
|
||||||
|
|
||||||
|
results := make([][][32]byte, numGoroutines)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
// Run concurrent hashing
|
||||||
|
for i := 0; i < numGoroutines; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(index int) {
|
||||||
|
defer wg.Done()
|
||||||
|
input := generateTestData(inputSize)
|
||||||
|
results[index] = VectorizedSha256(input)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Verify all results have correct length
|
||||||
|
expectedLen := inputSize / 2
|
||||||
|
for i, result := range results {
|
||||||
|
require.Equal(t, expectedLen, len(result), "Result %d should have correct length", i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_GohashtreeSmall benchmarks gohashtree with small input
|
||||||
|
func Benchmark_GohashtreeSmall(b *testing.B) {
|
||||||
|
SetUseHashtree(false)
|
||||||
|
input := generateTestData(100)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_HashtreeSmall benchmarks hashtree with small input
|
||||||
|
func Benchmark_HashtreeSmall(b *testing.B) {
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
input := generateTestData(100)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_GohashtreeMedium benchmarks gohashtree with medium input
|
||||||
|
func Benchmark_GohashtreeMedium(b *testing.B) {
|
||||||
|
SetUseHashtree(false)
|
||||||
|
input := generateTestData(2000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_HashtreeMedium benchmarks hashtree with medium input
|
||||||
|
func Benchmark_HashtreeMedium(b *testing.B) {
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
input := generateTestData(2000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_GohashtreeLarge benchmarks gohashtree with large input (parallel path)
|
||||||
|
func Benchmark_GohashtreeLarge(b *testing.B) {
|
||||||
|
SetUseHashtree(false)
|
||||||
|
input := generateTestData(minSliceSizeToParallelize + 1000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_HashtreeLarge benchmarks hashtree with large input (parallel path)
|
||||||
|
func Benchmark_HashtreeLarge(b *testing.B) {
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
input := generateTestData(minSliceSizeToParallelize + 1000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_GohashtreeVeryLarge benchmarks gohashtree with very large input
|
||||||
|
func Benchmark_GohashtreeVeryLarge(b *testing.B) {
|
||||||
|
SetUseHashtree(false)
|
||||||
|
input := generateTestData(50000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_HashtreeVeryLarge benchmarks hashtree with very large input
|
||||||
|
func Benchmark_HashtreeVeryLarge(b *testing.B) {
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
input := generateTestData(50000)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark_Comparison runs both implementations side by side for direct comparison
|
||||||
|
func Benchmark_Comparison(b *testing.B) {
|
||||||
|
input := generateTestData(10000)
|
||||||
|
|
||||||
|
b.Run("gohashtree", func(b *testing.B) {
|
||||||
|
SetUseHashtree(false)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("hashtree", func(b *testing.B) {
|
||||||
|
SetUseHashtree(true)
|
||||||
|
defer SetUseHashtree(false)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = VectorizedSha256(input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -61,6 +61,7 @@ require (
|
|||||||
github.com/prometheus/prom2json v1.3.0
|
github.com/prometheus/prom2json v1.3.0
|
||||||
github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516
|
github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516
|
||||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e
|
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e
|
||||||
|
github.com/prysmaticlabs/hashtree v0.2.0
|
||||||
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c
|
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c
|
||||||
github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294
|
github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294
|
||||||
github.com/r3labs/sse/v2 v2.10.0
|
github.com/r3labs/sse/v2 v2.10.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -902,6 +902,8 @@ github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe
|
|||||||
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
|
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
|
||||||
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1:VK7thFOnhxAZ/5aolr5Os4beiubuD08WiuiHyRqgwks=
|
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b h1:VK7thFOnhxAZ/5aolr5Os4beiubuD08WiuiHyRqgwks=
|
||||||
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY=
|
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b/go.mod h1:HRuvtXLZ4WkaB1MItToVH2e8ZwKwZPY5/Rcby+CvvLY=
|
||||||
|
github.com/prysmaticlabs/hashtree v0.2.0 h1:vMBmqDmW9G7RYEc/Fv7OYnt9vCl8SYgDB2qQlm8Uj+k=
|
||||||
|
github.com/prysmaticlabs/hashtree v0.2.0/go.mod h1:8eJvdNI+eWfxzaCYVQ6EEMuBGx+kn3rMTaUI7aAVlCY=
|
||||||
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c h1:9PHRCuO/VN0s9k+RmLykho7AjDxblNYI5bYKed16NPU=
|
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c h1:9PHRCuO/VN0s9k+RmLykho7AjDxblNYI5bYKed16NPU=
|
||||||
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c/go.mod h1:ZRws458tYHS/Zs936OQ6oCrL+Ict5O4Xpwve1UQ6C9M=
|
github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c/go.mod h1:ZRws458tYHS/Zs936OQ6oCrL+Ict5O4Xpwve1UQ6C9M=
|
||||||
github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU=
|
github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h1:q9wE0ZZRdTUAAeyFP/w0SwBEnCqlVy2+on6X2/e+eAU=
|
||||||
|
|||||||
12
third_party/hashtree/BUILD.bazel
vendored
Normal file
12
third_party/hashtree/BUILD.bazel
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Alias to the hashtree library from the external repository
|
||||||
|
alias(
|
||||||
|
name = "hashtree",
|
||||||
|
actual = "@offchainlabs_hashtree//:hashtree",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
alias(
|
||||||
|
name = "go_default_library",
|
||||||
|
actual = "@offchainlabs_hashtree//:hashtree",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
22
third_party/hashtree/hashtree.bzl
vendored
Normal file
22
third_party/hashtree/hashtree.bzl
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
"""
|
||||||
|
OffchainLabs hashtree library for fast merkle tree hashing.
|
||||||
|
Uses native Go bindings with syso files, no CGO overhead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def hashtree_dependencies():
|
||||||
|
_maybe(
|
||||||
|
http_archive,
|
||||||
|
name = "offchainlabs_hashtree",
|
||||||
|
strip_prefix = "hashtree-main",
|
||||||
|
urls = [
|
||||||
|
"https://github.com/OffchainLabs/hashtree/archive/main.tar.gz",
|
||||||
|
],
|
||||||
|
build_file = "@prysm//third_party/hashtree:hashtree_source.BUILD",
|
||||||
|
integrity = "sha256-FLfalJ7at89Tgm/XnZ90zRxjYqkBmv2dD418uhfVvYc=",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _maybe(repo_rule, name, **kwargs):
|
||||||
|
if name not in native.existing_rules():
|
||||||
|
repo_rule(name = name, **kwargs)
|
||||||
203
third_party/hashtree/hashtree_source.BUILD
vendored
Normal file
203
third_party/hashtree/hashtree_source.BUILD
vendored
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
# BUILD file for building hashtree from source with proper cross-compilation support
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
# Config settings for platform detection
|
||||||
|
config_setting(
|
||||||
|
name = "windows_amd64",
|
||||||
|
constraint_values = [
|
||||||
|
"@platforms//os:windows",
|
||||||
|
"@platforms//cpu:x86_64",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Build hashtree library for AMD64 - only build when targeting x86_64
|
||||||
|
genrule(
|
||||||
|
name = "build_hashtree_amd64",
|
||||||
|
srcs = [
|
||||||
|
"src/hashtree.c",
|
||||||
|
"src/hashtree.h",
|
||||||
|
"src/sha256_generic.c",
|
||||||
|
"src/sha256_shani.S",
|
||||||
|
"src/sha256_avx_x16.S",
|
||||||
|
"src/sha256_avx_x8.S",
|
||||||
|
"src/sha256_avx_x4.S",
|
||||||
|
"src/sha256_avx_x1.S",
|
||||||
|
"src/sha256_sse_x1.S",
|
||||||
|
],
|
||||||
|
outs = ["hashtree_amd64.syso"],
|
||||||
|
cmd = """
|
||||||
|
# Create build directories
|
||||||
|
mkdir -p build/obj build/lib
|
||||||
|
|
||||||
|
# Use Bazel's toolchain - CC and AR are set by the toolchain
|
||||||
|
COMPILER="$${CC:-gcc}"
|
||||||
|
ARCHIVER="$${AR:-ar}"
|
||||||
|
|
||||||
|
# Add debugging output
|
||||||
|
echo "Building hashtree with compiler: $$COMPILER"
|
||||||
|
echo "Building hashtree with archiver: $$ARCHIVER"
|
||||||
|
echo "Target architecture: $${GOARCH:-unknown}"
|
||||||
|
|
||||||
|
# Compile assembly files (handles Intel syntax)
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_shani.S) -o build/obj/sha256_shani.o
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_avx_x16.S) -o build/obj/sha256_avx_x16.o
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_avx_x8.S) -o build/obj/sha256_avx_x8.o
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_avx_x4.S) -o build/obj/sha256_avx_x4.o
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_avx_x1.S) -o build/obj/sha256_avx_x1.o
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_sse_x1.S) -o build/obj/sha256_sse_x1.o
|
||||||
|
|
||||||
|
# Compile C files
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/sha256_generic.c) -o build/obj/sha256_generic.o
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/hashtree.c) -I. -o build/obj/hashtree.o
|
||||||
|
|
||||||
|
# Create static library
|
||||||
|
$$ARCHIVER rcs build/lib/libhashtree.a build/obj/*.o
|
||||||
|
|
||||||
|
# Copy to syso file
|
||||||
|
cp build/lib/libhashtree.a $@
|
||||||
|
""",
|
||||||
|
target_compatible_with = ["@platforms//cpu:x86_64"],
|
||||||
|
tags = ["requires-network"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build hashtree library for ARM64 - only build when targeting ARM64
|
||||||
|
genrule(
|
||||||
|
name = "build_hashtree_arm64",
|
||||||
|
srcs = [
|
||||||
|
"src/hashtree.c",
|
||||||
|
"src/hashtree.h",
|
||||||
|
"src/sha256_generic.c",
|
||||||
|
"src/sha256_armv8_neon_x4.S",
|
||||||
|
"src/sha256_armv8_neon_x1.S",
|
||||||
|
"src/sha256_armv8_crypto.S",
|
||||||
|
],
|
||||||
|
outs = ["hashtree_arm64.syso"],
|
||||||
|
cmd = """
|
||||||
|
# Create build directories
|
||||||
|
mkdir -p build/obj build/lib
|
||||||
|
|
||||||
|
# Try to use proper cross-compilation compiler
|
||||||
|
if command -v clang >/dev/null 2>&1; then
|
||||||
|
# CI environment with clang - use ARM64 cross-compilation
|
||||||
|
COMPILER="clang --target=aarch64-linux-gnu"
|
||||||
|
ARCHIVER="llvm-ar"
|
||||||
|
echo "Using clang cross-compiler for ARM64"
|
||||||
|
elif command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then
|
||||||
|
# Local environment with ARM64 GCC cross-compiler
|
||||||
|
COMPILER="aarch64-linux-gnu-gcc"
|
||||||
|
ARCHIVER="aarch64-linux-gnu-ar"
|
||||||
|
echo "Using GCC cross-compiler for ARM64"
|
||||||
|
else
|
||||||
|
# Fallback: use system compiler (will fail for cross-compilation)
|
||||||
|
COMPILER="gcc"
|
||||||
|
ARCHIVER="ar"
|
||||||
|
echo "WARNING: Using system compiler - cross-compilation may fail"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building hashtree with compiler: $$COMPILER"
|
||||||
|
echo "Building hashtree with archiver: $$ARCHIVER"
|
||||||
|
|
||||||
|
# Compile assembly files with ARM64 target
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_armv8_neon_x4.S) -o build/obj/sha256_armv8_neon_x4.o || echo "Failed to compile neon_x4"
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_armv8_neon_x1.S) -o build/obj/sha256_armv8_neon_x1.o || echo "Failed to compile neon_x1"
|
||||||
|
$$COMPILER -g -fpic -c $(location src/sha256_armv8_crypto.S) -o build/obj/sha256_armv8_crypto.o || echo "Failed to compile crypto"
|
||||||
|
|
||||||
|
# Compile C files with ARM64 target
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/sha256_generic.c) -o build/obj/sha256_generic.o || echo "Failed to compile generic"
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/hashtree.c) -I. -o build/obj/hashtree.o || echo "Failed to compile hashtree"
|
||||||
|
|
||||||
|
# Create static library
|
||||||
|
$$ARCHIVER rcs build/lib/libhashtree.a build/obj/*.o || echo "Failed to create archive"
|
||||||
|
|
||||||
|
# Copy to syso file
|
||||||
|
cp build/lib/libhashtree.a $@
|
||||||
|
""",
|
||||||
|
target_compatible_with = ["@platforms//cpu:aarch64"],
|
||||||
|
tags = ["requires-network"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build hashtree library for Windows AMD64 - compile from source
|
||||||
|
genrule(
|
||||||
|
name = "build_hashtree_windows_amd64",
|
||||||
|
srcs = [
|
||||||
|
"src/hashtree.c",
|
||||||
|
"src/hashtree.h",
|
||||||
|
"src/sha256_generic.c",
|
||||||
|
"src/sha256_shani.S",
|
||||||
|
"src/sha256_avx_x16.S",
|
||||||
|
"src/sha256_avx_x8.S",
|
||||||
|
"src/sha256_avx_x4.S",
|
||||||
|
"src/sha256_avx_x1.S",
|
||||||
|
"src/sha256_sse_x1.S",
|
||||||
|
],
|
||||||
|
outs = ["hashtree_windows_amd64.syso"],
|
||||||
|
cmd = """
|
||||||
|
# Create build directories
|
||||||
|
mkdir -p build/obj build/lib
|
||||||
|
|
||||||
|
# Use Bazel's Windows cross-compilation toolchain
|
||||||
|
COMPILER="$${CC:-x86_64-w64-mingw32-gcc}"
|
||||||
|
ARCHIVER="$${AR:-x86_64-w64-mingw32-ar}"
|
||||||
|
|
||||||
|
# Add debugging output
|
||||||
|
echo "Building hashtree for Windows with compiler: $$COMPILER"
|
||||||
|
echo "Building hashtree for Windows with archiver: $$ARCHIVER"
|
||||||
|
|
||||||
|
# Compile assembly files for Windows (Intel syntax)
|
||||||
|
$$COMPILER -g -c $(location src/sha256_shani.S) -o build/obj/sha256_shani.o
|
||||||
|
$$COMPILER -g -c $(location src/sha256_avx_x16.S) -o build/obj/sha256_avx_x16.o
|
||||||
|
$$COMPILER -g -c $(location src/sha256_avx_x8.S) -o build/obj/sha256_avx_x8.o
|
||||||
|
$$COMPILER -g -c $(location src/sha256_avx_x4.S) -o build/obj/sha256_avx_x4.o
|
||||||
|
$$COMPILER -g -c $(location src/sha256_avx_x1.S) -o build/obj/sha256_avx_x1.o
|
||||||
|
$$COMPILER -g -c $(location src/sha256_sse_x1.S) -o build/obj/sha256_sse_x1.o
|
||||||
|
|
||||||
|
# Compile C files for Windows
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/sha256_generic.c) -o build/obj/sha256_generic.o
|
||||||
|
$$COMPILER -g -Wall -Werror -O3 -c $(location src/hashtree.c) -I. -o build/obj/hashtree.o
|
||||||
|
|
||||||
|
# Create static library (Windows uses .lib extension but we create .a for compatibility)
|
||||||
|
$$ARCHIVER rcs build/lib/libhashtree.a build/obj/*.o
|
||||||
|
|
||||||
|
# Copy to syso file
|
||||||
|
cp build/lib/libhashtree.a $@
|
||||||
|
""",
|
||||||
|
target_compatible_with = [
|
||||||
|
"@platforms//os:windows",
|
||||||
|
"@platforms//cpu:x86_64",
|
||||||
|
],
|
||||||
|
tags = ["requires-network"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Empty syso file for platforms where hashtree is not available
|
||||||
|
genrule(
|
||||||
|
name = "build_hashtree_generic",
|
||||||
|
outs = ["hashtree_generic.syso"],
|
||||||
|
cmd = "touch $@",
|
||||||
|
tags = ["manual"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Go library with architecture-specific syso files
|
||||||
|
go_library(
|
||||||
|
name = "hashtree",
|
||||||
|
srcs = [
|
||||||
|
"bindings.go",
|
||||||
|
"bindings_amd64.go",
|
||||||
|
"bindings_arm64.go",
|
||||||
|
"sha256_1_generic.go",
|
||||||
|
"wrapper_linux_amd64.s",
|
||||||
|
"wrapper_arm64.s",
|
||||||
|
"wrapper_windows_amd64.s",
|
||||||
|
] + select({
|
||||||
|
":windows_amd64": [":build_hashtree_windows_amd64"],
|
||||||
|
"@platforms//cpu:x86_64": [":build_hashtree_amd64"],
|
||||||
|
"@platforms//cpu:aarch64": [":build_hashtree_arm64"],
|
||||||
|
"//conditions:default": [":build_hashtree_generic"],
|
||||||
|
}),
|
||||||
|
cgo = False,
|
||||||
|
importpath = "github.com/prysmaticlabs/hashtree",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = ["@com_github_klauspost_cpuid_v2//:go_default_library"],
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user