mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
remove gmp_pool
This commit is contained in:
parent
c521fa6bdb
commit
7fc1617ac1
4 changed files with 1 additions and 355 deletions
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/modexp/bigint"
|
||||
gmpcwrapper "github.com/ethereum/go-ethereum/crypto/modexp/gmp/cwrapper"
|
||||
gmpgeneric "github.com/ethereum/go-ethereum/crypto/modexp/gmp/generic"
|
||||
gmppool "github.com/ethereum/go-ethereum/crypto/modexp/gmp/pool"
|
||||
)
|
||||
|
||||
// generateWorstCase generates a byte array with all bits set to 1
|
||||
|
|
@ -47,7 +46,6 @@ func BenchmarkComprehensive(b *testing.B) {
|
|||
{"BigInt", bigint.ModExp},
|
||||
{"GMPGeneric", gmpgeneric.ModExp},
|
||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||
{"GMPPooled", gmppool.ModExp},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
@ -100,7 +98,6 @@ func BenchmarkWorstCaseOnly(b *testing.B) {
|
|||
{"BigInt", bigint.ModExp},
|
||||
{"GMPGeneric", gmpgeneric.ModExp},
|
||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||
{"GMPPooled", gmppool.ModExp},
|
||||
}
|
||||
|
||||
b.Run("AllFF", func(b *testing.B) {
|
||||
|
|
@ -147,7 +144,6 @@ func BenchmarkMemoryProfile(b *testing.B) {
|
|||
{"BigInt", bigint.ModExp},
|
||||
{"GMPGeneric", gmpgeneric.ModExp},
|
||||
{"GMPCWrapper", gmpcwrapper.ModExp},
|
||||
{"GMPPooled", gmppool.ModExp},
|
||||
}
|
||||
|
||||
for _, size := range sizes {
|
||||
|
|
@ -230,12 +226,6 @@ func BenchmarkComparison(b *testing.B) {
|
|||
_, _ = gmpcwrapper.ModExp(data.base, data.exp, data.mod)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("GMPPooled", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = gmppool.ModExp(data.base, data.exp, data.mod)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ You need to have GMP development libraries installed on your system:
|
|||
|
||||
This package provides a GMP-backed implementation for modular exponentiation. The API can be expanded, however right now, the main usage is for the modexp precompile.
|
||||
|
||||
There are currently two APIs. This is because its unclear if a direct API is better or using a syncPool with a more flexible API is better.
|
||||
There are currently two implementations: generic (using Go wrapper types) and cwrapper (direct C calls).
|
||||
|
||||
### Byte Array Interface (Recommended)
|
||||
|
||||
|
|
@ -55,18 +55,6 @@ result.ExpMod(base, exp, mod)
|
|||
fmt.Printf("Result: %s\n", result)
|
||||
```
|
||||
|
||||
### Pooled Interface (High Performance)
|
||||
|
||||
```go
|
||||
// Create a pool once
|
||||
pool := gmp.NewIntPool()
|
||||
|
||||
// Use it for many operations
|
||||
for i := 0; i < 1000000; i++ {
|
||||
result := gmp.ExpModPooled(pool, base, exp, mod)
|
||||
// Process result...
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
package gmp
|
||||
|
||||
// #include <gmp.h>
|
||||
import "C"
|
||||
import (
|
||||
"sync"
|
||||
generic "github.com/ethereum/go-ethereum/crypto/modexp/gmp/generic"
|
||||
)
|
||||
|
||||
// IntPool provides a pool of reusable GMP Int objects to reduce allocations
|
||||
type IntPool struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
// NewIntPool creates a new pool for GMP Int objects
|
||||
func NewIntPool() *IntPool {
|
||||
return &IntPool{
|
||||
pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return generic.NewInt()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves an Int from the pool
|
||||
func (p *IntPool) Get() *generic.Int {
|
||||
return p.pool.Get().(*generic.Int)
|
||||
}
|
||||
|
||||
// Put returns an Int to the pool after clearing it
|
||||
func (p *IntPool) Put(i *generic.Int) {
|
||||
// Clear the Int to avoid keeping large numbers in memory
|
||||
i.SetUint64(0)
|
||||
p.pool.Put(i)
|
||||
}
|
||||
|
||||
// ExpModPooled performs modular exponentiation using pooled Int objects
|
||||
// This is useful for high-throughput scenarios where you want to minimize allocations
|
||||
// This function matches the behavior of the EVM modexp precompile
|
||||
func ExpModPooled(pool *IntPool, base, exp, mod []byte) []byte {
|
||||
// Handle empty modulus - return empty result (EVM behavior)
|
||||
if len(mod) == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
// Get Ints from pool
|
||||
baseInt := pool.Get()
|
||||
expInt := pool.Get()
|
||||
modInt := pool.Get()
|
||||
resultInt := pool.Get()
|
||||
|
||||
// Ensure we return Ints to pool when done
|
||||
defer func() {
|
||||
pool.Put(baseInt)
|
||||
pool.Put(expInt)
|
||||
pool.Put(modInt)
|
||||
pool.Put(resultInt)
|
||||
}()
|
||||
|
||||
// Set values
|
||||
baseInt.SetBytes(base)
|
||||
expInt.SetBytes(exp)
|
||||
modInt.SetBytes(mod)
|
||||
|
||||
// Check for zero modulus
|
||||
if modInt.BitLen() == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
// Special case: base has bit length 1 (base == 1)
|
||||
if baseInt.BitLen() == 1 {
|
||||
// Just return base % mod
|
||||
resultInt.Mod(baseInt, modInt)
|
||||
} else {
|
||||
// Normal case: perform modular exponentiation
|
||||
resultInt.ExpMod(baseInt, expInt, modInt)
|
||||
}
|
||||
|
||||
// Get result bytes (this allocates, but much less than creating new Ints)
|
||||
return resultInt.Bytes()
|
||||
}
|
||||
|
||||
// ModExp performs modular exponentiation using a new pool for each operation
|
||||
// For better performance, create a pool once and reuse it with ExpModPooled
|
||||
func ModExp(base, exp, mod []byte) ([]byte, error) {
|
||||
pool := NewIntPool()
|
||||
result := ExpModPooled(pool, base, exp, mod)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PreallocatedExpMod performs modular exponentiation with pre-allocated Int objects
|
||||
// This gives the caller full control over object lifecycle
|
||||
func PreallocatedExpMod(result, base, exp, mod *generic.Int) {
|
||||
result.ExpMod(base, exp, mod)
|
||||
}
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
package gmp
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestIntPool tests basic pool functionality
|
||||
func TestIntPool(t *testing.T) {
|
||||
pool := NewIntPool()
|
||||
|
||||
// Get an Int from pool
|
||||
i1 := pool.Get()
|
||||
i1.SetString("12345", 10)
|
||||
|
||||
// Return it to pool
|
||||
pool.Put(i1)
|
||||
|
||||
// Get another Int (should be the same one, cleared)
|
||||
i2 := pool.Get()
|
||||
if i2.String() != "0" {
|
||||
t.Errorf("Expected cleared Int, got %s", i2.String())
|
||||
}
|
||||
|
||||
// Verify it's the same object
|
||||
i2.SetString("67890", 10)
|
||||
pool.Put(i2)
|
||||
|
||||
i3 := pool.Get()
|
||||
// Should be cleared again
|
||||
if i3.String() != "0" {
|
||||
t.Errorf("Expected cleared Int after second put, got %s", i3.String())
|
||||
}
|
||||
}
|
||||
|
||||
// TestExpModPooled tests pooled modular exponentiation
|
||||
func TestExpModPooled(t *testing.T) {
|
||||
pool := NewIntPool()
|
||||
|
||||
base := []byte{0x02} // 2
|
||||
exp := []byte{0x0A} // 10
|
||||
mod := []byte{0x03, 0xE8} // 1000
|
||||
|
||||
result := ExpModPooled(pool, base, exp, mod)
|
||||
|
||||
// 2^10 mod 1000 = 1024 mod 1000 = 24
|
||||
expected := []byte{0x18} // 24
|
||||
if !bytesEqual(result, expected) {
|
||||
t.Errorf("ExpModPooled: expected %x, got %x", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestModExp tests the convenience function
|
||||
func TestModExp(t *testing.T) {
|
||||
base := []byte{0x02}
|
||||
exp := []byte{0x0A}
|
||||
mod := []byte{0x03, 0xE8}
|
||||
|
||||
result, err := ModExp(base, exp, mod)
|
||||
if err != nil {
|
||||
t.Fatalf("ModExp failed: %v", err)
|
||||
}
|
||||
|
||||
expected := []byte{0x18}
|
||||
if !bytesEqual(result, expected) {
|
||||
t.Errorf("ModExp: expected %x, got %x", expected, result)
|
||||
}
|
||||
|
||||
// Test empty modulus case
|
||||
result, err = ModExp(base, exp, []byte{})
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error for empty modulus, got %v", err)
|
||||
}
|
||||
if len(result) != 0 {
|
||||
t.Errorf("Expected empty result for empty modulus, got %x", result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TestPreallocatedExpMod tests pre-allocated operations
|
||||
func TestPreallocatedExpMod(t *testing.T) {
|
||||
base := NewInt()
|
||||
exp := NewInt()
|
||||
mod := NewInt()
|
||||
result := NewInt()
|
||||
|
||||
base.SetString("2", 10)
|
||||
exp.SetString("10", 10)
|
||||
mod.SetString("1000", 10)
|
||||
|
||||
PreallocatedExpMod(result, base, exp, mod)
|
||||
|
||||
if result.String() != "24" {
|
||||
t.Errorf("PreallocatedExpMod: expected 24, got %s", result.String())
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkExpModPooled compares pooled vs non-pooled performance
|
||||
func BenchmarkExpModPooled(b *testing.B) {
|
||||
pool := NewIntPool()
|
||||
base := []byte("123456789012345678901234567890")
|
||||
exp := []byte("987654321098765432109876543210")
|
||||
mod := []byte("111111111111111111111111111111")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ExpModPooled(pool, base, exp, mod)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkExpModNonPooled benchmarks non-pooled operations for comparison
|
||||
func BenchmarkExpModNonPooled(b *testing.B) {
|
||||
base := []byte("123456789012345678901234567890")
|
||||
exp := []byte("987654321098765432109876543210")
|
||||
mod := []byte("111111111111111111111111111111")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
baseInt := NewInt()
|
||||
expInt := NewInt()
|
||||
modInt := NewInt()
|
||||
resultInt := NewInt()
|
||||
|
||||
baseInt.SetBytes(base)
|
||||
expInt.SetBytes(exp)
|
||||
modInt.SetBytes(mod)
|
||||
resultInt.ExpMod(baseInt, expInt, modInt)
|
||||
_ = resultInt.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TestPoolConcurrency tests that the pool is safe for concurrent use
|
||||
func TestPoolConcurrency(t *testing.T) {
|
||||
pool := NewIntPool()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Run many concurrent operations
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func(n int) {
|
||||
defer wg.Done()
|
||||
|
||||
// Get and use an Int
|
||||
num := pool.Get()
|
||||
num.SetString("12345", 10)
|
||||
|
||||
// Simulate some work
|
||||
result := NewInt()
|
||||
result.ExpMod(num, num, num)
|
||||
|
||||
// Return to pool
|
||||
pool.Put(num)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// TestPoolWithBigNumbers tests pool with large numbers
|
||||
func TestPoolWithBigNumbers(t *testing.T) {
|
||||
pool := NewIntPool()
|
||||
|
||||
// Test with 2048-bit numbers
|
||||
base := make([]byte, 256)
|
||||
exp := make([]byte, 256)
|
||||
mod := make([]byte, 256)
|
||||
|
||||
// Fill with test data
|
||||
for i := range base {
|
||||
base[i] = byte(i)
|
||||
exp[i] = byte(255 - i)
|
||||
mod[i] = 0xFF
|
||||
}
|
||||
mod[0] = 0x7F // Make sure modulus is not too large
|
||||
|
||||
// This should not panic or leak memory
|
||||
for i := 0; i < 10; i++ {
|
||||
result := ExpModPooled(pool, base, exp, mod)
|
||||
if len(result) == 0 {
|
||||
t.Error("Expected non-empty result")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPoolComparison verifies pooled operations match non-pooled
|
||||
func TestPoolComparison(t *testing.T) {
|
||||
pool := NewIntPool()
|
||||
|
||||
testCases := []struct {
|
||||
base string
|
||||
exp string
|
||||
mod string
|
||||
}{
|
||||
{"2", "10", "1000"},
|
||||
{"123456789", "987654321", "1000000007"},
|
||||
{"999999999999", "888888888888", "777777777777"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
// Non-pooled
|
||||
base1 := NewInt()
|
||||
exp1 := NewInt()
|
||||
mod1 := NewInt()
|
||||
result1 := NewInt()
|
||||
|
||||
base1.SetString(tc.base, 10)
|
||||
exp1.SetString(tc.exp, 10)
|
||||
mod1.SetString(tc.mod, 10)
|
||||
result1.ExpMod(base1, exp1, mod1)
|
||||
|
||||
// Pooled
|
||||
result2 := ExpModPooled(pool, base1.Bytes(), exp1.Bytes(), mod1.Bytes())
|
||||
|
||||
// Compare
|
||||
if !bytesEqual(result1.Bytes(), result2) {
|
||||
t.Errorf("Pooled vs non-pooled mismatch for %s^%s mod %s", tc.base, tc.exp, tc.mod)
|
||||
}
|
||||
|
||||
// Also compare with math/big
|
||||
bigBase := new(big.Int)
|
||||
bigExp := new(big.Int)
|
||||
bigMod := new(big.Int)
|
||||
bigResult := new(big.Int)
|
||||
|
||||
bigBase.SetString(tc.base, 10)
|
||||
bigExp.SetString(tc.exp, 10)
|
||||
bigMod.SetString(tc.mod, 10)
|
||||
bigResult.Exp(bigBase, bigExp, bigMod)
|
||||
|
||||
if !bytesEqual(bigResult.Bytes(), result2) {
|
||||
t.Errorf("Pooled vs math/big mismatch for %s^%s mod %s", tc.base, tc.exp, tc.mod)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue