-
Notifications
You must be signed in to change notification settings - Fork 382
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
fix: consolidate vm gas consumption #1430
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1430 +/- ##
==========================================
+ Coverage 46.25% 46.27% +0.02%
==========================================
Files 483 483
Lines 68837 68851 +14
==========================================
+ Hits 31838 31861 +23
+ Misses 34368 34357 -11
- Partials 2631 2633 +2 ☔ View full report in Codecov by Sentry. |
A very minimal change for such a big improvement, that is impressive! |
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 anteHandler will be super helpful, thanks for taking the time to knock all this out @piux2. Really amazing work.
To fill you in on where we're at for the mechanics (would love your feedback/thoughts here, maybe a meeting soon before sims start):
Currently it seems like we'll largely be mirroring EIP 1559. So as you probably already know in that case... we'd have a target block size, actual block size, max amount gas can change per block, and real block size threshold. If the threshold is 200%, then when the actual block size is 200% larger than the target then gas would increase by the max amount, 12.5% in ETH's case.
From there we can feed the resulting gas value into a final fee equation that also takes into account CPU cycles and/or bytes required for a specific transaction. To account for the resources needed we'd need to assign a fixed value to each byte stored and each CPU cycle to multiply by the required amounts. CPU cycles would be hardware specific though, right?
I also still like the idea of a logistic or expo curve that kicks in after a certain level, as a circuit breaker spam protection method.
At the same time the resource parameters present a game-able situation where people can create less efficient/optimized code to earn additional fees. But considering these submissions will often undergo manual review early on and even with us planning to give a % of all fees earned to the realm's builder, we should have some counter measures to play with.
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.
Handful of small nitpicks, nothing major
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.
Just left a couple comments on style and consolidating code, but the overall approach seems good to me. Great job 🎉
Co-authored-by: Thomas Bruyelle <[email protected]>
Co-authored-by: Thomas Bruyelle <[email protected]>
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.
Me & Ray went through the PR to discuss changes and optimisations. From Ray's benchmarking, it seems that a considerable amount of the lost optimisations are due to the overflow
checks which are now in place in the gas
package.
To address the benchmarking issues, I see two future improvements which can be made:
- Changing
gnolang/overflow
to use the OF/CF flags instead, checking for overflow directly on hardware rather than with a software solution. This is tracked in gnolang/overflow: optimise package for common architectures #1844. - Eventually moving to having one single system to track resource consumption, rather than the double system of Gas meter + MaxCycles. (For now, it makes sense to keep the latter as a "dumb fallback".)
Another thing to note, is that the keeper
code is painfully repetitive, but it's not this PR's fault. Some refactor should aim to try to make it not repeat itself, while trying not to harm readability.
After resolving outstanding comments, I'd say good to merge.
still reviewing plz hold off on merging just yet. |
Are we sure about this? Another example of something tricky... struct fields that are later in field order take longer to access. (this is actually true, or was true when I was first developing GNO). If a struct field is used often, such as the gas meter, it is better to declare it higher, rather than how it is how, where it is among the last of fields. Another fact is that interface methods take much longer to call than concrete implementation methods. We probably want to use the tm2.GasMeter interface, but read the next point: If it really is the call to incrCPU that is slow, there is something else we can do... we can batch increment CPUs... e.g. run 10, 20, or 100 GNO OpCode operations, accumulate the CPU cycles used, and then call incrCPU just once with the accumulated figure. We just need to remember to call incrCPU at the end of Machine.Run() before the batch call to incrCPU happens again for any remainder CPU cycles. But I would only do this once we are absolutely certain that it is the call to gasMeter that is slow. We could implement a concrete BatchGasMeter and have the Machine hold a reference to *BatchGasMeter rather than an interface, and BatchGasMeter could "flush" the batch to the underlying tm2.GasMeter every 10/20/100 runs; this way the more expensive call to an interface can be amortized. And then we can call BatchGasMeter.Flush() to flush any remainders to the underlying tm2.GasMeter upon completion of Machine.Run. <-- probably will help. But how much would this actually help in performance? |
The phrase 'a considerable amount of' is based solely on limited benchmarking of the incrCPU test. The overflow is accounted for 27% of the gas consumption logic in incrCPU. The benchmarking method and results discussed here focus on a Gno contract, emphasizing nested for-range loops and two-dimensional slice access, without considering I/O or any other factors. More information can be found here: https://github.com/gnolang/gno/blob/master/gnovm/pkg/gnolang/benchdata/matrix.gno After conducting several more rounds of benchmark tests using the same method, there was a 7% increase observed, instead of the previously mentioned 20%. All of this increase can be attributed to the incrCPU() function. Of this 7%, 27% is from overflow checking, and the remainder is from the gas consumption function.
All of these are good points for us to consider during performance optimizations. How about we tackle performance by prioritizing IO > VM > GasMeter after we complete the basic functionalities and fixing critical issues? |
Contributors' checklist...
Ref: #1070 #1067 #649 #1281
Summary
The current gno.land node, optimized for development purposes, has a simplified verification process and gas meter implementation. To transition the gno.land node to a production-ready state, it is necessary to implement a comprehensive gas metering system that accurately accounts for VM gas consumption. This includes refining the gas fee structure to encompass all relevant costs, ensuring robust transaction validation, and calculating gas consumption based on actual computational load.
This PR aims to address these limitations by introducing a complete gas meter and validation flow, laying the groundwork for further gas meter profiling and configuration.
Problem Definition
Current State and Limitations for Production:
VM Gas Consumption Not Accounted in Gas Meter: The current gas meter fails to calculate VM gas consumption, potentially allowing heavy contract loads without corresponding gas meter deductions. A refined system should measure and charge for VM gas usage accurately.
Gas Fee Structure: Presently, the gas fee structure only includes storage access, transaction size, and signature verification. VM gas fees are levied as a separate, flat fee, which might lead to confusion among users expecting the total fee to match the amount specified in the 'gas-fee' argument. For improved transparency and precision, the gas fee structure should integrate all these aspects.
Transaction Validation: The system currently validates basic information for VM msg_addpkg and msg_call. However, gas consumption cannot be determined before fully executing these messages against the VM. Consequently, VM transactions are placed in the mempool and propagated to other nodes, even if they may not meet the gas fee requirements to execute these transactions. This undermines the purpose of using gas fees to prevent VM spamming.
Solution: ( Updated )
This is a high-level description of the implemented features:
Added an anteHandler in VM to monitor gas consumptionImplemented chained VM anteHandler in auth.anteHandlerTrade-offs and Future Optimization: ( Updated )
The current implementation processes messages against the VM to check gas consumption in abci.CheckTx() before inclusion in the mempool and propagation to other nodes.Messages lacking sufficient gas-wanted will be dropped, preventing abuse without adequate gas fees. However, the trade-off is that for each message with enough gas, the VM executes the transaction twice: once in CheckTx() and once in DeliverTx(). As these occur in separate execution contexts and are not in synchronized sequence, the performance impact is currently a secondary concern.We moved the VM gas check from CheckTx to DeliverTx for the following reasons:
By moving the VM gas check from CheckTx to DeliverTx, we are able to reduce the load on the mempool of a node and allow transactions to propagate through the network faster.
In the future, we may use a predicted median value instead of the exact value from transaction execution for efficiency.
What's Next: