Skip to content

Optimize existing and add missing Hash implementations #7238

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

Merged
merged 2 commits into from
Jun 18, 2025

Conversation

ironcev
Copy link
Member

@ironcev ironcev commented Jun 18, 2025

Description

This PR:

  • optimizes the existing std::hash::Hash implementations in the std, for bytecode size and gas usage. The optimizations are based on eliminations of intensive memory allocations and memory copying. On a sample application, the bytcode size reduction was ~10%, and the gas usage reduction >50%. The detailed results are presented below.
  • fixes Calling Bytes::append on empty target Bytes can lead to content/memory overrides #7234
  • adds Hash implementations for:
    • unit type (),
    • tuples of a single element (T, ),
    • empty arrays [T; 0],
    • other std types that were missing Hash implementations, like, e.g.: Duration, Time, U128, B512, etc. Note that Hash implementations were not provided for various Error enums.

Performance Optimizations

To measure performance gains, a sample application was used, that:

  • hashed all built-in types individually, as well as tuples and array of those, and Bytes and Vec.
  • hashed all the above types within a same Hasher, simulating hashing of complex types like, e.g., structs.

The bytcode size of the sample application got reduced from 9560 to 8584 bytes (10.21% reduction).

The overall gas usage for hashing types individually got reduced from 35826 to 15562 (56.56% reduction).

The overall gas usage for hashing types within a same Hasher got reduced from 34951 to 14443 (58.67% reduction).

The in_language tests for hashing, hash_inline_tests, also demonstrated a significant reduction in gas usage, up to 57.82%. A small regression is noticable when hashing empty types, e.g., unit. We expect that regression also to disappear once const fn is implemented, and local constants could be evaluated from generic functions.

Expand to see the detailed gas cost comparison for all `hash_inline_tests`
Test Before After Percentage
hash_address 10070 6366 36.78%
hash_array_10 33691 15867 52.90%
hash_array_1 4870 3102 36.30%
hash_array_2 8069 4517 44.02%
hash_array_3 11219 5883 47.56%
hash_array_4 14369 7249 49.55%
hash_array_5 17519 8615 50.82%
hash_array_6 20917 10229 51.10%
hash_array_7 24109 11637 51.73%
hash_array_8 27302 13046 52.22%
hash_array_9 30494 14454 52.60%
hash_array_empty 773 777 -0.52%
hash_asset_id 10070 6366 36.78%
hash_b256 9604 5900 38.57%
hash_b512 6028 4476 25.75%
hash_bool 3885 1993 48.70%
hash_bytes 8200 7112 13.27%
hash_call_params 11439 6019 47.38%
hash_contract_id 10070 6366 36.78%
hash_duration 4753 2985 37.20%
hash_ed25519 18268 14780 19.09%
hash_evm_address 10098 6394 36.68%
hash_fn_sha256_str_array 1953 1167 40.25%
hash_hasher_write_str 2512 2013 19.86%
hash_hasher_write_str_array 2306 1807 21.64%
hash_identity 30526 14998 50.87%
hash_input 11766 5718 51.40%
hash_message 3003 3099 -3.20%
hash_option 21373 9511 55.50%
hash_output 19649 9569 51.30%
hash_point2d 6861 4801 30.02%
hash_public_key 7372 7692 -4.34%
hash_result 27083 11423 57.82%
hash_scalar 4159 3313 20.34%
hash_secp256k1 18268 14780 19.09%
hash_secp256r1 18268 14780 19.09%
hash_signature 31213 16936 45.74%
hash_str 6902 5417 21.52%
hash_string 5453 5252 3.69%
hash_time 4753 2985 37.20%
hash_transaction 23604 11508 51.25%
hash_tuple_1 4834 3066 36.57%
hash_tuple_2 7997 4445 44.42%
hash_tuple_3 11147 5811 47.87%
hash_tuple_4 14297 7177 49.80%
hash_tuple_5 17447 8543 51.03%
hash_u128 7943 4391 44.72%
hash_u16 9384 5860 37.55%
hash_u256 9600 5896 38.58%
hash_u32 9384 5860 37.55%
hash_u64 9372 5836 37.73%
hash_u8 7740 3704 52.14%
hash_unit 771 775 -0.52%
hash_vec 53077 33733 36.45%

Breaking Changes

Strictly seen, adding Hash implementations for std types like Option<T> represents a breaking change for those who eventually had their own implementations. However, if such implementations existed, after introducing strict trait coherence checks, they already became invalid, because neither the Hash trait nor the types Hash is implemented for are part of third party packages, but contained within the std.

Thus, we can have a breaking change only if someone migrates from an older version that does not have trait coherence in place. But in that case, the trait coherence itself will already report breaking change errors.

Because Hash implementations for std types must and should have already been provided within the std, we can treat adding those implementations as a bug fix.

Checklist

  • I have linked to any relevant issues.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have updated the documentation where relevant (API docs, the reference, and the Sway book).
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added (or requested a maintainer to add) the necessary Breaking* or New Feature labels where relevant.
  • I have done my best to ensure that my PR adheres to the Fuel Labs Code Review Standards.
  • I have requested a review from the relevant team or maintainers.

@ironcev ironcev self-assigned this Jun 18, 2025
@ironcev ironcev temporarily deployed to fuel-sway-bot June 18, 2025 03:10 — with GitHub Actions Inactive
@ironcev ironcev added lib: std Standard library breaking May cause existing user code to break. Requires a minor or major release. labels Jun 18, 2025
@ironcev ironcev marked this pull request as ready for review June 18, 2025 03:39
@ironcev ironcev requested review from a team as code owners June 18, 2025 03:39
@ironcev ironcev requested a review from bitzoic June 18, 2025 03:39
Copy link
Member

@bitzoic bitzoic left a comment

Choose a reason for hiding this comment

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

🚀

@IGI-111
Copy link
Contributor

IGI-111 commented Jun 18, 2025

For the record I don't think expanding std implementations should ever be treated as a breaking change. Rust has this same theoretical problem that star imports mean any change to the surface of a library is potentially breaking, but stands by the pragmatic behavior too.

@IGI-111 IGI-111 removed the breaking May cause existing user code to break. Requires a minor or major release. label Jun 18, 2025
@IGI-111 IGI-111 enabled auto-merge (squash) June 18, 2025 12:23
@ironcev ironcev temporarily deployed to fuel-sway-bot June 18, 2025 12:23 — with GitHub Actions Inactive
@IGI-111 IGI-111 merged commit f12c789 into master Jun 18, 2025
41 of 77 checks passed
@IGI-111 IGI-111 deleted the ironcev/optimize-hash-module branch June 18, 2025 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lib: std Standard library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Calling Bytes::append on empty target Bytes can lead to content/memory overrides
5 participants