mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
added new bitvec implementation
* updated benchmarks to compare performance
This commit is contained in:
parent
04593bb71a
commit
8a9807da9a
2 changed files with 214 additions and 36 deletions
|
|
@ -116,3 +116,73 @@ func codeBitmapInternal(code, bits bitvec) bitvec {
|
||||||
}
|
}
|
||||||
return bits
|
return bits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bitVec []uint32
|
||||||
|
|
||||||
|
func (b bitVec) isCode(pc uint64) bool {
|
||||||
|
return (b[pc/32] & (1 << (pc % 32))) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCodeBitVec(code []byte) (bv bitVec) {
|
||||||
|
bv = make(bitVec, len(code)/32+2)
|
||||||
|
bv.codeBitVec(code)
|
||||||
|
return bv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bv bitVec) codeBitVec(code []byte) bitVec {
|
||||||
|
var pc uint64
|
||||||
|
for pc < uint64(len(code)) {
|
||||||
|
op := code[pc]
|
||||||
|
if int8(op) < 0x60 {
|
||||||
|
pc++
|
||||||
|
continue // continue if the OpCode is not PUSH1..32
|
||||||
|
}
|
||||||
|
numBytes := op - 0x5f // number of data bytes pushed
|
||||||
|
|
||||||
|
// set bits
|
||||||
|
mask := masks[numBytes] // optimization for: (1 << numBytes) - 1
|
||||||
|
shift := uint8(pc+1) % 32
|
||||||
|
|
||||||
|
i := (pc + 1) / 32
|
||||||
|
bv[i+1] = mask >> (32 - shift)
|
||||||
|
bv[i] |= mask << shift
|
||||||
|
|
||||||
|
pc += uint64(numBytes + 1)
|
||||||
|
}
|
||||||
|
return bv
|
||||||
|
}
|
||||||
|
|
||||||
|
var masks = [256]uint32{
|
||||||
|
1: 0b00000000_00000000_00000000_00000001,
|
||||||
|
2: 0b00000000_00000000_00000000_00000011,
|
||||||
|
3: 0b00000000_00000000_00000000_00000111,
|
||||||
|
4: 0b00000000_00000000_00000000_00001111,
|
||||||
|
5: 0b00000000_00000000_00000000_00011111,
|
||||||
|
6: 0b00000000_00000000_00000000_00111111,
|
||||||
|
7: 0b00000000_00000000_00000000_01111111,
|
||||||
|
8: 0b00000000_00000000_00000000_11111111,
|
||||||
|
9: 0b00000000_00000000_00000001_11111111,
|
||||||
|
10: 0b00000000_00000000_00000011_11111111,
|
||||||
|
11: 0b00000000_00000000_00000111_11111111,
|
||||||
|
12: 0b00000000_00000000_00001111_11111111,
|
||||||
|
13: 0b00000000_00000000_00011111_11111111,
|
||||||
|
14: 0b00000000_00000000_00111111_11111111,
|
||||||
|
15: 0b00000000_00000000_01111111_11111111,
|
||||||
|
16: 0b00000000_00000000_11111111_11111111,
|
||||||
|
17: 0b00000000_00000001_11111111_11111111,
|
||||||
|
18: 0b00000000_00000011_11111111_11111111,
|
||||||
|
19: 0b00000000_00000111_11111111_11111111,
|
||||||
|
20: 0b00000000_00001111_11111111_11111111,
|
||||||
|
21: 0b00000000_00011111_11111111_11111111,
|
||||||
|
22: 0b00000000_00111111_11111111_11111111,
|
||||||
|
23: 0b00000000_01111111_11111111_11111111,
|
||||||
|
24: 0b00000000_11111111_11111111_11111111,
|
||||||
|
25: 0b00000001_11111111_11111111_11111111,
|
||||||
|
26: 0b00000011_11111111_11111111_11111111,
|
||||||
|
27: 0b00000111_11111111_11111111_11111111,
|
||||||
|
28: 0b00001111_11111111_11111111_11111111,
|
||||||
|
29: 0b00011111_11111111_11111111_11111111,
|
||||||
|
30: 0b00111111_11111111_11111111_11111111,
|
||||||
|
31: 0b01111111_11111111_11111111_11111111,
|
||||||
|
32: 0b11111111_11111111_11111111_11111111,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,12 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJumpDestAnalysis(t *testing.T) {
|
func TestJumpDestAnalysis(t *testing.T) {
|
||||||
|
|
@ -62,6 +64,59 @@ func TestJumpDestAnalysis(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBitVec(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Code []byte
|
||||||
|
Want bitVec
|
||||||
|
}{
|
||||||
|
{[]byte{}, bitVec{0, 0}},
|
||||||
|
{[]byte{byte(PUSH1), 0xff, 0x00, 0x00}, bitVec{0b00000000_00000000_00000000_00000010, 0}},
|
||||||
|
{[]byte{byte(PUSH2), 0xff, 0xff, 0x00}, bitVec{0b00000000_00000000_00000000_00000110, 0}},
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(PUSH2), 0xff,
|
||||||
|
0xff,
|
||||||
|
},
|
||||||
|
bitVec{0b10000000_00000000_00000000_00000000, 0b00000000_00000000_00000000_00000001, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[]byte{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, byte(PUSH32),
|
||||||
|
},
|
||||||
|
bitVec{0b00000000_00000000_00000000_00000000, 0b11111111_11111111_11111111_11111111, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
got := newCodeBitVec(test.Code)
|
||||||
|
if !slices.Equal(test.Want, got) {
|
||||||
|
t.Fatalf("(-want +got)\n- %32b\n+ %32b\n", test.Want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FuzzBitVec(f *testing.F) {
|
||||||
|
f.Add([]byte{0x1})
|
||||||
|
f.Fuzz(func(t *testing.T, code []byte) {
|
||||||
|
newBitVec := newCodeBitVec(code)
|
||||||
|
oldBitVec := codeBitmap(code)
|
||||||
|
|
||||||
|
for i := range code {
|
||||||
|
if newBitVec.isCode(uint64(i)) != oldBitVec.codeSegment(uint64(i)) {
|
||||||
|
t.Fatalf("mismatch at %d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const analysisCodeSize = 1200 * 1024
|
const analysisCodeSize = 1200 * 1024
|
||||||
|
|
||||||
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
||||||
|
|
@ -75,6 +130,7 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJumpdestAnalysis_rand(b *testing.B) {
|
func BenchmarkJumpdestAnalysis_rand(b *testing.B) {
|
||||||
|
b.Run("v=old", func(b *testing.B) {
|
||||||
code := make([]byte, analysisCodeSize)
|
code := make([]byte, analysisCodeSize)
|
||||||
rand.Read(code)
|
rand.Read(code)
|
||||||
|
|
||||||
|
|
@ -84,6 +140,19 @@ func BenchmarkJumpdestAnalysis_rand(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
codeBitmapInternal(code, bv)
|
codeBitmapInternal(code, bv)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("v=new", func(b *testing.B) {
|
||||||
|
code := make([]byte, analysisCodeSize)
|
||||||
|
rand.Read(code)
|
||||||
|
|
||||||
|
bv := newCodeBitVec(code)
|
||||||
|
b.SetBytes(int64(len(code)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bv.codeBitVec(code)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -93,12 +162,23 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkJumpdestAnalysis_weth9(b *testing.B) {
|
func BenchmarkJumpdestAnalysis_weth9(b *testing.B) {
|
||||||
|
b.Run("v=old", func(b *testing.B) {
|
||||||
bv := codeBitmap(codeWETH9)
|
bv := codeBitmap(codeWETH9)
|
||||||
b.SetBytes(int64(len(codeWETH9)))
|
b.SetBytes(int64(len(codeWETH9)))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
codeBitmapInternal(codeWETH9, bv)
|
codeBitmapInternal(codeWETH9, bv)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("v=new", func(b *testing.B) {
|
||||||
|
bv := newCodeBitVec(codeWETH9)
|
||||||
|
b.SetBytes(int64(len(codeWETH9)))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bv.codeBitVec(codeWETH9)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
||||||
|
|
@ -111,7 +191,8 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
func BenchmarkJumpdestOpAnalysis(b *testing.B) {
|
||||||
|
b.Run("v=old", func(b *testing.B) {
|
||||||
var op OpCode
|
var op OpCode
|
||||||
bencher := func(b *testing.B) {
|
bencher := func(b *testing.B) {
|
||||||
code := make([]byte, analysisCodeSize)
|
code := make([]byte, analysisCodeSize)
|
||||||
|
|
@ -129,10 +210,37 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for op = PUSH1; op <= PUSH32; op++ {
|
for op = PUSH1; op <= PUSH32; op++ {
|
||||||
bench.Run(op.String(), bencher)
|
b.Run(op.String(), bencher)
|
||||||
}
|
}
|
||||||
op = JUMPDEST
|
op = JUMPDEST
|
||||||
bench.Run(op.String(), bencher)
|
b.Run(op.String(), bencher)
|
||||||
op = STOP
|
op = STOP
|
||||||
bench.Run(op.String(), bencher)
|
b.Run(op.String(), bencher)
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("v=new", func(b *testing.B) {
|
||||||
|
bencher := func(op OpCode) (string, func(b *testing.B)) {
|
||||||
|
return op.String(), func(b *testing.B) {
|
||||||
|
code := make([]byte, analysisCodeSize)
|
||||||
|
b.SetBytes(analysisCodeSize)
|
||||||
|
|
||||||
|
for i := range code {
|
||||||
|
code[i] = byte(op)
|
||||||
|
}
|
||||||
|
bv := newCodeBitVec(code)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := range bv {
|
||||||
|
bv[j] = 0
|
||||||
|
}
|
||||||
|
bv.codeBitVec(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for op := PUSH1; op <= PUSH32; op++ {
|
||||||
|
b.Run(bencher(op))
|
||||||
|
}
|
||||||
|
b.Run(bencher(JUMPDEST))
|
||||||
|
b.Run(bencher(STOP))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue