Skip to content
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: cpu and store gas updates #3054

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

Conversation

piux2
Copy link
Contributor

@piux2 piux2 commented Oct 31, 2024

Summary:

This PR updates the CPU and Store gas based on results from the benchmarking tool:#2241

For CPU gas, the measurement is in nanoseconds per opcode execution.
For storage gas, the measurement is in nanoseconds per byte for each type of Gno store access.

Changes:

We moved the gas meter from the underlying store to the upper Gno store to capture accurate resource consumption for VM transactions. At the same time, we retain the original gas store and gas meter for the Auth Keeper to handle regular blockchain transactions that do not necessarily involve the VM.

We also updated the gas-wanted in the integration test to reflect actual gas usage. This can serve as a flag to alert us to future changes that might increase gas assumptions.

Additional reasons for these changes include:

  • The Gno VM store processes additional complex encoding and decoding of data structures, rather than simply reading and writing bytes to disk.
  • For the above reason, we benchmarked gas for store access at the Gno store level.
  • We want to avoid consuming gas at two points for a single store access during a VM transaction.

Here are the diagrams to explain the store access gas before and after changes

Before:

image

After:

image

Contributors' checklist...
  • Added new tests
  • Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory
  • Updated the official documentation or not needed
  • No breaking changes were made, or a BREAKING CHANGE: xxx message was included in the description
  • Added references to related issues and PRs
  • Provided any useful hints for running manual tests

@github-actions github-actions bot added 📦 🤖 gnovm Issues or PRs gnovm related 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related labels Oct 31, 2024
gno.land/pkg/sdk/vm/gas_test.go Outdated Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

can we just make the txtars all just use a -gas-wanted of 10M?

just because they'll have to be updated again. I'd want for us to keep track of "golden" gas values just in one place.

Copy link
Contributor Author

@piux2 piux2 Dec 6, 2024

Choose a reason for hiding this comment

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

I understand that we need to update for gas changes. This provides an opportunity to verify the changes and understand where the gas impact comes from, especially if there is a significant increase in gas usage in our implementation. We can add a 50% buffer to the gas usage as a safeguard. I am also open to other solutions.

@@ -1154,127 +1154,130 @@ func (m *Machine) incrCPU(cycles int64) {
}

const (
// CPU cycles
Copy link
Member

Choose a reason for hiding this comment

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

Can we add a reference to the benchmarks and methodology?

Copy link
Member

Choose a reason for hiding this comment

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

Yes please 🙏

I'm a bit worried about the new gas values, and a comparison would help us make a fair assessment of the gas bumps

Copy link
Member

Choose a reason for hiding this comment

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

I'm concerned that this approach renders ugnot nearly valueless, as it results in excessively long and difficult-to-read numbers.

We should either adopt a strategy that sacrifices some precision, such as dividing by N (which could be a parameter), or consider introducing another level of gnot conversion, like ngnot for gas, to maintain the relevance of ugnot.

Copy link
Contributor Author

@piux2 piux2 Dec 6, 2024

Choose a reason for hiding this comment

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

@moul @zivkovicmilos I totally agree; the high gas fees create a poor user experience. Let me think about how we can address this later. I think we could maintain precision in the program and round it up at the UI layer.

@@ -205,6 +205,7 @@ var gnoStoreContextKey gnoStoreContextKeyType
func (vm *VMKeeper) newGnoTransactionStore(ctx sdk.Context) gno.TransactionStore {
base := ctx.Store(vm.baseKey)
iavl := ctx.Store(vm.iavlKey)
vm.gnoStore.SetGasMeter(ctx.GasMeter())
Copy link
Member

Choose a reason for hiding this comment

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

This is unsafe, because vm.gnoStore is shared, and this would set the gas meter globally; ie. not scoped to the transaction.

Copy link
Member

Choose a reason for hiding this comment

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

It sounds reasonable to switch to start with per transaction and later move to per machine if we also have machine concurrency.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@thehowl good catch! I will pass the gasMeter to gnoStore.BeginTransaction() instead

Comment on lines 762 to 764
func (ds *defaultStore) SetGasMeter(gm store.GasMeter) {
ds.gasMeter = gm
}
Copy link
Member

Choose a reason for hiding this comment

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

I think this ought to go in BeginTransaction, and the store shouldn't use a gas store otherwise.

Copy link
Member

Choose a reason for hiding this comment

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

It makes sense to count gas for the store independently since future features will dissociate CPU gas from storage gas, allowing costs to cover cpuCost + storageCost + futureStorageRent. Additionally, realms could have different storage cost ratios—some sponsored by the chain, others transitioned to 'archives,' or even storage classes with varying configurations based on price tiers.

Copy link
Contributor Author

@piux2 piux2 Dec 6, 2024

Choose a reason for hiding this comment

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

I think this ought to go in BeginTransaction, and the store shouldn't use a gas store otherwise.

@thehowl Agreed, I will assign the gasMeter in BeginTransaction and remove this method.

Comment on lines +115 to +125
func DefaultGasConfig() GasConfig {
return GasConfig{
GasGetObject: 16, // per byte cost
GasSetObject: 16, // per byte cost
GasGetType: 52, // per byte cost
GasSetType: 52, // per byte cost
GasGetPackageRealm: 524, // per byte cost
GasSetPackageRealm: 524, // per byte cost
GasAddMemPackage: 8, // per byte cost
GasGetMemPackage: 8, // per byte cost
GasDeleteObject: 3715, // flat cost
}
}
Copy link
Member

Choose a reason for hiding this comment

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

These are constants for the VM. Can these also not be constants?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is better to keep the gas constant until we implement other ways to update the value, such as through genesis or GovDAO with the parameter module. For now, whenever we change the value, we should modify this section of the code and go through code reviews again.

Comment on lines +178 to +181
func (c Context) Store(key store.StoreKey) store.Store {
return c.MultiStore().GetStore(key)
}

Copy link
Member

Choose a reason for hiding this comment

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

I can understand the rename to GasStore, but I don't think we should expose a gas-less Store method, especially when getting the underlying store is still trivial and exported.

Copy link
Member

@moul moul Nov 25, 2024

Choose a reason for hiding this comment

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

Suggested change
func (c Context) Store(key store.StoreKey) store.Store {
return c.MultiStore().GetStore(key)
}
func (c Context) GaslessStore(key store.StoreKey) store.Store {
return c.MultiStore().GetStore(key)
}

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can understand the rename to GasStore, but I don't think we should expose a gas-less Store method, especially when getting the underlying store is still trivial and exported.

The purpose here is not to rename the store but to separate gas store access from non-gas store access in the context. This separation is necessary because we need to wrap the gas meter at different levels for Auth Keeper gas consumption and VM gas consumption.

Please refer to the diagram above. In Auth Keeper, the GasStore wraps the gas meter at the underlying base store level. In VM Keeper, the gas meter is wrapped above the underlying store in the gnoStore, allowing us to capture activity to persist realm objects. The ctx.Store is used by the VM Keeper to access the underlying store without the gas meter, so it can wrap it at a higher layer.

The context.Store abstracts away the underlying MultiStore concept. This approach is consistent with how context, app, and keeper store access are managed.

@Kouteki Kouteki added the in focus Core team is prioritizing this work label Nov 1, 2024
@Kouteki Kouteki added this to the 🚀 Mainnet launch milestone Nov 11, 2024
@Kouteki Kouteki mentioned this pull request Nov 19, 2024
Copy link
Member

@zivkovicmilos zivkovicmilos left a comment

Choose a reason for hiding this comment

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

I think the PR looks fine, apart from the comments left by @thehowl, which should be addressed about the global gas meter setting in the VM keeper 🙏

I'm temporarily just putting a pause on the PR, to allow for us to discuss whether these new gas values make sense, since they are a huge change. I also want to see some more reasoning and gas comparisons, apart from #2241

If we're fine with the bumps, I am also cool with the PR moving forward as is ✅

cc @moul @notJoon

@@ -1154,127 +1154,130 @@ func (m *Machine) incrCPU(cycles int64) {
}

const (
// CPU cycles
Copy link
Member

Choose a reason for hiding this comment

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

Yes please 🙏

I'm a bit worried about the new gas values, and a comparison would help us make a fair assessment of the gas bumps

@Kouteki Kouteki requested a review from moul November 20, 2024 11:37
Copy link
Member

@moul moul left a comment

Choose a reason for hiding this comment

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

Overall, it looks good. In this PR or a future one, I suggest we:

  • Add a global multiplier/divider parameter to avoid using ngnot while keeping the numbers relatively readable.
  • Create a benchmarking tool that runs all the gnocode in the repository and generates a CSV or TXT file with the gas details we commit periodically (there's no need for this on every commit, in my opinion). This way, whenever we want to fine-tune a parameter, we can run the same script again and compare the results.

Copy link
Member

@zivkovicmilos zivkovicmilos left a comment

Choose a reason for hiding this comment

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

Unblocking after @moul is fine with the bumps

@Gno2D2
Copy link
Collaborator

Gno2D2 commented Dec 6, 2024

I'm a bot that assists the Gno Core team in maintaining this repository. My role is to ensure that contributors understand and follow our guidelines, helping to streamline the development process.

The following requirements must be fulfilled before a pull request can be merged.
Some requirement checks are automated and can be verified by the CI, while others need manual verification by a staff member.

These requirements are defined in this configuration file.

Automated Checks

🟢 Maintainers must be able to edit this pull request (more info)
🟢 The pull request head branch must be up-to-date with its base (more info)

Manual Checks

No manual checks match this pull request.

Debug
Automated Checks
Maintainers must be able to edit this pull request (more info)

If

🟢 Condition met
└── 🟢 On every pull request

Then

🟢 Requirement satisfied
└── 🟢 Maintainer can modify this pull request

The pull request head branch must be up-to-date with its base (more info)

If

🟢 Condition met
└── 🟢 On every pull request

Then

🟢 Requirement satisfied
└── 🟢 Head branch (piux2:feat_gas_ops) is up to date with base (master): behind by 0 / ahead by 5

Copy link

codecov bot commented Dec 6, 2024

Codecov Report

Attention: Patch coverage is 80.32787% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
gno.land/pkg/keyscli/addpkg.go 0.00% 4 Missing and 2 partials ⚠️
gnovm/pkg/gnolang/store.go 87.50% 4 Missing and 1 partial ⚠️
tm2/pkg/sdk/auth/keeper.go 80.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in focus Core team is prioritizing this work 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related
Projects
Status: In Progress
Status: In Review
Development

Successfully merging this pull request may close these issues.

6 participants