-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
feat: VM Execution Lanes #10551
feat: VM Execution Lanes #10551
Conversation
chain/vm/execution.go
Outdated
|
||
const ( | ||
DefaultAvailableExecutionLanes = 4 | ||
DefaultPriorityExecutionLanes = 2 |
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.
As we discussed synchronously, we need to justify this number by looking at the critical path usages in the chain sync subsystem. From my understanding, those are the following, but @magik6k will probably have better ideas.
- Incoming block validation via gossip
- Head coalescer executions.
- Actual deferred execution.
Also note that time-sensitive miner gas estimations and mpool pushes will fall under the "user" section. I'm not sure how much of a problem that could be, probably not much since those daemons won't be facing arbitrary user workloads. But worth confirming.
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.
I haven't reviewed this whole file yet. Pending.
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.
Broadly looks like it should work, some notes.
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.
Looks good, just some minor comments
// DefaultPriorityExecutionLanes is the number of reserved execution lanes for priority computations. | ||
// This is purely userspace, but we believe it is a reasonable default, even with more available | ||
// lanes. | ||
DefaultPriorityExecutionLanes = 2 |
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.
Can we add a comment somewhere that DefaultAvailableExecutionLanes
include the DefaultPriorityExecutionLanes
so they are not added together (unless I am somehow terribly mistaken).
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.
ok, this should not be confusing.
reserving := 0 | ||
if e.reserved > 0 { | ||
e.reserved-- | ||
reserving = 1 | ||
} |
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.
So I understand, is the executionToken.reserved
mainly used to handle the case where we have a ExecutionLanePriority where all the priority lanes are used/full but there are free default ones?
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.
exactly, it is the spill mechanism.
Co-authored-by: Aayush Rajasekaran <arajasek94@gmail.com>
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.
LGTM, but let's let @fridrik01 also approve.
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.
LGTM also
Doing this entirely in lotus is looking pretty gnarly. Someone is going to forget to call Please don't merge this yet, I think there's a better way and I'll spend a few minutes trying to flesh it out. |
Ok, so:
An alternative approach is to handle this at the
Unlike the current approach, this doesn't require any deferred calls to some "done" method. Really, this can be entirely handled with a flag in the context passed to In the future, this can be refined further: Instead of always reserving a couple of lanes, |
calling Done multiple times is idempotent, so that part is safe. |
Your proposal would use a similar mechanism (ie tokens for execution lanes). The advantage it has is that the lock is more finegrained and that the Done call is limited in scope at ApplyMessage. |
I am not opposed to doing it that way, but the benefit really is marginal imo. |
Also, we dont need the context option at all, we can do the locking at the ApplyMessage shim and have the priority come from vmopts as it currently does. In short, we can hide the whole getToken/putToken/Done business inside the ApplyMessage shim of the executor, and it's not the end of the world if you forget to call Done outside. That's a change I am more willing to implement. |
See #10590 for implementation of the finegrained lock. Honestly, I don't think perf will improve (it will likely be slightly worse for ApplyBlocks as we'll have to take the lock a thousand times), but not having to call Done is a win safety-wise. |
VM Execution Lanes Part II: Hide the lock
Overview
This pr implements effective execution lanes for the fvm, supporting a default and a priority execution lane.
The priority execution lane reserves a number of execution units so that there is always some capacity for it even if default execution load spikes, typically through user load.
Implementation Notes
LOTUS_FVM_CONCURRENCY
(also used by the fvm itself to reserve execution units) andLOTUS_FVM_CONCURRENCY_RESERVED
env vars.ApplyBlocks
execution is prioritized, as it is critical for sync/consensus.