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

[Stress-06] Price Finalize Opcodes Based On Operand Types #2281

Merged
merged 22 commits into from
Jan 28, 2024

Conversation

iamalwaysuncomfortable
Copy link
Contributor

@iamalwaysuncomfortable iamalwaysuncomfortable commented Jan 8, 2024

Prelude

This PR presents initial suggestions for revised finalize pricing. This is in no way final or authoritative, but rather a basis for community discussion on how pricing of operations in finalize scopes should work.

Motivation

All opcodes currently have a fixed pricing regardless of inputs. For opcodes that encapsulate simple operations, this is
acceptable. However, for opcodes that encapsulate complex computations such as cryptographic hashes or mapping reads & writes, runtime is significant and affected non-trivially by the size of the opcode's operands.

This motivates pricing opcodes based on the size of their operands so that network performance is not impacted by inefficient or malicious use of complex opcodes.

Analysis

To quantify the runtime costs of Aleo opcodes, a benchmark analysis of each opcode was performed on hardware with
recommended validator specifications (Intel Xeon Platinum 8175 - 128CPU - 128GB RAM)

The analysis can be found here (Credit: @miazn - @iamalwaysuncomfortable)

Results

Key results from the benchmark analysis are summarized below.

Simple Opcodes

Most opcodes have runtimes under 10000 nanoseconds, with most in the range of 1000-3000 nanoseconds.

Outliers include:

  • mul(field,field) ~ 100000 ns
  • inv ~ 10000 ns
  • div(field,field) ~ 15000ns
  • squareroot ~ 25000ns
  • pow(field) ~ 15000ns

Hashes & Commits

  • Each hash family has a base runtime which increases in a (mostly) linearly fashion with input size.
  • BHP & Poseidon hash families have the highest runtime per byte. The remaining hash families have parity in runtime
    per byte.

Set/Get/Contain

  • Sets are the slowest, having a similar runtime per byte as BHP hashes.
  • Get & Contain are faster, having a similar runtime per byte.

Initial Pricing Conclusions

1. Pricing should allow finalize scopes to be affordable to use

One of the core motivations of this PR is to discourage overuse of high runtime opcodes in finalize scopes to ensure
network performance and stability through pricing these opcodes per byte.

The other core motivation is to price opcodes fairly so that effective use of this scope for useful applications is
affordable for the foreseeable future. This motivates pricing computationally cheap opcodes at an affordable flat rate.

In pricing both "simple" and "complex" opcodes, this PR suggests that 1 second of runtime in a finalize scope should
cost roughly ~100 credits.

2. Flat Pricing for Simple Opcodes

Runtimes for simple opcodes are low enough to justify grouping these opcodes together and pricing them at a flat low
rate. The rate in this PR is proposed to be 500 microcredits per opcode as an average pricing.

Outliers

Suggested pricing per byte for outliers mentioned above:

  • mul(field,field) ~ 10000 microcredits
  • inv ~ 1000 microcredits
  • div(field,field) ~ 1500 microcredits
  • squareroot ~ 2500 microcredits
  • pow(field) ~ 1500 microcredits

3. Byte based pricing for complex opcodes (cast, commit, hash, set/get/contain)

For Casts, Commits, Hashes and GET/SET/CONTAIN operations, pricing should be based on the number of bytes in the operands. Since each opcode has a base cost each should follow an equation of base_cost + cost_per_byte * num_bytes.

6 cost tiers are proposed based on benchmark analysis:

  1. BASE Hash Tier: 10,000 + 30*input_bytes (i.e. 0.8 microcredits for BASE hashes over 8192 bytes)
  2. BPH Hash Tier: 50,000 + 300*input_bytes (i.e. 16 microcredits for BHP hashes over 8192 bytes)
  3. PSD Hash Tier: 40,000 + 75*input_bytes (i.e. 4 microcredits for PSD hashes over 8192 bytes)
  4. CAST Tier 500 + 30*input_bytes (i.e. ~0.25 microcredits for CASTs over 8192 bytes)
  5. GET/CONTAIN tier: 10,000 + 10*input_bytes (i.e. 0.8 microcredits for GETs/CONTAINs over 8192 bytes)
  6. SET Tier: 10,000 + 100*input_bytes microcredits/byte (i.e. 2.4 microcredits for SETs over 8192 bytes)

SET operations are limited to under 10 invocations per finalize block, so despite incurring a heavy cost per byte, they are priced lower due to their central importance for Aleo applications.

@vicsn vicsn mentioned this pull request Jan 10, 2024
Copy link
Contributor

@vicsn vicsn left a comment

Choose a reason for hiding this comment

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

Just a minor style comment. I would also encourage we implement a maximum finalize fee, similar in spirit to https://github.com/AleoHQ/snarkVM/pull/2271

synthesizer/src/vm/helpers/cost.rs Outdated Show resolved Hide resolved
@iamalwaysuncomfortable iamalwaysuncomfortable marked this pull request as ready for review January 10, 2024 18:42

use std::collections::HashMap;

// Finalize Costs for compute heavy operations. Used as BASE_COST + PER_BYTE_COST * SIZE_IN_BYTES.
Copy link
Contributor

Choose a reason for hiding this comment

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

How were the numbers backed into? I realize the target was ~100 credits per second of finalize runtime, but was there a formula for determining the base cost and per byte cost in relation to the observed runtime?

Copy link
Contributor

Choose a reason for hiding this comment

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

In addition, did we take into account that the scaling of runtime was linear for increases to the number of bytes of the operands? Because the fee model assumes the linear growth.

Copy link
Contributor

@raychu86 raychu86 Jan 21, 2024

Choose a reason for hiding this comment

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

In addition, did we take into account that the scaling of runtime was linear for increases to the number of bytes of the operands? Because the fee model assumes the linear growth.

Looking at the data, seems like the runtimes scale fairly linearly with the input size.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How were the numbers backed into? I realize the target was ~100 credits per second of finalize runtime, but was there a formula for determining the base cost and per byte cost in relation to the observed runtime?

The values were regressed over (using linear regression) using bytes as the independent variable and benchmarked runtime as the dependent variable.

The resulting equations are of the form are:
runtime(bytes) = A*bytes + C - both A & C were then extracted to be the cost multiplier for the opcode & the base cost for the opcode respectively. These values were scaled to get ~100 credits per second.

In addition, did we take into account that the scaling of runtime was linear for increases to the number of bytes of the operands? Because the fee model assumes the linear growth.

If you do look at the data you will indeed find there are some outliers, but as you say it scales "mostly" linearly

@raychu86 raychu86 changed the base branch from testnet3 to mainnet January 21, 2024 21:42
@raychu86
Copy link
Contributor

Converting the base to mainnet as fee changes will break verification of blocks on the current network.

Copy link
Contributor Author

@iamalwaysuncomfortable iamalwaysuncomfortable left a comment

Choose a reason for hiding this comment

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

Responded to the questions about how the pricing equations were arrived at by @raychu86

@howardwu howardwu force-pushed the feat/fee-based-on-operand-size branch from e6b29ad to 40e29a7 Compare January 27, 2024 22:14
@howardwu
Copy link
Contributor

^Note: The cost of inv and sign.verify was increased in this commit.

@howardwu howardwu merged commit 90b3515 into mainnet Jan 28, 2024
59 of 78 checks passed
@howardwu howardwu deleted the feat/fee-based-on-operand-size branch January 28, 2024 00:18
@howardwu
Copy link
Contributor

(Leaving a gist for myself)

Fee amount changes based on analysis and benchmarks (see Python notebook)

3 considerations:

  • Runtime: Target was 1 sec of runtime equates to ~100 credits in fees
  • Flat fee: Flat fee for fixed and common opcodes
  • Operand sizes: Based on size of inputs. Tiered system

@raychu86 raychu86 changed the title Price Finalize Opcodes Based On Operand Types [Stress-06] Price Finalize Opcodes Based On Operand Types Feb 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants