Skip to content

Commit

Permalink
optimize Exp for even bases (#189)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Martin Holst Swende <[email protected]>
  • Loading branch information
Valeh2012 and holiman authored Dec 6, 2024
1 parent 439fbd4 commit a17fcfb
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 54 deletions.
88 changes: 39 additions & 49 deletions benchmarks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,10 +572,7 @@ func bigExp(result, base, exponent *big.Int) *big.Int {
return result
}

func benchmark_Exp_Big(bench *testing.B) {
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"

func benchmark_Exp_Big(bench *testing.B, x, y string) {
orig := big.NewInt(0).SetBytes(hex2Bytes(x))
base := big.NewInt(0).SetBytes(hex2Bytes(x))
exp := big.NewInt(0).SetBytes(hex2Bytes(y))
Expand All @@ -587,62 +584,55 @@ func benchmark_Exp_Big(bench *testing.B) {
base.Set(orig)
}
}
func benchmark_Exp_Bit(bench *testing.B) {
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"

base := big.NewInt(0).SetBytes(hex2Bytes(x))
exp := big.NewInt(0).SetBytes(hex2Bytes(y))

f_base, _ := FromBig(base)
f_orig, _ := FromBig(base)
f_exp, _ := FromBig(exp)
f_res := Int{}
func benchmark_Exp_U256(bench *testing.B, x, y string) {
var (
base = big.NewInt(0).SetBytes(hex2Bytes(x))
exp = big.NewInt(0).SetBytes(hex2Bytes(y))

f_base, _ = FromBig(base)
f_orig, _ = FromBig(base)
f_exp, _ = FromBig(exp)
f_res Int
)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
f_res.Exp(f_base, f_exp)
f_base.Set(f_orig)
}
}
func benchmark_ExpSmall_Big(bench *testing.B) {
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
y := "8abcdef"

orig := big.NewInt(0).SetBytes(hex2Bytes(x))
base := big.NewInt(0).SetBytes(hex2Bytes(x))
exp := big.NewInt(0).SetBytes(hex2Bytes(y))

result := new(big.Int)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
bigExp(result, base, exp)
base.Set(orig)
func BenchmarkExp(bench *testing.B) {
{ // Large values
base := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
exp := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
bench.Run("large/big", func(b *testing.B) {
benchmark_Exp_Big(b, base, exp)
})
bench.Run("large/uint256", func(b *testing.B) {
benchmark_Exp_U256(b, base, exp)
})
}
}
func benchmark_ExpSmall_Bit(bench *testing.B) {
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
y := "8abcdef"

base := big.NewInt(0).SetBytes(hex2Bytes(x))
exp := big.NewInt(0).SetBytes(hex2Bytes(y))

f_base, _ := FromBig(base)
f_orig, _ := FromBig(base)
f_exp, _ := FromBig(exp)
f_res := Int{}

bench.ResetTimer()
for i := 0; i < bench.N; i++ {
f_res.Exp(f_base, f_exp)
f_base.Set(f_orig)
{ // Smaller exponent
base := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
exp := "8abcdef"
bench.Run("small/big", func(b *testing.B) {
benchmark_Exp_Big(b, base, exp)
})
bench.Run("small/uint256", func(b *testing.B) {
benchmark_Exp_U256(b, base, exp)
})
}
{ // Even base (and very small exponent)
base := "ABCDEF090807060504030201fffffffffffffffffffffffffffffffffffffffe"
exp := "ff"
bench.Run("even/big", func(b *testing.B) {
benchmark_Exp_Big(b, base, exp)
})
bench.Run("even/uint256", func(b *testing.B) {
benchmark_Exp_U256(b, base, exp)
})
}
}
func BenchmarkExp(bench *testing.B) {
bench.Run("large/big", benchmark_Exp_Big)
bench.Run("large/uint256", benchmark_Exp_Bit)
bench.Run("small/big", benchmark_ExpSmall_Big)
bench.Run("small/uint256", benchmark_ExpSmall_Bit)
}

func BenchmarkDiv(b *testing.B) {
Expand Down
19 changes: 14 additions & 5 deletions uint256.go
Original file line number Diff line number Diff line change
Expand Up @@ -1199,19 +1199,28 @@ func (z *Int) Byte(n *Int) *Int {

// Exp sets z = base**exponent mod 2**256, and returns z.
func (z *Int) Exp(base, exponent *Int) *Int {
res := Int{1, 0, 0, 0}
multiplier := *base
expBitLen := exponent.BitLen()
var (
res = Int{1, 0, 0, 0}
multiplier = *base
expBitLen = exponent.BitLen()
curBit = 0
word = exponent[0]
even = base[0]&1 == 0
)
if even && expBitLen > 8 {
return z.Clear()
}

curBit := 0
word := exponent[0]
for ; curBit < expBitLen && curBit < 64; curBit++ {
if word&1 == 1 {
res.Mul(&res, &multiplier)
}
multiplier.squared()
word >>= 1
}
if even { // If the base was even, we are finished now
return z.Set(&res)
}

word = exponent[1]
for ; curBit < expBitLen && curBit < 128; curBit++ {
Expand Down

0 comments on commit a17fcfb

Please sign in to comment.