高性能Golang Merkle树实现方案探讨

高性能Golang Merkle树实现方案探讨 大家好,祝你们有美好的一天!

我很高兴与大家分享我新实现的高性能 Golang Merkle 树计算库。

GitHub 链接:https://github.com/txaty/go-merkletree

Go 文档:https://pkg.go.dev/github.com/txaty/go-merkletree

我认为大多数时候我们需要的 Merkle 树是 Merkle 根和每个叶子节点的证明,这样我们就可以通过使用其证明重新计算根,然后将计算出的根与原始根进行比较,来证明叶子节点的成员资格/存在性。如果计算出的根与原始根相等,那么叶子节点的成员资格就是有效的(它存在)。

因此,与其他一些实现不同,在构建新的 Merkle 树时,我的程序只构建叶子节点证明并最终生成 Merkle 根,而不是缓存树本身。通过这种优化,我的程序运行速度比 GitHub 上最受欢迎的类似库 cbergoon/merkletree 快得多。我通过使用 goroutine 进行并行化进一步提高了性能。

以下是我的 Merkle 树库与 cbergoon/merkletree 的快速基准测试。我测量了:

  • 证明生成时间——生成 Merkle 根和所有叶子节点的证明,以及
  • 验证时间——验证树中的所有叶子节点。

在我的库中,树的构建和证明生成是同时进行的。在 cbergoon/merkletree 中,树的构建只构建树,每个叶子节点的证明(Merkle 路径)需要通过为每个叶子节点调用 GetMerklePath() 函数来获取。

1,000 个节点(证明生成快 4.7 倍,验证快 3.6 倍):

goos: linux
goarch: amd64
pkg: github.com/txaty/go-merkletree
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMerkleTreeProofGen
BenchmarkMerkleTreeProofGen-12             	     774	   1525119 ns/op
BenchmarkMerkleTreeProofGenParallel
BenchmarkMerkleTreeProofGenParallel-12     	     866	   1402052 ns/op
Benchmark_cbergoonMerkleTreeProofGen
Benchmark_cbergoonMerkleTreeProofGen-12    	     165	   7142707 ns/op
BenchmarkMerkleTreeVerify
BenchmarkMerkleTreeVerify-12               	     172	   6886498 ns/op
Benchmark_cbergoonMerkleTreeVerify
Benchmark_cbergoonMerkleTreeVerify-12      	      46	  24741956 ns/op
PASS

10,000 个节点(证明生成快 24 倍,验证快 8.2 倍):

goos: linux
goarch: amd64
pkg: github.com/txaty/go-merkletree
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMerkleTreeProofGen
BenchmarkMerkleTreeProofGen-12             	      55	  20999696 ns/op
BenchmarkMerkleTreeProofGenParallel
BenchmarkMerkleTreeProofGenParallel-12     	     128	   9295963 ns/op
Benchmark_cbergoonMerkleTreeProofGen
Benchmark_cbergoonMerkleTreeProofGen-12    	       2	 504747049 ns/op
BenchmarkMerkleTreeVerify
BenchmarkMerkleTreeVerify-12               	      12	  93694508 ns/op
Benchmark_cbergoonMerkleTreeVerify
Benchmark_cbergoonMerkleTreeVerify-12      	       2	 771403038 ns/op
PASS

100,000 个节点(证明生成快 333 倍,验证快 42 倍):

goos: linux
goarch: amd64
pkg: github.com/txaty/go-merkletree
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkMerkleTreeProofGen
BenchmarkMerkleTreeProofGen-12             	       6	 181101101 ns/op
BenchmarkMerkleTreeProofGenParallel
BenchmarkMerkleTreeProofGenParallel-12     	      10	 107610935 ns/op
Benchmark_cbergoonMerkleTreeProofGen
Benchmark_cbergoonMerkleTreeProofGen-12    	       1	60383341291 ns/op
BenchmarkMerkleTreeVerify
BenchmarkMerkleTreeVerify-12               	       1	1148882340 ns/op
Benchmark_cbergoonMerkleTreeVerify
Benchmark_cbergoonMerkleTreeVerify-12      	       1	48003616811 ns/op
PASS

此外,对于大量数据块,并行化算法的运行速度比原始算法快得多。目前,并行化还不够完美,我仍在思考如何改进它。

希望我的库能对您有所帮助。如果您在我的代码中发现任何问题,请告知我(例如,提交一个 issue)。谢谢!slight_smile


更多关于高性能Golang Merkle树实现方案探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于高性能Golang Merkle树实现方案探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢分享这个高性能的Golang Merkle树实现。从基准测试结果来看,你的库在性能上确实有显著优势,特别是在处理大量节点时。以下是一些技术观察和示例代码:

性能优化分析:

  1. 延迟构建策略:只计算必要的证明和根节点,避免了完整树的存储开销
  2. 并行计算:利用goroutine并发处理哈希计算,适合现代多核CPU
  3. 内存效率:不缓存中间节点,减少了内存分配和GC压力

示例使用代码:

package main

import (
    "crypto/sha256"
    "fmt"
    "github.com/txaty/go-merkletree"
)

type DataBlock struct {
    data []byte
}

func (d *DataBlock) Serialize() []byte {
    return d.data
}

func main() {
    // 准备数据块
    blocks := make([]merkletree.DataBlock, 0, 1000)
    for i := 0; i < 1000; i++ {
        blocks = append(blocks, &DataBlock{data: []byte(fmt.Sprintf("block%d", i))})
    }

    // 配置Merkle树
    config := &merkletree.Config{
        HashFunc: sha256.New,
        RunInParallel: true,
    }

    // 构建树并生成证明
    tree, err := merkletree.New(config, blocks)
    if err != nil {
        panic(err)
    }

    // 获取根哈希
    root := tree.Root
    fmt.Printf("Merkle Root: %x\n", root)

    // 验证特定数据块
    proof := tree.Proofs[0]
    verified := merkletree.Verify(blocks[0], proof, root, config.HashFunc)
    fmt.Printf("Verification result: %v\n", verified)
}

并行处理实现要点:

// 在你的实现中,并行哈希计算可能类似这样:
func (t *Tree) buildTreeParallel(blocks []DataBlock) {
    var wg sync.WaitGroup
    chunkSize := len(blocks) / runtime.NumCPU()
    
    for i := 0; i < len(blocks); i += chunkSize {
        wg.Add(1)
        go func(start int) {
            defer wg.Done()
            end := start + chunkSize
            if end > len(blocks) {
                end = len(blocks)
            }
            // 处理数据块子集
            t.processChunk(blocks[start:end], start)
        }(i)
    }
    wg.Wait()
}

性能对比的关键因素:

  1. 哈希计算优化:批量处理减少哈希函数调用次数
  2. 内存布局:连续内存访问模式提高缓存命中率
  3. 证明生成算法:单次遍历同时生成所有证明,避免重复计算

这个实现特别适合需要高效验证大量数据完整性的场景,如区块链、分布式存储系统等。基准测试显示随着节点数量增加,性能优势更加明显,这验证了设计决策的正确性。

回到顶部