Skip to content

Commit

Permalink
Inline gas cost/instruction fetching
Browse files Browse the repository at this point in the history
These make up 5:ish % of EVM execution time - even though they're
trivial they end up not being inlined - this little change gives a
practically free perf boost ;)

Also unify the style of creating the output to `setLen`..
  • Loading branch information
arnetheduck committed Nov 22, 2024
1 parent a241050 commit 8937302
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 40 deletions.
19 changes: 12 additions & 7 deletions nimbus/evm/code_stream.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,19 @@ func readVmWord*(c: var CodeStream, n: static int): UInt256 =
func len*(c: CodeStream): int =
len(c.code)

func next*(c: var CodeStream): Op {.inline.} =
# The extra >= 0 check helps eliminate `IndexDefect` from the optimized code
# which keeps this hotspot in the EVM small, code-size-wise
if c.pc >= 0 and c.pc < c.code.len:
result = Op(c.code.bytes[c.pc])
inc c.pc
template next*(c: var CodeStream): Op =
# Retrieve the next opcode (or stop) - this is a hot spot in the interpreter
# and must be kept small for performance
let
# uint: range checked manually -> benefit from smaller codegen
pc = uint(c.pc)
bytes {.cursor.} = c.code.bytes
if pc < uint(bytes.len):
let op = Op(bytes[pc])
c.pc = cast[int](pc + 1)
op
else:
result = Op.Stop
Op.Stop

iterator items*(c: var CodeStream): Op =
var nextOpcode = c.next()
Expand Down
11 changes: 6 additions & 5 deletions nimbus/evm/computation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ func traceError*(c: Computation) =
func prepareTracer*(c: Computation) =
c.vmState.capturePrepare(c, c.msg.depth)

func opcodeGasCost*(
template opcodeGasCost*(
c: Computation, op: Op, gasCost: static GasInt, tracingEnabled: static bool,
reason: static string): EvmResultVoid {.inline.} =
reason: static string): EvmResultVoid =
# Special case of the opcodeGasCost function used for fixed-gas opcodes - since
# the parameters are known at compile time, we inline and specialize it
when tracingEnabled:
Expand All @@ -465,16 +465,17 @@ func opcodeGasCost*(
c.msg.depth + 1)
c.gasMeter.consumeGas(gasCost, reason)

func opcodeGasCost*(
template opcodeGasCost*(
c: Computation, op: Op, gasCost: GasInt, reason: static string): EvmResultVoid =
let cost = gasCost
if c.vmState.tracingEnabled:
c.vmState.captureGasCost(
c,
op,
gasCost,
cost,
c.gasMeter.gasRemaining,
c.msg.depth + 1)
c.gasMeter.consumeGas(gasCost, reason)
c.gasMeter.consumeGas(cost, reason)

# ------------------------------------------------------------------------------
# End
Expand Down
11 changes: 6 additions & 5 deletions nimbus/evm/interpreter/gas_meter.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ func init*(m: var GasMeter, startGas: GasInt) =
m.gasRemaining = startGas
m.gasRefunded = 0

func consumeGas*(
gasMeter: var GasMeter; amount: GasInt; reason: static string): EvmResultVoid {.inline.} =
template consumeGas*(
gasMeter: var GasMeter; amount: GasInt; reason: static string): EvmResultVoid =
# consumeGas is a hotspot in the vm due to it being called for every
# instruction
# TODO report reason - consumeGas is a hotspot in EVM execution so it has to
# be done carefully
if amount > gasMeter.gasRemaining:
return err(gasErr(OutOfGas))
gasMeter.gasRemaining -= amount
ok()
EvmResultVoid.err(gasErr(OutOfGas))
else:
gasMeter.gasRemaining -= amount
EvmResultVoid.ok()

func returnGas*(gasMeter: var GasMeter; amount: GasInt) =
gasMeter.gasRemaining += amount
Expand Down
43 changes: 20 additions & 23 deletions nimbus/evm/precompiles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,18 @@ func bn256ecAdd(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =

var
input: array[128, byte]
output: array[64, byte]
# Padding data
let len = min(c.msg.data.len, 128) - 1
input[0..len] = c.msg.data[0..len]
var p1 = ? G1.getPoint(input.toOpenArray(0, 63))
var p2 = ? G1.getPoint(input.toOpenArray(64, 127))
var apo = (p1 + p2).toAffine()

c.output.setLen(64)
if isSome(apo):
# we can discard here because we supply proper buffer
discard apo.get().toBytes(output)
discard apo.get().toBytes(c.output)

assign(c.output, output)
ok()

func bn256ecMul(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
Expand All @@ -321,19 +321,19 @@ func bn256ecMul(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =

var
input: array[96, byte]
output: array[64, byte]

# Padding data
let len = min(c.msg.data.len, 96) - 1
input[0..len] = c.msg.data[0..len]
var p1 = ? G1.getPoint(input.toOpenArray(0, 63))
var fr = ? getFR(input.toOpenArray(64, 95))
var apo = (p1 * fr).toAffine()

c.output.setLen(64)
if isSome(apo):
# we can discard here because we supply buffer of proper size
discard apo.get().toBytes(output)
discard apo.get().toBytes(c.output)

assign(c.output, output)
ok()

func bn256ecPairing(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid =
Expand All @@ -348,10 +348,10 @@ func bn256ecPairing(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid
GasECPairingBaseIstanbul + numPoints * GasECPairingPerPointIstanbul
? c.gasMeter.consumeGas(gasFee, reason="ecPairing Precompile")

var output: array[32, byte]
c.output.setLen(32)
if msglen == 0:
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytes(output)
discard BNU256.one().toBytes(c.output)
else:
# Calculate number of pairing pairs
let count = msglen div 192
Expand All @@ -369,9 +369,8 @@ func bn256ecPairing(c: Computation, fork: EVMFork = FkByzantium): EvmResultVoid

if acc == FQ12.one():
# we can discard here because we supply buffer of proper size
discard BNU256.one().toBytes(output)
discard BNU256.one().toBytes(c.output)

assign(c.output, output)
ok()

func blake2bf(c: Computation): EvmResultVoid =
Expand All @@ -382,11 +381,9 @@ func blake2bf(c: Computation): EvmResultVoid =
let gasFee = GasInt(beLoad32(input, 0))
? c.gasMeter.consumeGas(gasFee, reason="blake2bf Precompile")

var output: array[64, byte]
if not blake2b_F(input, output):
c.output.setLen(64)
if not blake2b_F(input, c.output):
return err(prcErr(PrcInvalidParam))
else:
assign(c.output, output)
ok()

func blsG1Add(c: Computation): EvmResultVoid =
Expand All @@ -407,7 +404,7 @@ func blsG1Add(c: Computation): EvmResultVoid =

a.add b

c.output = newSeq[byte](128)
c.output.setLen(128)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand All @@ -431,7 +428,7 @@ func blsG1Mul(c: Computation): EvmResultVoid =

a.mul(scalar)

c.output = newSeq[byte](128)
c.output.setLen(128)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand Down Expand Up @@ -504,7 +501,7 @@ func blsG1MultiExp(c: Computation): EvmResultVoid =
else:
acc.add(p)

c.output = newSeq[byte](128)
c.output.setLen(128)
if not encodePoint(acc, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand All @@ -527,7 +524,7 @@ func blsG2Add(c: Computation): EvmResultVoid =

a.add b

c.output = newSeq[byte](256)
c.output.setLen(256)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand All @@ -551,7 +548,7 @@ func blsG2Mul(c: Computation): EvmResultVoid =

a.mul(scalar)

c.output = newSeq[byte](256)
c.output.setLen(256)
if not encodePoint(a, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand Down Expand Up @@ -593,7 +590,7 @@ func blsG2MultiExp(c: Computation): EvmResultVoid =
else:
acc.add(p)

c.output = newSeq[byte](256)
c.output.setLen(256)
if not encodePoint(acc, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand Down Expand Up @@ -643,7 +640,7 @@ func blsPairing(c: Computation): EvmResultVoid =
else:
acc.mul(millerLoop(g1, g2))

c.output = newSeq[byte](32)
c.output.setLen(32)
if acc.check():
c.output[^1] = 1.byte
ok()
Expand All @@ -663,7 +660,7 @@ func blsMapG1(c: Computation): EvmResultVoid =

let p = fe.mapFPToG1()

c.output = newSeq[byte](128)
c.output.setLen(128)
if not encodePoint(p, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand All @@ -683,7 +680,7 @@ func blsMapG2(c: Computation): EvmResultVoid =

let p = fe.mapFPToG2()

c.output = newSeq[byte](256)
c.output.setLen(256)
if not encodePoint(p, c.output):
return err(prcErr(PrcInvalidPoint))
ok()
Expand Down

0 comments on commit 8937302

Please sign in to comment.