-
Notifications
You must be signed in to change notification settings - Fork 25
perf(evm): optimize EVMC execute entry path with address-based module cache and instance reuse #366
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ | |
| #include "intx/intx.hpp" | ||
|
|
||
| #include <array> | ||
| #include <deque> | ||
| #include <vector> | ||
|
|
||
| namespace zen { | ||
|
|
@@ -70,7 +69,7 @@ struct EVMFrame { | |
| class InterpreterExecContext { | ||
| private: | ||
| runtime::EVMInstance *Inst; | ||
| std::deque<EVMFrame> FrameStack; | ||
| std::vector<EVMFrame> FrameStack; | ||
| evmc_status_code Status = EVMC_SUCCESS; | ||
| std::vector<uint8_t> ReturnData; | ||
| evmc::Result ExeResult; | ||
|
|
@@ -80,6 +79,17 @@ class InterpreterExecContext { | |
|
|
||
| InterpreterExecContext(runtime::EVMInstance *Inst) : Inst(Inst) {} | ||
|
|
||
| /// Reset state for reuse across calls. Keeps allocated capacity to avoid | ||
| /// re-allocating the ~32KB EVMFrame on every call. | ||
| void resetForNewCall(runtime::EVMInstance *NewInst) { | ||
| Inst = NewInst; | ||
| FrameStack.clear(); // keeps vector capacity | ||
| Status = EVMC_SUCCESS; | ||
| ReturnData.clear(); // keeps vector capacity | ||
| IsJump = false; | ||
| ExeResult = evmc::Result{EVMC_SUCCESS, 0, 0}; | ||
| } | ||
|
Comment on lines
+82
to
+91
|
||
|
|
||
| EVMFrame *allocTopFrame(evmc_message *Msg); | ||
| void freeBackFrame(); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -101,6 +101,10 @@ class EVMInstance final : public RuntimeObject<EVMInstance> { | |||||||||||
| void setError(const Error &E) { Err = E; } | ||||||||||||
| void clearError() { Err = ErrorCode::NoError; } | ||||||||||||
|
|
||||||||||||
| /// Reset instance state for reuse in interpreter mode. | ||||||||||||
| /// Avoids the cost of destroy + recreate on every EVMC execute() call. | ||||||||||||
|
Comment on lines
+104
to
+105
|
||||||||||||
| /// Reset instance state for reuse in interpreter mode. | |
| /// Avoids the cost of destroy + recreate on every EVMC execute() call. | |
| /// Reset instance state for reuse across EVMC execute() calls, regardless | |
| /// of execution mode (interpreter, multipass, or JIT). This avoids the cost | |
| /// of destroying and recreating the instance for each call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing FrameStack from std::deque to std::vector can invalidate pointers/references to existing frames on growth (reallocation). This is unsafe here because allocTopFrame() stores pointers to Frame.Msg in EVMInstance::MessageStack and interpreter code keeps EVMFrame* (e.g., parent frame) across nested calls; a reallocation would turn those pointers into dangling pointers and can cause memory corruption. Use a container with stable addresses (e.g., keep std::deque), or reserve a hard upper bound up-front and enforce max depth so FrameStack never reallocates, or store frames/messages in separately allocated stable storage.