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
|
||||
}
|
||||
|
||||
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"
|
||||
_ "embed"
|
||||
"math/bits"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
||||
|
|
@ -75,6 +130,7 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkJumpdestAnalysis_rand(b *testing.B) {
|
||||
b.Run("v=old", func(b *testing.B) {
|
||||
code := make([]byte, analysisCodeSize)
|
||||
rand.Read(code)
|
||||
|
||||
|
|
@ -84,6 +140,19 @@ func BenchmarkJumpdestAnalysis_rand(b *testing.B) {
|
|||
for i := 0; i < b.N; i++ {
|
||||
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 (
|
||||
|
|
@ -93,12 +162,23 @@ var (
|
|||
)
|
||||
|
||||
func BenchmarkJumpdestAnalysis_weth9(b *testing.B) {
|
||||
b.Run("v=old", func(b *testing.B) {
|
||||
bv := codeBitmap(codeWETH9)
|
||||
b.SetBytes(int64(len(codeWETH9)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
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) {
|
||||
|
|
@ -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
|
||||
bencher := func(b *testing.B) {
|
||||
code := make([]byte, analysisCodeSize)
|
||||
|
|
@ -129,10 +210,37 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
|||
}
|
||||
}
|
||||
for op = PUSH1; op <= PUSH32; op++ {
|
||||
bench.Run(op.String(), bencher)
|
||||
b.Run(op.String(), bencher)
|
||||
}
|
||||
op = JUMPDEST
|
||||
bench.Run(op.String(), bencher)
|
||||
b.Run(op.String(), bencher)
|
||||
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