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

Gas metering #2414

Merged
merged 22 commits into from
Jan 4, 2022
Merged

Gas metering #2414

merged 22 commits into from
Jan 4, 2022

Conversation

leoyvens
Copy link
Collaborator

This PR picks up on @That3Percent's work on gas metering for handlers, see #2038.

I tried this out on the Livepeer and the Uniswap subgraphs. Our target gas per nanosecond is 10, I got numbers between 1 and 1000 for that metric, which varies a lot for different calls to the same handler. The underestimation cases are the most concerning since if we underestimate too much that defeats the purpose of gas metering, but 1 gas per nanosecond is within one order of magnitude from the target so I consider it to be an acceptable underestimate. 1000 gas nanosecond per second is two orders of magnitude of error, but even those handlers didn't use more than a few billion gas, so they could be much more expensive and not hit the limit so the overestimate cases also seem acceptable. This still needs to be tried out in the integration to see if any important subgraphs hit the limit.

On the indexing performance impact, I measured each call to the gas host fn to take less than 100ns, which is fast, but that export is called tens of thousands of times on typical handlers, so it can add up to a millisecond per handler, which is a measurable but not major impact, and I don't think there is much of a way around it.

@leoyvens
Copy link
Collaborator Author

@That3Percent I did a few improvements in the last 5 commits. This is ready for review.

@leoyvens leoyvens force-pushed the zac/gas branch 3 times, most recently from 3a0fad4 to 4f57452 Compare May 13, 2021 11:39
@leoyvens leoyvens requested a review from That3Percent May 27, 2021 13:59
Copy link
Contributor

@That3Percent That3Percent left a comment

Choose a reason for hiding this comment

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

Sorry for taking so long on this review.


#[inline]
fn const_gas_size_of() -> Option<Gas> {
None
Copy link
Contributor

Choose a reason for hiding this comment

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

This could have an impl which should lead to better performance due to const propagation

Self::const_gas_size_of().expect("GasSizeOf unimplemented")
}
/// Some when every member of the type has the same gas size.
#[inline(always)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: Because this has the "implement one or the other" requirement, it would seem natural to split GasSizeOf and ConstGasSizeOf and add a blanket impl for GasSizeOf for T where T: ConstGasSizeOf. The reason this is not this way is because it would require specialization for tuples and that is not a stable feature.

pub const ZERO: Gas = Gas(0);
}

impl From<u64> for Gas {
Copy link
Contributor

Choose a reason for hiding this comment

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

I regret this impl and it's inverse (below). Seems convenient but ambiguous.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've removed them

@@ -376,6 +392,7 @@ impl WasmInstance {
let host_metrics = host_metrics.cheap_clone();
let timeout_stopwatch = timeout_stopwatch.cheap_clone();
let ctx = ctx.cheap_clone();
let gas = gas.cheap_clone();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the gas cloned here? Is this safe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

GasCounter holds an Arc internally so this is a reference being cloned.

@@ -428,6 +442,7 @@ impl WasmInstance {

for module in modules {
let func_shared_ctx = Rc::downgrade(&shared_ctx);
let gas = gas.cheap_clone();
Copy link
Contributor

Choose a reason for hiding this comment

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

Again the gas clones. Seems suspicious to me that gas might get lost possibly within a single mapping function

bytes: &Vec<u8>,
gas: &GasCounter,
) -> Result<serde_json::Value, DeterministicHostError> {
gas.consume_host_fn(gas::DEFAULT_GAS_OP.with_args(gas::complexity::Size, &bytes))?;
Copy link
Contributor

Choose a reason for hiding this comment

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

I expect this should have a reasonably expensive OP, since parsing JSON is slow and hard. Comparing to h160 parsing (below) - the latter is practically free.

Copy link
Collaborator Author

@leoyvens leoyvens Dec 8, 2021

Choose a reason for hiding this comment

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

True that the computational cost is very different, but I think the default throughput assumption of 10MB/s is pessimistic enough to be a lower bound to the performance of json parsing.

Copy link
Contributor

@That3Percent That3Percent left a comment

Choose a reason for hiding this comment

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

I'll leave further edits based on your discretion.

@azf20
Copy link
Contributor

azf20 commented Sep 2, 2021

@leoyvens is this the gas costing work - are we planning to merge this in?

@leoyvens
Copy link
Collaborator Author

leoyvens commented Sep 2, 2021

Yes, we plan to merge this, I just need to find a moment to rebase and deploy it.

@enzojavier-desimone
Copy link

Could be realated to #2112 (comment)

How is possible to set a experimental feature flag when running the graph?

@leoyvens
Copy link
Collaborator Author

leoyvens commented Mar 9, 2022

The env var is GRAPH_ALLOW_NON_DETERMINISTIC_IPFS.

@kasparkallas
Copy link

kasparkallas commented Apr 25, 2022

Hi, @leoyvens! Thanks for your work on the awesome project!

Sorry if this is a silly question, but... Do I understand correctly that the "gas metering" measures how much resources a Subgraph mapper is using? So when there's a infinite loop in a mapper then it's going to quickly run into these limitations, right?

If so, I'm wondering whether the gas metering is done per block or per event?

@leoyvens
Copy link
Collaborator Author

Hi @kasparkallas, you understand correctly. The limit is per trigger (such as an EVM event).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants