Skip to content

Commit

Permalink
Optimize FindFirstSet routine
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Apr 5, 2024
1 parent a80604e commit 8e5499d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 67 deletions.
118 changes: 59 additions & 59 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -67,108 +67,108 @@ BenchTest:testMintPandora_13() (gas: 967960)
BenchTest:testMintPandora_14() (gas: 1037484)
BenchTest:testMintPandora_15() (gas: 1106967)
BenchTest:testMintPandora_16() (gas: 1176473)
BenchTest:test__codesize() (gas: 26827)
BenchTest:test__codesize() (gas: 26848)
DN404CustomUnitTest:testInitializeCorrectUnitSuccess() (gas: 129962)
DN404CustomUnitTest:testInitializeWithUnitTooLargeReverts() (gas: 33824)
DN404CustomUnitTest:testInitializeWithZeroUnitReverts() (gas: 13897)
DN404CustomUnitTest:testMint() (gas: 162923)
DN404CustomUnitTest:testMintWithoutNFTs(uint256,uint256,uint256) (runs: 256, μ: 154657, ~: 163018)
DN404CustomUnitTest:testMintWithoutNFTs(uint256,uint256,uint256) (runs: 258, μ: 154944, ~: 163018)
DN404CustomUnitTest:testNFTMint() (gas: 57526664)
DN404CustomUnitTest:testNFTMintAndBurn(uint256,uint256,uint256) (runs: 256, μ: 207418, ~: 161918)
DN404CustomUnitTest:testNFTMintViaTransfer(uint256,uint256,uint256) (runs: 256, μ: 217261, ~: 241493)
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256) (runs: 256, μ: 635, ~: 692)
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256,uint256) (runs: 256, μ: 780, ~: 746)
DN404CustomUnitTest:testUnitInvalidCheckTrick(uint256) (runs: 256, μ: 548, ~: 550)
DN404CustomUnitTest:test__codesize() (gas: 28180)
DN404CustomUnitTest:testNFTMintAndBurn(uint256,uint256,uint256) (runs: 258, μ: 206548, ~: 161918)
DN404CustomUnitTest:testNFTMintViaTransfer(uint256,uint256,uint256) (runs: 258, μ: 218693, ~: 241612)
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256) (runs: 258, μ: 635, ~: 692)
DN404CustomUnitTest:testTotalSupplyOverflowsTrick(uint256,uint256,uint256) (runs: 258, μ: 779, ~: 746)
DN404CustomUnitTest:testUnitInvalidCheckTrick(uint256) (runs: 258, μ: 548, ~: 550)
DN404CustomUnitTest:test__codesize() (gas: 28201)
DN404MirrorTest:testBaseERC20() (gas: 114578)
DN404MirrorTest:testFnSelectorNotRecognized() (gas: 7032)
DN404MirrorTest:testLinkMirrorContract() (gas: 45864)
DN404MirrorTest:testLogDirectTransfers() (gas: 378691)
DN404MirrorTest:testLogTransfer() (gas: 120655)
DN404MirrorTest:testNameAndSymbol(string,string) (runs: 256, μ: 207746, ~: 208087)
DN404MirrorTest:testNameAndSymbol(string,string) (runs: 258, μ: 207574, ~: 208087)
DN404MirrorTest:testNotLinked() (gas: 12767)
DN404MirrorTest:testPullOwner() (gas: 112613)
DN404MirrorTest:testPullOwnerWithOwnable() (gas: 3244380)
DN404MirrorTest:testSafeTransferFrom(uint32) (runs: 256, μ: 465850, ~: 465839)
DN404MirrorTest:testPullOwnerWithOwnable() (gas: 3248596)
DN404MirrorTest:testSafeTransferFrom(uint32) (runs: 258, μ: 465848, ~: 465839)
DN404MirrorTest:testSetAndGetApprovalForAll() (gas: 325298)
DN404MirrorTest:testSetAndGetApproved() (gas: 322497)
DN404MirrorTest:testSupportsInterface() (gas: 7544)
DN404MirrorTest:testTokenURI(string,uint256) (runs: 256, μ: 158053, ~: 135791)
DN404MirrorTest:testTransferFrom(uint32) (runs: 256, μ: 366878, ~: 366870)
DN404MirrorTest:testTransferFromMixed(uint256) (runs: 256, μ: 654691, ~: 601450)
DN404MirrorTest:test__codesize() (gas: 56875)
DN404MirrorTest:testTokenURI(string,uint256) (runs: 258, μ: 158056, ~: 135791)
DN404MirrorTest:testTransferFrom(uint32) (runs: 258, μ: 366878, ~: 366870)
DN404MirrorTest:testTransferFromMixed(uint256) (runs: 258, μ: 657212, ~: 616815)
DN404MirrorTest:test__codesize() (gas: 56917)
DN404OnlyERC20Test:testApprove() (gas: 35803)
DN404OnlyERC20Test:testApprove(address,uint256) (runs: 256, μ: 30110, ~: 31354)
DN404OnlyERC20Test:testApprove(address,uint256) (runs: 258, μ: 30505, ~: 31354)
DN404OnlyERC20Test:testBurn() (gas: 51486)
DN404OnlyERC20Test:testBurn(address,uint256,uint256) (runs: 256, μ: 52745, ~: 52687)
DN404OnlyERC20Test:testBurnInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 45816, ~: 45844)
DN404OnlyERC20Test:testBurn(address,uint256,uint256) (runs: 258, μ: 52484, ~: 52687)
DN404OnlyERC20Test:testBurnInsufficientBalanceReverts(address,uint256,uint256) (runs: 258, μ: 45646, ~: 45780)
DN404OnlyERC20Test:testInfiniteApproveTransferFrom() (gas: 104368)
DN404OnlyERC20Test:testMaxSupplyTrick(uint256) (runs: 256, μ: 541, ~: 541)
DN404OnlyERC20Test:testMaxSupplyTrick(uint256) (runs: 258, μ: 541, ~: 541)
DN404OnlyERC20Test:testMetadata() (gas: 10044)
DN404OnlyERC20Test:testMint() (gas: 47556)
DN404OnlyERC20Test:testMintOverMaxLimitReverts() (gas: 43535)
DN404OnlyERC20Test:testMintz(address,uint256) (runs: 256, μ: 47963, ~: 47978)
DN404OnlyERC20Test:testMintz(address,uint256) (runs: 258, μ: 47999, ~: 47978)
DN404OnlyERC20Test:testTransfer() (gas: 77086)
DN404OnlyERC20Test:testTransfer(address,uint256) (runs: 256, μ: 77523, ~: 77543)
DN404OnlyERC20Test:testTransfer(address,uint256) (runs: 258, μ: 77584, ~: 77543)
DN404OnlyERC20Test:testTransferFrom() (gas: 86826)
DN404OnlyERC20Test:testTransferFrom(address,address,address,uint256,uint256) (runs: 256, μ: 107687, ~: 109795)
DN404OnlyERC20Test:testTransferFrom(address,address,address,uint256,uint256) (runs: 258, μ: 108050, ~: 109795)
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts() (gas: 70261)
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256) (runs: 256, μ: 70872, ~: 71371)
DN404OnlyERC20Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256) (runs: 258, μ: 70540, ~: 71362)
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts() (gas: 76697)
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 77940, ~: 77827)
DN404OnlyERC20Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256) (runs: 258, μ: 77778, ~: 77818)
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts() (gas: 68196)
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 69324, ~: 69283)
DN404OnlyERC20Test:test__codesize() (gas: 30269)
DN404OnlyERC20Test:testTransferInsufficientBalanceReverts(address,uint256,uint256) (runs: 258, μ: 69152, ~: 69283)
DN404OnlyERC20Test:test__codesize() (gas: 30290)
DN404Test:testBatchNFTLog() (gas: 291443)
DN404Test:testBurnOnTransfer(uint32,address) (runs: 256, μ: 269747, ~: 269767)
DN404Test:testBurnOnTransfer(uint32,address) (runs: 258, μ: 269767, ~: 269767)
DN404Test:testFnSelectorNotRecognized() (gas: 6974)
DN404Test:testInitialize(uint32,address) (runs: 256, μ: 112848, ~: 116293)
DN404Test:testInitialize(uint32,address) (runs: 258, μ: 112476, ~: 116293)
DN404Test:testMintAndBurn() (gas: 328527)
DN404Test:testMintAndBurn2() (gas: 275124)
DN404Test:testMintNext() (gas: 627811)
DN404Test:testMintNextMixed(uint256) (runs: 256, μ: 649840, ~: 563338)
DN404Test:testMintOnTransfer(uint32,address) (runs: 256, μ: 287397, ~: 287417)
DN404Test:testMixed(uint256) (runs: 256, μ: 839247, ~: 678570)
DN404Test:testNameAndSymbol(string,string) (runs: 256, μ: 207464, ~: 207805)
DN404Test:testMintNextMixed(uint256) (runs: 258, μ: 642243, ~: 558410)
DN404Test:testMintOnTransfer(uint32,address) (runs: 258, μ: 287407, ~: 287417)
DN404Test:testMixed(uint256) (runs: 258, μ: 812401, ~: 646502)
DN404Test:testNameAndSymbol(string,string) (runs: 258, μ: 207292, ~: 207805)
DN404Test:testNumAliasesOverflowReverts() (gas: 57345)
DN404Test:testOwnedIds() (gas: 350398)
DN404Test:testOwnedIds(uint256) (runs: 256, μ: 269018, ~: 279561)
DN404Test:testOwnedIds(uint256) (runs: 258, μ: 272722, ~: 282630)
DN404Test:testPermit2() (gas: 437378)
DN404Test:testRegisterAndResolveAlias(address,address) (runs: 256, μ: 126495, ~: 126593)
DN404Test:testSetAndGetAux(address,uint88) (runs: 256, μ: 22003, ~: 22344)
DN404Test:testSetAndGetOperatorApprovals(address,address,bool) (runs: 256, μ: 129355, ~: 120416)
DN404Test:testRegisterAndResolveAlias(address,address) (runs: 258, μ: 126496, ~: 126593)
DN404Test:testSetAndGetAux(address,uint88) (runs: 258, μ: 22006, ~: 22344)
DN404Test:testSetAndGetOperatorApprovals(address,address,bool) (runs: 258, μ: 130828, ~: 140316)
DN404Test:testSetAndGetSkipNFT() (gas: 719127)
DN404Test:testTokenURI(string,uint256) (runs: 256, μ: 157919, ~: 135657)
DN404Test:testTokenURI(string,uint256) (runs: 258, μ: 157922, ~: 135657)
DN404Test:testTransfersAndBurns() (gas: 461532)
DN404Test:testWrapAround(uint32,uint256) (runs: 256, μ: 347850, ~: 343630)
DN404Test:test__codesize() (gas: 55865)
MappingsTest:testAddressPairMapSetAndGet(address[2],address[2],uint256,uint256) (runs: 256, μ: 45731, ~: 47053)
MappingsTest:testBitmapSetAndGet(uint256) (runs: 256, μ: 448253, ~: 406079)
MappingsTest:testBitmapSetAndGet(uint256,uint256,bool,bool) (runs: 256, μ: 25726, ~: 26337)
MappingsTest:testFindFirstUnset() (gas: 80100)
MappingsTest:testFindFirstUnset(uint256) (runs: 256, μ: 309525, ~: 222129)
MappingsTest:testRestrictNFTId(uint256) (runs: 256, μ: 340, ~: 340)
MappingsTest:testSetOwnerAliasAndOwnedIndex(uint256,uint32,uint32) (runs: 256, μ: 23247, ~: 23516)
MappingsTest:testStorageSlotsNoCollision(uint256,uint256,uint256,uint256) (runs: 256, μ: 26827, ~: 26688)
MappingsTest:testUint32MapSetAndGet(uint256) (runs: 256, μ: 1372635, ~: 1526111)
MappingsTest:testUint32MapSetAndGet(uint256,uint256,uint32,uint32) (runs: 256, μ: 42310, ~: 46244)
MappingsTest:testWrapNFTIdWithOverflowCheck(uint256,uint256,uint256) (runs: 256, μ: 823, ~: 852)
MappingsTest:test__codesize() (gas: 7973)
MintTests:test_WhenAmountIsGreaterThan_MAX_SUPPLYOrMintMakesNFTTotalSupplyExceed_MAX_SUPPLY(uint256) (runs: 256, μ: 64533, ~: 64706)
MintTests:test_WhenRecipientAddressHasSkipNFTEnabled(uint256) (runs: 256, μ: 85927, ~: 85920)
MintTests:test_WhenRecipientIsAddress0(uint256) (runs: 256, μ: 31105, ~: 31169)
MintTests:test_WhenRecipientsBalanceDifferenceIsNotUpTo1e18(uint256) (runs: 256, μ: 82833, ~: 82970)
MintTests:test_WhenRecipientsBalanceDifferenceIsUpTo1e18OrAbove(uint256) (runs: 256, μ: 89409, ~: 89492)
MintTests:test__codesize() (gas: 26518)
DN404Test:testWrapAround(uint32,uint256) (runs: 258, μ: 347412, ~: 343378)
DN404Test:test__codesize() (gas: 55886)
MappingsTest:testAddressPairMapSetAndGet(address[2],address[2],uint256,uint256) (runs: 258, μ: 45433, ~: 47053)
MappingsTest:testBitmapSetAndGet(uint256) (runs: 258, μ: 463246, ~: 422073)
MappingsTest:testBitmapSetAndGet(uint256,uint256,bool,bool) (runs: 258, μ: 25664, ~: 26361)
MappingsTest:testFindFirstUnset() (gas: 79815)
MappingsTest:testFindFirstUnset(uint256) (runs: 258, μ: 303430, ~: 221863)
MappingsTest:testRestrictNFTId(uint256) (runs: 258, μ: 340, ~: 340)
MappingsTest:testSetOwnerAliasAndOwnedIndex(uint256,uint32,uint32) (runs: 258, μ: 23331, ~: 23516)
MappingsTest:testStorageSlotsNoCollision(uint256,uint256,uint256,uint256) (runs: 258, μ: 26806, ~: 26675)
MappingsTest:testUint32MapSetAndGet(uint256) (runs: 258, μ: 1375364, ~: 1571823)
MappingsTest:testUint32MapSetAndGet(uint256,uint256,uint32,uint32) (runs: 258, μ: 42568, ~: 46244)
MappingsTest:testWrapNFTIdWithOverflowCheck(uint256,uint256,uint256) (runs: 258, μ: 827, ~: 852)
MappingsTest:test__codesize() (gas: 7995)
MintTests:test_WhenAmountIsGreaterThan_MAX_SUPPLYOrMintMakesNFTTotalSupplyExceed_MAX_SUPPLY(uint256) (runs: 258, μ: 64532, ~: 64713)
MintTests:test_WhenRecipientAddressHasSkipNFTEnabled(uint256) (runs: 258, μ: 85930, ~: 85920)
MintTests:test_WhenRecipientIsAddress0(uint256) (runs: 258, μ: 31104, ~: 31169)
MintTests:test_WhenRecipientsBalanceDifferenceIsNotUpTo1e18(uint256) (runs: 258, μ: 82890, ~: 82974)
MintTests:test_WhenRecipientsBalanceDifferenceIsUpTo1e18OrAbove(uint256) (runs: 258, μ: 89464, ~: 89492)
MintTests:test__codesize() (gas: 26539)
NFTMintDN404Test:testAllowlistMint() (gas: 257449)
NFTMintDN404Test:testMint() (gas: 231750)
NFTMintDN404Test:testTotalSupplyReached() (gas: 629566900)
NFTMintDN404Test:test__codesize() (gas: 26501)
NFTMintDN404Test:test__codesize() (gas: 26522)
SimpleDN404Test:testMint() (gas: 47571)
SimpleDN404Test:testName() (gas: 9674)
SimpleDN404Test:testSetBaseURI() (gas: 47011)
SimpleDN404Test:testSymbol() (gas: 9672)
SimpleDN404Test:testWithdraw() (gas: 18277)
SimpleDN404Test:test__codesize() (gas: 20220)
SimpleDN404Test:test__codesize() (gas: 20241)
SoladyTest:test__codesize() (gas: 1102)
TestPlus:test__codesize() (gas: 406)
12 changes: 8 additions & 4 deletions src/DN404.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1280,11 +1280,15 @@ abstract contract DN404 {
}
if negBits {
// Find-first-set routine.
// From: https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol
let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, b))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
let r := shl(5, shr(252, shl(shl(2, shr(250, mul(b,
0x2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff))),
0x1412563212c14164235266736f7425221143267a45243675267677)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
Expand Down
12 changes: 8 additions & 4 deletions test/Mappings.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,15 @@ contract MappingsTest is SoladyTest {
}
if negBits {
// Find-first-set routine.
// From: https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol
let b := and(negBits, add(not(negBits), 1)) // Isolate the least significant bit.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, b))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.
// For the upper 3 bits of the result, use a De Bruijn-like lookup.
// Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
// forgefmt: disable-next-item
let r := shl(5, shr(252, shl(shl(2, shr(250, mul(b,
0x2aaaaaaaba69a69a6db6db6db2cb2cb2ce739ce73def7bdeffffffff))),
0x1412563212c14164235266736f7425221143267a45243675267677)))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
Expand Down

0 comments on commit 8e5499d

Please sign in to comment.