Skip to content

Latest commit

 

History

History
126 lines (91 loc) · 4.84 KB

tip-344.md

File metadata and controls

126 lines (91 loc) · 4.84 KB
tip: 344
title: Optimize instruction execution for TVM	
author: yanghang8612@163.com
discussions to: https://github.com/tronprotocol/TIPs/issues/344
status: Final
type: Standards Track
category: VM
created: 2021-11-29

Simple Summary

The purpose of this TIP is to optimize instruction execution for TVM

Motivation

Now, the implemention of TVM in project java-tron learns from the project ethereumJ. Because the project ethereumJ is deprecated since Aug 2019, there are no updates or fixes which can be followed anymore. At the same time, execution methods of each instruction are implemented in the same source file. This implementation makes the source code difficult to read and maintain.

Optimization of the instruction execution for TVM is urgent. In order to make the optimized TVM fully equivalent to the origin, we design different levels of tests, including single tests, integration tests and syncing the whole blockchain. Meanwhile, through the pressure test comparison of the before and after optimized TVM, we have achieved a significant performance improvement of instruction execution speed.

Specification

Define Operation:

class Operation {
  int opcode;
  int requireStackSize;
  int retrunStackSize;
  Function<ProgramContext, long> energyCostFunc;
  Function<ProgramContext, void> actionFunc;
}

Define OperationEnergyCosts and OperationActions, and just split energy cost logic and execution logic into those files.

class OperationEnergyCosts {
  long getAddCost(ProgramContext context) { ... }
  ...
  long getCallCost(ProgramContext context) { ... }
  ...
}

class OperationActions {
  long getAddAction(ProgramContext context) { ... }
  ...
  long getCallAction(ProgramContext context) { ... }
  ...
}

Then operation set is initialized as follow.

Operation[] opSet = new Operation[256]
opSet[Op.ADD] = new Operation(Op.ADD, 2, 1,
    OperationEnergyCosts::getAddCost,
    OperationActions::getAddAction);
...

The instruction interpeter works as follow.

class Interpeter {

  void run(ProgramContext context) {
  
    while (!context.isStopped()) {
      Operation op = opSet[context.getOp()];
      
      // op code not activated
      if (op == null) { ... }
      
      // check require stack size (underflow)
      context.verifyRequireStackSize(op);
      
      // check ret stack size (overflow)
      context.verifyRetStackSize(op);
      
      // check memory size
      context.checkMemory(op);
      
      // consume energy
      context.spendEnergy(op.getEnergyCostFunc().get(context));
      
      // exec op action
      op.getActionFunc().exec(context)
    }
  }
}

Full Synchronization Confirmation

In order to confirm the equivalence of TVM before and after optimization, in addition to single tests and integration tests, we also designed a new synchronization mode to synchronize part of the historical blockchain. In this mode, we abandon some operations that are not related to status checking, such as storing blocks, storing transaction receipts, and verifying transaction signatures. The status check after synchronization is based on the same root of the MPT tree generated by the modification operations of TVM on the status database before and after the optimization, such as account store, storage store, etc.

Check branch is final_check.

Instruction Benchmark

We performed performance tests on the vm instructions before and after optimization. The initial set of instructions have been selected by the following criteria:

1. gas cost is constant (does not depend on instruction inputs)
2. instruction implementation is constant-time (or at least straight-forward and simple) — this eliminates instructions like DIV, MOD or ADDMOD
3. instruction does not access memory directly
4. instruction does not access state (e.g. SLOAD, EXTCODEHASH)

We have executed these basic instructions millions of times and counted the average execution time of the instructions. Through comparison, we found that the average execution time of a single instruction before optimization is 90ns, and after optimization is 70ns, the execution efficiency has increased by about 30%. We use different configuration machines the same test was performed on. Although the execution time of each machine is different, the improvement in execution efficiency is is about the same.


| instructions | count of executions | average time(ns) | average time(ns) | improve percent |
| ------------ | ------------------- | ---------------- | ---------------- | --------------- |
|    push1     |      10526456       |        90        |       70         |      28%        |

|    pop       |      12528375       |        75        |       58         |      29%        |

|    swap1     |      12193723       |        77        |       59         |      30%        |

...