Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modexp: 2.5x accel on small exponent #268

Merged
merged 3 commits into from
Sep 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 177 additions & 8 deletions benchmarks/bench_evm_modexp_dos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import
../constantine/math/arithmetic,
../constantine/math/io/io_bigints,
../constantine/platforms/abstractions,
./platforms, ./bench_blueprint
./bench_blueprint

proc report(op: string, elapsedNs: int64, elapsedCycles: int64, iters: int) =
let ns = elapsedNs div iters
let cycles = elapsedCycles div iters
let throughput = 1e9 / float64(ns)
when SupportsGetTicks:
echo &"{op:<45} {throughput:>15.3f} ops/s {ns:>16} ns/op {cycles:>12} CPU cycles (approx)"
echo &"{op:<70} {throughput:>15.3f} ops/s {ns:>16} ns/op {cycles:>12} CPU cycles (approx)"
else:
echo &"{op:<45} {throughput:>15.3f} ops/s {ns:>16} ns/op"
echo &"{op:<70} {throughput:>15.3f} ops/s {ns:>16} ns/op"

template bench(fnCall: untyped, ticks, ns: var int64): untyped =
block:
Expand Down Expand Up @@ -148,11 +148,119 @@ proc dos1() =
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 32,32,32", nanoseconds, ticks, execsEIP2565)
report("EVM Modexp - 32,32,32 - even base & power-of-2 modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos2() =

let input = [
# Length of base (1)
uint8 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, 0x01,

# Length of exponent (1)
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, 0x01,

# Length of modulus (121)
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, 0x79,

# Base
0x33,

# Exponent
0x01,

# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 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, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x77,
]

var r = newSeq[byte](121)
var ticks, nanoseconds: int64

let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121 - exponent=1 and odd modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos2a() =
# shortcuttable variation with even modulus

let input = [
# Length of base (1)
uint8 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, 0x01,

# Length of exponent (1)
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, 0x01,

# Length of modulus (121)
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, 0x79,

# Base
0x33,

# Exponent
0x01,

# Modulus
0x04, 0xea, 0xbb, 0x12, 0x55, 0x88, 0xd7, 0x3c, 0xad, 0x22, 0xea, 0x2b, 0x4a, 0x77, 0x6e, 0x9d,
0x4d, 0xfc, 0x13, 0xa8, 0x1b, 0xf9, 0x0c, 0x0d, 0x37, 0xe8, 0x4e, 0x8b, 0xeb, 0xb2, 0xa5, 0x48,
0x8b, 0x2c, 0x87, 0x6d, 0x13, 0x51, 0x75, 0xeb, 0x97, 0xc6, 0x13, 0xd9, 0x06, 0xce, 0x8b, 0x53,
0xd0, 0x02, 0x68, 0xb8, 0xd6, 0x12, 0xab, 0x8b, 0x15, 0x0c, 0xef, 0x0a, 0xd0, 0x3b, 0x73, 0xd2,
0xdb, 0x9d, 0x2a, 0xa5, 0x23, 0x70, 0xdc, 0x26, 0x55, 0x80, 0xca, 0xf2, 0xc0, 0x18, 0xe3, 0xe3,
0x1b, 0xad, 0xd5, 0x22, 0xdd, 0x10, 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, 0x1c, 0x05, 0x71, 0x52, 0x7c, 0x3a, 0xb0, 0x76,
]

var r = newSeq[byte](121)
var ticks, nanoseconds: int64

let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121 - exponent=1 and even modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos2b() =
# even variation with no shortcut

let input = [
# Length of base (1)
uint8 0x00,
Expand Down Expand Up @@ -201,10 +309,11 @@ proc dos2() =
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121", nanoseconds, ticks, execsEIP2565)
report("EVM Modexp - 1,1,121 - exponent=16 and odd modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos3() =
proc dos2c() =
# odd variation with no shortcut

let input = [
# Length of base (1)
Expand Down Expand Up @@ -254,11 +363,71 @@ proc dos3() =
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121", nanoseconds, ticks, execsEIP2565)
report("EVM Modexp - 1,1,121 - exponent=7 and odd modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

proc dos2d() =
# odd variation with no shortcut and power of 2 modulus

let input = [
# Length of base (1)
uint8 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, 0x01,

# Length of exponent (1)
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, 0x01,

# Length of modulus (121)
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, 0x79,

# Base
0x33,

# Exponent
0x07,

# Modulus
0x04, 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,
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, 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, 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,
]

var r = newSeq[byte](121)
var ticks, nanoseconds: int64

let (gasFeeEIP198, gasFeeEIP2565) = computeGasFee(input)
const blockSize = 30000000

let execsEIP198 = blockSize div gasFeeEIP198
let execsEIP2565 = blockSize div gasFeeEIP2565

echo "Gas cost: ", gasFeeEIP198, " gas (EIP-198) - ", execsEIP198, " executions per block"
echo "Gas cost: ", gasFeeEIP2565, " gas (EIP-2565) - ", execsEIP2565, " executions per block"

for i in 0 ..< execsEIP2565:
bench(
(let _ = r.eth_evm_modexp(input)),
ticks, nanoseconds)

report("EVM Modexp - 1,1,121 - exponent=7 and power-of-2 modulus", nanoseconds, ticks, execsEIP2565)
echo "Total time: ", nanoseconds.float64 / 1e6, " ms for ", execsEIP2565, " iterations"

dos1()
echo "\n"
dos2()
echo "\n"
dos3()
dos2a()
echo "\n"
dos2b()
echo "\n"
dos2c()
echo "\n"
dos2d()
2 changes: 1 addition & 1 deletion constantine/ethereum_evm_precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func eth_evm_ecpairing*(
r[r.len-1] = byte 1
return cttEVM_Success

func eth_evm_modexp*(r: var openArray[byte], inputs: openArray[byte]): CttEVMStatus {.noInline, tags:[Alloca, Vartime].} =
func eth_evm_modexp*(r: var openArray[byte], inputs: openArray[byte]): CttEVMStatus {.noInline, tags:[Alloca, Vartime], meter.} =
## Modular exponentiation
##
## Name: MODEXP
Expand Down
8 changes: 4 additions & 4 deletions constantine/math/elliptic/ec_scalar_mul_vartime.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ template `+=`[F; G: static Subgroup](P: var (ECP_ShortW_Jac[F, G] or ECP_ShortW_
template `-=`[F; G: static Subgroup](P: var (ECP_ShortW_Jac[F, G] or ECP_ShortW_Prj[F, G]), Q: ECP_ShortW_Aff[F, G]) =
P.msub_vartime(P, Q)

func scalarMul_doubleAdd_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime].} =
func scalarMul_doubleAdd_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime], meter.} =
## **Variable-time** Elliptic Curve Scalar Multiplication
##
## P <- [k] P
Expand Down Expand Up @@ -67,7 +67,7 @@ func scalarMul_doubleAdd_vartime*[EC](P: var EC, scalar: BigInt) {.tags:[VarTime
else:
P += Paff

func scalarMul_addchain_4bit_vartime[EC](P: var EC, scalar: BigInt) {.tags:[VarTime].} =
func scalarMul_addchain_4bit_vartime[EC](P: var EC, scalar: BigInt) {.tags:[VarTime], meter.} =
## **Variable-time** Elliptic Curve Scalar Multiplication
## This can only handle for small scalars up to 2⁴ = 16 excluded
let s = uint scalar.limbs[0]
Expand Down Expand Up @@ -206,7 +206,7 @@ func accumNAF[precompSize, NafMax: static int, EC, ECaff](
elif digit < 0:
P -= tab[-digit shr 1]

func scalarMul_minHammingWeight_windowed_vartime*[EC](P: var EC, scalar: BigInt, window: static int) {.tags:[VarTime, Alloca].} =
func scalarMul_minHammingWeight_windowed_vartime*[EC](P: var EC, scalar: BigInt, window: static int) {.tags:[VarTime, Alloca], meter.} =
## **Variable-time** Elliptic Curve Scalar Multiplication
##
## P <- [k] P
Expand Down Expand Up @@ -246,7 +246,7 @@ func scalarMul_minHammingWeight_windowed_vartime*[EC](P: var EC, scalar: BigInt,
func scalarMulEndo_minHammingWeight_windowed_vartime*[scalBits: static int; EC](
P: var EC,
scalar: BigInt[scalBits],
window: static int) {.tags:[VarTime, Alloca].} =
window: static int) {.tags:[VarTime, Alloca], meter.} =
## Endomorphism-accelerated windowed vartime scalar multiplication
##
## P <- [k] P
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func batchAffine*[N: static int, F, G](
func batchAffine*[F, G](
affs: ptr UncheckedArray[ECP_ShortW_Aff[F, G]],
jacs: ptr UncheckedArray[ECP_ShortW_Jac[F, G]],
N: int) {.noInline, tags:[Alloca].} =
N: int) {.noInline, tags:[Alloca], meter.} =
# Algorithm: Montgomery's batch inversion
# - Speeding the Pollard and Elliptic Curve Methods of Factorization
# Section 10.3.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func powOddMod_vartime*(
a: openArray[SecretWord],
exponent: openArray[byte],
M: openArray[SecretWord],
window: int) {.noInline, tags:[Alloca, VarTime].} =
window: int) {.noInline, tags:[Alloca, VarTime], meter.} =
## r <- a^exponent (mod M) with M odd
## assumes a < M
##
Expand All @@ -57,6 +57,12 @@ func powOddMod_vartime*(

let aBits = a.getBits_LE_vartime()
let mBits = M.getBits_LE_vartime()
let eBits = exponent.getBits_BE_vartime()

if eBits == 1:
r.view().reduce(a.view(), aBits, M.view(), mBits)
return

let L = wordsRequired(mBits)
let m0ninv = M[0].negInvModWord()
var rMont = allocStackArray(SecretWord, L)
Expand Down Expand Up @@ -97,7 +103,7 @@ func powMod_vartime*(
a: openArray[SecretWord],
exponent: openArray[byte],
M: openArray[SecretWord],
window: int) {.noInline, tags:[Alloca, VarTime].} =
window: int) {.noInline, tags:[Alloca, VarTime], meter.} =
## r <- a^exponent (mod M) with M odd
## assumes a < exponent
##
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func shlAddMod(a: LimbsViewMut, aLen: int,

func reduce*(r: LimbsViewMut,
a: LimbsViewAny, aBits: int,
M: LimbsViewConst, mBits: int) =
M: LimbsViewConst, mBits: int) {.meter.} =
## Reduce `a` modulo `M` and store the result in `r`
##
## The modulus `M` most-significant bit at `mBits` MUST be set.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ func prod_comba(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.noI
for i in stopEx ..< r.len:
r[i] = Zero

func prod*(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.inline.}=
func prod*(r: var openArray[SecretWord], a, b: openArray[SecretWord]) {.inline, meter.}=
## Extended precision multiplication
r.prod_comba(a, b)
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import
# Comparison
# ------------------------------------------------------------

func lt*(a, b: distinct LimbsViewAny, len: int): SecretBool =
func lt*(a, b: distinct LimbsViewAny, len: int): SecretBool {.meter.} =
## Returns true if a < b
## Comparison is constant-time
var diff: SecretWord
Expand All @@ -43,7 +43,7 @@ func lt*(a, b: distinct LimbsViewAny, len: int): SecretBool =
# Type-erased add-sub
# ------------------------------------------------------------

func cadd*(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Carry =
func cadd*(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Carry {.meter.} =
## Type-erased conditional addition
## Returns the carry
##
Expand All @@ -58,7 +58,7 @@ func cadd*(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Carry =
addC(result, sum, a[i], b[i], result)
ctl.ccopy(a[i], sum)

func csub*(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Borrow =
func csub*(a: LimbsViewMut, b: LimbsViewAny, ctl: SecretBool, len: int): Borrow {.meter.} =
## Type-erased conditional addition
## Returns the borrow
##
Expand Down
4 changes: 2 additions & 2 deletions constantine/math_arbitrary_precision/arithmetic/limbs_mod.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import
#
# ############################################################

func addmod_vartime*(r: var openArray[SecretWord], a, b, M: openArray[SecretWord]) =
func addmod_vartime*(r: var openArray[SecretWord], a, b, M: openArray[SecretWord]) {.meter.} =
## r <- a+b (mod M)
## assumes a and b are in the range [0, M)

Expand All @@ -43,6 +43,6 @@ func addmod_vartime*(r: var openArray[SecretWord], a, b, M: openArray[SecretWord
for i in 0 ..< r.len:
r[i] = t[i]

func doublemod_vartime*(r: var openArray[SecretWord], a, M: openArray[SecretWord]) {.inline.} =
func doublemod_vartime*(r: var openArray[SecretWord], a, M: openArray[SecretWord]) {.inline, meter.} =
## r <- 2a (mod M)
r.addmod_vartime(a, a, M)
Loading
Loading