Skip to content

perf: optimize jump table initialization and reuse - #6943

Open
ouy95917 wants to merge 1 commit into
tronprotocol:masterfrom
ouy95917:opt/jumptable-optimization
Open

perf: optimize jump table initialization and reuse#6943
ouy95917 wants to merge 1 commit into
tronprotocol:masterfrom
ouy95917:opt/jumptable-optimization

Conversation

@ouy95917

@ouy95917 ouy95917 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Removes unused historical version entries and eagerly initialized table mappings while retaining the existing operation-set builders.
  • Centralizes the newest operation set selection through LATEST_VERSION and newLatestOperationSet().
  • Creates dedicated instances of the latest jump table for transactions and constant calls.
  • Reuses both tables across executions.
  • Applies configuration-dependent adjustments once per top-level execution.
  • Reuses the prepared table for nested contract calls and contract creation.

Why is this needed?

The previous implementation initialized jump tables for multiple historical versions even though only the latest version was selected for execution.

Transactions and constant calls also shared the same table, which allowed adjustments made for constant calls to affect transaction execution. In addition, configuration-dependent adjustments could be applied repeatedly during nested calls and contract creation.

This change centralizes the selection of the latest operation set and separates transaction and constant-call tables. It reduces startup initialization and repeated adjustment overhead while keeping the two execution paths isolated.

Tests

  • Added coverage for transaction and constant-call table isolation.
  • Added coverage for table reuse across executions.
  • Verified that constant-call adjustments do not affect the transaction table.
  • Verified transaction, constant-call, nested-call, and contract-creation execution paths.
  • Verified existing instruction and configuration-dependent behavior.

@yanghang8612
yanghang8612 marked this pull request as ready for review September 2, 2026 08:44
if (VMConfig.allowTvmSelfdestructRestriction()) {
adjustSelfdestruct(table);
}
public static JumpTable prepareAndGetTable(boolean isConstantCall) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice change — just to confirm, are these config-based adjustments expected to stay stable once this table has been prepared?

@TonyStank911

Copy link
Copy Markdown

Just to confirm the intended lifecycle: prepareAndGetTable() is called for the top-level execution, while nested executions reuse the table that has already been prepared for that execution context. Is that correct?

return enabled.getAsBoolean();
}

public Operation adjustCost(Function<Program, Long> newCost) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These helper methods preserve the opcode, stack metadata, and enabled supplier while replacing only the cost or action. Is retaining the same enabled supplier important for keeping the activation behavior identical across the cached variants?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Each variant should differ only in the cost or action being adjusted. Reusing the same enabled supplier ensures that opcode activation behavior and the remaining operation metadata stay unchanged.

tableMap.put(Version.TRON_V1_3, newTronV13OperationSet());
tableMap.put(Version.TRON_V1_4, newTronV14OperationSet());
tableMap.put(Version.TRON_V1_5, newTronV15OperationSet());
tableMap.put(LATEST_VERSION, newLatestOperationSet());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The historical operation-set builders remain available, while only the latest set is registered in tableMap. Is this intended to preserve construction and test compatibility without initializing tables that are no longer selected at runtime?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly. The builders are retained for compatibility and targeted testing, but only the currently selected version needs a shared runtime table. This avoids initializing unused table instances.

}
VM.play(program, OperationRegistry.getTable());
// Reuse the table prepared by the top-level execution.
VM.play(program, OperationRegistry.getTable(isConstantCall()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For nested contract creation, getTable(isConstantCall()) assumes that the corresponding table was already prepared by the top-level execution. Is the execution mode inherited consistently by all nested frames?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Nested frames inherit the execution mode of the parent context, so they should reuse the table prepared for that mode instead of applying the same adjustments again.

@ouy95917

ouy95917 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

@TonyStank911 Yes. The top-level execution prepares the configuration-dependent operations, and nested executions retrieve the already prepared table. This avoids repeating the same adjustment work during internal calls.


public class OperationRegistry {

private static final Operation DEFAULT_MLOAD = new Operation(

@alan-eth alan-eth Sep 3, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, the table isolation and cached operation variants make this much cleaner. Do you have rough numbers for the startup and per-execution gains after this change? It would be nice to capture them in the PR description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants