Skip to content
197 changes: 157 additions & 40 deletions dcrec/secp256k1/field_bench_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020-2024 The Decred developers
// Copyright (c) 2020-2026 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -25,93 +25,195 @@ func BenchmarkFieldNormalize(b *testing.B) {
}
}

// BenchmarkFieldSqrt benchmarks calculating the square root of an unsigned
// 256-bit big-endian integer modulo the field prime with the specialized type.
func BenchmarkFieldSqrt(b *testing.B) {
// BenchmarkBigIntNegateModP benchmarks calculating the additive inverse of an
// unsigned 256-bit big-endian integer modulo the field prime with stdlib big
// integers.
func BenchmarkBigIntNegateModP(b *testing.B) {
v1Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := new(big.Int).Neg(v1)
result.Mod(result, curveParams.P)

}
}

// BenchmarkFieldNegate benchmarks calculating the additive inverse of an
// unsigned 256-bit big-endian integer modulo the field prime with [FieldVal].
func BenchmarkFieldNegate(b *testing.B) {
// The function is constant time so any value is fine.
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f := new(FieldVal).SetHex(valHex).Normalize()
f := new(FieldVal).SetHex(valHex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var result FieldVal
_ = result.SquareRootVal(f)
_ = result.NegateVal(f, 1)
}
}

// BenchmarkBigSqrt benchmarks calculating the square root of an unsigned
// 256-bit big-endian integer modulo the field prime with stdlib big integers.
func BenchmarkBigSqrt(b *testing.B) {
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
val, ok := new(big.Int).SetString(valHex, 16)
if !ok {
b.Fatalf("failed to parse hex %s", valHex)
// BenchmarkBigIntAddModP benchmarks adding two unsigned 256-bit big-endian
// integers modulo the field prime with stdlib big integers.
func BenchmarkBigIntAddModP(b *testing.B) {
v1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"
v2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)
v2 := fromHex(v2Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := new(big.Int).Add(v1, v2)
result.Mod(result, curveParams.N)
}
}

// BenchmarkFieldAdd benchmarks adding two unsigned 256-bit big-endian integers
// modulo the field prime with [FieldVal].
func BenchmarkFieldAdd(b *testing.B) {
// The function is constant time so any values are fine.
f1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"
f2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f1 := new(FieldVal).SetHex(f1Hex)
f2 := new(FieldVal).SetHex(f2Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = new(big.Int).ModSqrt(val, curveParams.P)
var sum FieldVal
sum.Add2(f1, f2)
}
}

// BenchmarkFieldInverse calculating the multiplicative inverse of an unsigned
// 256-bit big-endian integer modulo the field prime with the specialized type.
func BenchmarkFieldInverse(b *testing.B) {
// BenchmarkBigIntMulModP benchmarks multiplying two unsigned 256-bit big-endian
// integers modulo the field prime with stdlib big integers.
func BenchmarkBigIntMulModP(b *testing.B) {
v1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"
v2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)
v2 := fromHex(v2Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := new(big.Int).Mul(v1, v2)
result.Mod(result, curveParams.P)
}
}

// BenchmarkFieldMul benchmarks multiplying two unsigned 256-bit big-endian
// integers modulo the field prime with [FieldVal].
func BenchmarkFieldMul(b *testing.B) {
// The function is constant time so any values are fine.
f1Hex := "d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"
f2Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f1 := new(FieldVal).SetHex(f1Hex)
f2 := new(FieldVal).SetHex(f2Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var prod FieldVal
prod.Mul2(f1, f2)
}
}

// BenchmarkBigIntSqrtModP benchmarks calculating the square root of an unsigned
// 256-bit big-endian integer modulo the field prime with stdlib big integers.
func BenchmarkBigIntSqrtModP(b *testing.B) {
v1Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = new(big.Int).ModSqrt(v1, curveParams.P)
}
}

// BenchmarkFieldSqrt benchmarks calculating the square root of an unsigned
// 256-bit big-endian integer modulo the field prime with [FieldVal].
func BenchmarkFieldSqrt(b *testing.B) {
// The function is constant time so any value is fine.
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f := new(FieldVal).SetHex(valHex).Normalize()

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
f.Inverse()
var result FieldVal
_ = result.SquareRootVal(f)
}
}

// BenchmarkBigInverse benchmarks calculating the multiplicative inverse of an
// unsigned 256-bit big-endian integer modulo the field prime with stdlib big
// integers.
func BenchmarkBigInverse(b *testing.B) {
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
val, ok := new(big.Int).SetString(valHex, 16)
if !ok {
b.Fatalf("failed to parse hex %s", valHex)
// BenchmarkBigIntSquareModP benchmarks squaring an unsigned 256-bit big-endian
// integer modulo the field prime with stdlib big integers.
func BenchmarkBigIntSquareModP(b *testing.B) {
v1Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
result := new(big.Int).Mul(v1, v1)
result.Mod(result, curveParams.P)
}
}

// BenchmarkFieldSquare benchmarks squaring a 256-bit big-endian integer modulo
// the field prime with [FieldVal].
func BenchmarkFieldSquare(b *testing.B) {
// The function is constant time so any values are fine.
fHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f := new(FieldVal).SetHex(fHex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = new(big.Int).ModInverse(val, curveParams.P)
var sq FieldVal
sq.SquareVal(f)
}
}

// BenchmarkFieldIsGtOrEqPrimeMinusOrder benchmarks determining whether a value
// is greater than or equal to the field prime minus the group order with the
// specialized type.
func BenchmarkFieldIsGtOrEqPrimeMinusOrder(b *testing.B) {
// BenchmarkBigIntInverseModP benchmarks calculating the multiplicative inverse
// of an unsigned 256-bit big-endian integer modulo the field prime with stdlib
// big integers.
func BenchmarkBigIntInverseModP(b *testing.B) {
v1Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = new(big.Int).ModInverse(v1, curveParams.P)
}
}

// BenchmarkFieldInverse calculating the multiplicative inverse of an unsigned
// 256-bit big-endian integer modulo the field prime with [FieldVal].
func BenchmarkFieldInverse(b *testing.B) {
// The function is constant time so any value is fine.
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f := new(FieldVal).SetHex(valHex).Normalize()

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = f.IsGtOrEqPrimeMinusOrder()
f.Inverse()
}
}

// BenchmarkBigIsGtOrEqPrimeMinusOrder benchmarks determining whether a value
// BenchmarkBigIntIsGtOrEqPrimeMinusOrder benchmarks determining whether a value
// is greater than or equal to the field prime minus the group order with stdlib
// big integers.
func BenchmarkBigIsGtOrEqPrimeMinusOrder(b *testing.B) {
func BenchmarkBigIntIsGtOrEqPrimeMinusOrder(b *testing.B) {
// Same value used in field val version.
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
val, ok := new(big.Int).SetString(valHex, 16)
if !ok {
b.Fatalf("failed to parse hex %s", valHex)
}
v1Hex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
v1 := fromHex(v1Hex)
bigPMinusN := new(big.Int).Sub(curveParams.P, curveParams.N)

b.ReportAllocs()
Expand All @@ -120,6 +222,21 @@ func BenchmarkBigIsGtOrEqPrimeMinusOrder(b *testing.B) {
// In practice, the internal value to compare would have to be converted
// to a big integer from bytes, so it's a fair comparison to allocate a
// new big int here and set all bytes.
_ = new(big.Int).SetBytes(val.Bytes()).Cmp(bigPMinusN) >= 0
_ = new(big.Int).SetBytes(v1.Bytes()).Cmp(bigPMinusN) >= 0
}
}

// BenchmarkFieldIsGtOrEqPrimeMinusOrder benchmarks determining whether a value
// is greater than or equal to the field prime minus the group order with the
// specialized type.
func BenchmarkFieldIsGtOrEqPrimeMinusOrder(b *testing.B) {
// The function is constant time so any value is fine.
valHex := "16fb970147a9acc73654d4be233cc48b875ce20a2122d24f073d29bd28805aca"
f := new(FieldVal).SetHex(valHex).Normalize()

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = f.IsGtOrEqPrimeMinusOrder()
}
}