-
Notifications
You must be signed in to change notification settings - Fork 11.8k
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
Optimize muldiv #4494
Optimize muldiv #4494
Conversation
🦋 Changeset detectedLatest commit: 9a86e03 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@@ -163,8 +162,7 @@ library Math { | |||
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. | |||
// See https://cs.stackexchange.com/q/138556/92363. | |||
|
|||
// Does not overflow because the denominator cannot be zero at this stage in the function. | |||
uint256 twos = denominator & (~denominator + 1); | |||
uint256 twos = denominator & (0 - denominator); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is indeed a good solution !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record, before solidity v8, the code used to be uint256 twos = -denominator & denominator;
but solidity 0.8 forbid the -x
syntax for unsigned integers. Doing (0-x)
indeed fixes it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are great findings !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Congrats, your important contribution to this open-source project has earned you a GitPOAP! GitPOAP: 2023 OpenZeppelin Contracts Contributor: Head to gitpoap.io & connect your GitHub account to mint! Learn more about GitPOAPs here. |
This pull request contains two small changes to the mulDiv function to slightly optimize gas.
Reduce stack operations in mulDiv
The product of x and y to obtain the least significant bits of the result is moved above, to the place where the variable prod0 is declared.
https://github.com/vladyan18/openzeppelin-contracts/blob/e54956d8453f04f59491511df8d353dbaab98202/contracts/utils/math/Math.sol#L127
Rationale: with compiler optimization enabled, the old implementation led to unnecessary initialization of the variable by zero. As a result, this initialization also added unnecessary stack operations below in the code. Since this code is in an unchecked block, calculating the product in assembly does not provide gas savings by itself.
Gas tests, small inputs. Optimizer 200 runs, 0.8.20
Gas before: 198
Gas after: 190 (-4%)
Simplify twos calculation in mulDiv
An easier method was used to factor powers of two out of denominator due to which the number of necessary operations is reduced by one.
https://github.com/vladyan18/openzeppelin-contracts/blob/e54956d8453f04f59491511df8d353dbaab98202/contracts/utils/math/Math.sol#L165
Rationale: the previous implementation uses a unary negation operation and ends up needing more operations to get the same result. The proposed line gives the same result inside an unchecked block with fewer operations.
Gas tests, big inputs. Optimizer 200 runs, 0.8.20
Gas before: 497
Gas after: 494 (-0.6%)
PR Checklist
npx changeset add
)Probably new tests and documentation are not required for this pull request.