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

Fast Keccak cache (wait-free and lockless) #7336

Merged
merged 53 commits into from
Sep 19, 2024
Merged

Fast Keccak cache (wait-free and lockless) #7336

merged 53 commits into from
Sep 19, 2024

Conversation

Scooletz
Copy link
Contributor

@Scooletz Scooletz commented Aug 16, 2024

This PR introduces Keccaks caching (see #7333 and related for other attempts) that is minimal in regards to locks, copying and memory overhead. This PR introduces a KeccakCache that

  1. is one-way set associative cache
  2. allows to cache 128k values
  3. occupies a fixed 12MB of unmanaged memory allocated once
  4. uses a fast hash to bucketize
  5. uses 1 Interlocked.CompareExchange to lock
  6. uses 1 Volatile.Write to unlock
  7. locks at per entry granularity

It does not augments the ValueKeccak itself, allowing the callsite owner to decide, whether or not the value should go through cache. Call sites amended:

  1. StateTree address->Keccak calculations
  2. EVM Instruction.Keccak256 execution
  3. StorageTree uint256 -> Keccak cell address calculations

There are several interesting methods used in this PR:

  1. encoding a lot of state in a single int to that volatile and Interlocked can operate on it
  2. wait-free, as if a caller fails to lock to check the value, they compute. If they fail to lock again, they don't put it in the cache
  3. special cases for lengths of 20 and 32 that are compared without calling to the span
  4. alignment for vectors, that should speed up the access and comparisons again
  5. stack copying, as the entry is unmanaged, when a lock is taken for reading purposes, what is needed from Entry can be copied on the stack, so that the lock is released in a fast manner

Changes

  • introduce KeccakCache that allows to cache values of the Keccak.
  • introduce a hardware accelerated Crc32c hash for Dictionary keys that is randomised per instance and use that for most of our dictionary key hashing as a common fast method (randomised so working out a hash collision attack on one node will not impact other nodes or work across restarts)

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Benchmarks

Synthetic

Single threaded, hammering the same value:

Method data Mean Error StdDev
Current Byte[20] 210.804 ns 2.4348 ns 3.4132 ns
Memoized Byte[20] 8.127 ns 0.0868 ns 0.1299 ns
Current Byte[32] 210.454 ns 1.7798 ns 2.6638 ns
Memoized Byte[32] 7.913 ns 0.0780 ns 0.1143 ns

With master

Running this branch and master, side by side

Block Number MGas - master MGas - keccak-cache
20790697 312.53 324.60
20790696 243.25 280.49
20790695 319.11 309.14
20790694 291.36 315.18
20790693 293.32 326.53
20790692 284.61 312.12
20790691 259.67 262.53
20790690 173.69 178.68
20790689 292.05 242.21
20790688 301.45 309.54
20790687 266.11 305.26
20790686 313.73 319.77
20790685 306.00 247.28
20790684 213.88 278.12
20790683 316.69 346.71

Profiling mainnet

Saves on approx 40% of keccaks when running mainnet (88780 to 53664 approx ~40%)

image

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Notes on testing

Optional. Remove if not applicable.

Documentation

Requires documentation update

  • Yes
  • No

If yes, link the PR to the docs update or the issue with the details labeled docs. Remove if not applicable.

Requires explanation in Release Notes

  • Yes
  • No

If yes, fill in the details here. Remove if not applicable.

Remarks

Optional. Remove if not applicable.

@Scooletz
Copy link
Contributor Author

@benaadams benaadams marked this pull request as ready for review August 18, 2024 07:59
Copy link
Member

@benaadams benaadams left a comment

Choose a reason for hiding this comment

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

Someone else should also review since I added a lot of changes

@benaadams benaadams changed the title Keccak cache Keccak cache (wait-free and lock-less) Aug 19, 2024
@benaadams benaadams changed the title Keccak cache (wait-free and lock-less) Keccak cache (wait-free and lockless) Aug 19, 2024
@Scooletz
Copy link
Contributor Author

Requires mano a mano comparison with master. Same blocks etc.

@Scooletz
Copy link
Contributor Author

Scooletz commented Aug 27, 2024

Can be merged once proven with #7346

@benaadams benaadams changed the title Keccak cache (wait-free and lockless) Fast Keccak cache (wait-free and lockless) Aug 31, 2024
@Scooletz
Copy link
Contributor Author

@benaadams benaadams merged commit 89292b1 into master Sep 19, 2024
66 checks passed
@benaadams benaadams deleted the keccak-cache branch September 19, 2024 18:36
@LukaszRozmej
Copy link
Member

Can it give false positive? Find some Keccak for input that will be wrong (it will be keccak of different input, but have same length and same FastHash)?

@Scooletz
Copy link
Contributor Author

Scooletz commented Sep 20, 2024

No, it can't give a false positive. The original input is preserved and properly compared. We use a fat struct that is copied on the stack and then compare, so no dirty reads/writes can happen. The copy is done under the lock.

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.

3 participants