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

Optimize log2 with a lookup table #5236

Merged
merged 30 commits into from
Oct 29, 2024
Merged
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
aeef274
Improve log2 performance
Lohann Oct 3, 2024
5a7c140
Add changeset
Lohann Oct 3, 2024
75b656c
fix typo
Lohann Oct 3, 2024
9a12c3a
fix typo
Lohann Oct 3, 2024
faaf063
another typo
Lohann Oct 3, 2024
d4a6b96
Delete .changeset/tidy-pugs-invent.md
Amxx Oct 4, 2024
2fb49c5
fix requested changes
Lohann Oct 4, 2024
6cee5f4
npm run lint:fix
Lohann Oct 4, 2024
fbb09a7
typo in index table
Lohann Oct 4, 2024
60de789
Fix typo
Lohann Oct 4, 2024
32e9732
Merge branch 'master' into lohann/efficient-log2-algorithm
Lohann Oct 5, 2024
dda4566
Improve log2 documentation
Lohann Oct 5, 2024
42d55fc
Fix wrong statement in documentation
Lohann Oct 5, 2024
52cd8a3
Fix wrong statement in documentation again
Lohann Oct 5, 2024
f2108d9
Update Math.sol
Amxx Oct 7, 2024
40e0171
fix compiler error
Lohann Oct 8, 2024
1a70e91
update exponents in log2(x/n) table
Lohann Oct 9, 2024
4b22ad1
Merge branch 'master' into lohann/efficient-log2-algorithm
Lohann Oct 9, 2024
e4769b8
Update docs
Lohann Oct 9, 2024
52545de
Apply suggestions from code review
Amxx Oct 16, 2024
2dd23b6
Update Math.sol
Amxx Oct 16, 2024
18ff1e7
Apply suggestions from code review
Amxx Oct 16, 2024
a4addc6
Single lookup for log2
cairoeth Oct 16, 2024
4521ed4
Merge pull request #1 from cairoeth/single-lookup-log2
Lohann Oct 17, 2024
2a50166
Improve readability
Lohann Oct 17, 2024
6e2bb8e
Merge branch 'master' into lohann/efficient-log2-algorithm
Lohann Oct 18, 2024
5efcdc2
Add suggestion
cairoeth Oct 25, 2024
fe5584f
Add Bruijin Multiplication reference
Lohann Oct 29, 2024
d81e3d2
Merge branch 'master' into lohann/efficient-log2-algorithm
Lohann Oct 29, 2024
111a13f
Update docs
Lohann Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 41 additions & 34 deletions contracts/utils/math/Math.sol
Original file line number Diff line number Diff line change
Expand Up @@ -537,41 +537,48 @@ library Math {
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;

exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;

exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;

exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;

exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;

exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;

exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;

result += SafeCast.toUint(value > 1);
function log2(uint256 x) internal pure returns (uint256 r) {
// log2 implementation based on De Bruijn Multiplication.
// ref: https://www.chessprogramming.org/BitScan#De_Bruijn_Multiplication_2

// If value has upper 128 bits set, log2 result is at least 128
Lohann marked this conversation as resolved.
Show resolved Hide resolved
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;

// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
return result;
}

/**
Expand Down