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
IL Virtual Machine #3888
base: master
Are you sure you want to change the base?
IL Virtual Machine #3888
Conversation
⚡ |
Great benchmark results! |
It should not as there's a jump so assume it's safe.
Definitely! I was thinking about the same to extract the ASM and print it in the output. This will require using https://github.com/microsoft/clrmd probably, which requires the author to load it in their head again 😅 I'll provide this print soon. |
@Ruteri Please take a look at the description. I added the bottom section that shows the
It was not easy as I self attach and do put iced on top of it, but it should be more or less valid. Let me know what you see in there. I could push forward to even map ByteCode -> IL -> addresses, but this would be an exercise that probably would not bring a lot as there's like 95% of opcodes that are still missing in this VM |
History rewritten, to allow interacting with VM. |
An update before taking a break from this PR. After amending the way the tests are run the gains are much less bold that claimed before. The current way the ILVM is integrated is the call within the current implementation of
The multiplier than fell down from initial 100x to 15x but now, it's embedded in the existing VM like it'd be if this was fully implemented. |
👀 |
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.
Hello. My name is WillQ. And I am one of the participant in ethereum protocol fellow program.
I chose the IL-VM project to work on and @LukaszRozmej guided me to this pr. FYI, I am a noob in C# and dotnet. I am trying to comprehend what this pr is doing and see what I can do for.
I got some questions on the pr especially for IL-VM part.I hope I can get some help here.
And I also wrote a benchmark for this pr in my own branch https://github.com/zsluedem/nethermind/blob/il-vm/src/Nethermind/Nethermind.Evm.ILBenchmark/Program.cs .
Here is my benchmark result.
Method | Bytecode | Mean | Error | StdDev |
---|---|---|---|---|
ILEvm | 5850 | 196.2 us | 3.74 us | 3.49 us |
Evm | 5850 | 192.2 us | 1.57 us | 1.39 us |
ILEvm | 6000600157 | 194.0 us | 1.85 us | 1.73 us |
Evm | 6000600157 | 194.1 us | 2.05 us | 1.81 us |
ILEvm | 600156 | 178.3 us | 1.68 us | 1.49 us |
Evm | 600156 | 215.5 us | 1.59 us | 1.33 us |
ILEvm | 6001600157 | 176.2 us | 2.69 us | 2.39 us |
Evm | 6001600157 | 218.8 us | 2.73 us | 2.55 us |
ILEvm | 60016(...)30303 [22] | 193.0 us | 1.81 us | 1.51 us |
Evm | 60016(...)30303 [22] | 199.6 us | 0.74 us | 0.66 us |
ILEvm | 60016005575B | 190.9 us | 2.33 us | 2.06 us |
Evm | 60016005575B | 195.3 us | 2.36 us | 1.97 us |
ILEvm | 6001800350 | 193.6 us | 2.20 us | 1.84 us |
Evm | 6001800350 | 196.6 us | 1.93 us | 1.71 us |
ILEvm | 600260019003 | 190.8 us | 1.95 us | 1.73 us |
Evm | 600260019003 | 196.7 us | 1.16 us | 1.03 us |
ILEvm | 6003565B | 191.4 us | 2.53 us | 2.24 us |
Evm | 6003565B | 196.6 us | 1.50 us | 1.33 us |
ILEvm | 63000(...)55750 [30] | 6,532.3 us | 39.32 us | 34.85 us |
Evm | 63000(...)55750 [30] | 11,069.8 us | 112.30 us | 99.56 us |
I got a little bit different result compared to your testcase which haven't been warmed up. The 63000(...)55750 [30]
case is the same as your loop testcase. I hope this data could help.
if (isIL) | ||
{ | ||
// differentiate by adding one point | ||
code = code.Op(Instruction.POP); |
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.
Why is this difference needed?
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 don't remember.
const int wordToAlignTo = 32; | ||
|
||
il.Emit(OpCodes.Ldc_I4, EvmStack.MaxStackSize * Word.Size + wordToAlignTo); | ||
il.Emit(OpCodes.Localloc); |
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.
The doc of Localloc
says
Allocates a certain number of bytes from the local dynamic memory pool and pushes
the address (a transient pointer, type *) of the first allocated byte onto the
evaluation stack.
Does this Localloc
opcode would load the locals define above like uint256A
into memory pool ?
And what is the first allocated byte from the docs?Is it current
?
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.
Localalloc is used here to allocate the whole EVM stack on the actual stack.
// 4. set the field | ||
// 5. advance pointer | ||
case Instruction.PUSH1: | ||
il.Load(current); |
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 could not really understand how stack works in msil. Could you expand this with more knowledges?
I feel like everything is operating on this current
local.
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.
Yes, the current
here is the top of the evm stack. If PUSH1
is executed, the plan for it is flushed out above between lines 122-127. Load the value, zero it, set 1, advance.
Hey, nice to meet you 😃 This PR requires fair deep understanding of IL, .NET runtime and C#. Not sure if this is the best way to start with .NET 😅 . One remark that I need to start with is that I flushed it 6 months ago and did not revisit from this moment. My context of it atm is high level, and it might require me to spend more time on recalling specifics. Also, currently I cannot support implementing it fully or dive deep into specifics. Still, will do my best to provide you with some answers.
In regards to the benchmarks, I can see that you call the following one
in
The initial tests were focused on longer executions, so that the rest of the infrastructure should just work. It was a mad idea and I was pushing it further looking if it breaks. Maybe you found a breaking point, but before comparing numbers, I'd check the rest. For short ones, the numbers should be comparable I believe. This is the best that I can share atm @zsluedem I hope it helps a bit. |
Implements #4672
This PR proposes an introduction of another
IVirtualMachine
implementation based on transpilingEVM
bytecode toIL
.IL
, orMSIL
, is an intermediate language that .NET (the runtime that Nethermind uses) languages compile to, to be later JITted to assembly by the runtime. In this PR, instead of having a loop and executing instructions on the pc basis it emits the whole contract as a single method.Tentative Plan of Future Actions
The plan of action, that is frequently updated and reorganized:
ClrMD
and ASM print for the methodWord*
to offset based (start + int as the current)Currently supported
POP
PC
PUSH1
,PUSH2
,PUSH4
DUP1
SWAP1
SUB
JUMPDEST
JUMP
- this is a full blown two layer jump table, first based on the switch with fanout 128, second layer with simple ifsJUMPI
- uses the same jump table as above + branch-free condition pop from the stackOutOfGas
when it happensAhead of ILVM
SHA3
)JUMPDEST
, a global jump table at the end of the function. If the destination for the jump is known at static time (PUSHN
followed byJUMP
), this can go directly to the label without any checkCALL
s and discussing how to interop with other contractsPotential
Potential usages:
Benchmarks
The following code was used for a terribly simple benchmark. It represents a simple loop that performs multiple spins
The comparison with a long enough run that should amortize all the const costs is as follows:
🔥 This means that in this terrible benchmark ILVM is 100x faster than the existing one!
Benchmarks ASM output
The bytecode JITted away, according to my knowledge of JIT, and ASM and addressing, results in the following code