Skip to content

Commit

Permalink
Optimize position and factor out logic to compute compressed tick i…
Browse files Browse the repository at this point in the history
…n `TickBitmap` (#654)

* Optimize `TickBitmap.position` using assembly

* Factor out logic to compute `compressed` to a `compress` function

* Add fuzz test for `compress` and `position`

* Replace `compressed + 1` by `++compressed` in `nextInitializedTickWithinOneWord`
  • Loading branch information
shuhuiluo committed May 17, 2024
1 parent 6786fd4 commit edc9b10
Show file tree
Hide file tree
Showing 33 changed files with 66 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity CA fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
331635
331619
2 changes: 1 addition & 1 deletion .forge-snapshots/addLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
286628
286612
Original file line number Diff line number Diff line change
@@ -1 +1 @@
302147
302131
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22504
22496
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5513
5505
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2578
2488
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2578
2488
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2556
2475
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2865
2784
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2556
2475
2 changes: 1 addition & 1 deletion .forge-snapshots/poolManager bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
21878
21796
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity CA fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187101
187085
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with empty hook.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
123469
123453
2 changes: 1 addition & 1 deletion .forge-snapshots/removeLiquidity with native token.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120256
120240
2 changes: 1 addition & 1 deletion .forge-snapshots/simple addLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
167750
167734
2 changes: 1 addition & 1 deletion .forge-snapshots/simple removeLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
90895
90879
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap with native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
117057
116827
2 changes: 1 addition & 1 deletion .forge-snapshots/simple swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132218
131988
2 changes: 1 addition & 1 deletion .forge-snapshots/swap CA fee on unspecified.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183356
183126
Original file line number Diff line number Diff line change
@@ -1 +1 @@
112697
112548
2 changes: 1 addition & 1 deletion .forge-snapshots/swap against liquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
124040
123891
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
136081
135923
2 changes: 1 addition & 1 deletion .forge-snapshots/swap burn native 6909 for input.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125221
125140
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint native output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
147270
147180
2 changes: 1 addition & 1 deletion .forge-snapshots/swap mint output as 6909.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164072
163842
Original file line number Diff line number Diff line change
@@ -1 +1 @@
222578
222199
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
148273
148043
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with hooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
124052
123903
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with lp fee and protocol fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
180453
180223
2 changes: 1 addition & 1 deletion .forge-snapshots/swap with return dynamic fee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
156132
155902
2 changes: 1 addition & 1 deletion .forge-snapshots/update dynamic fee in before swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158735
158505
30 changes: 22 additions & 8 deletions src/libraries/TickBitmap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,29 @@ library TickBitmap {
/// @param tickSpacing The tick spacing of the pool
error TickMisaligned(int24 tick, int24 tickSpacing);

/// @dev round towards negative infinity
function compress(int24 tick, int24 tickSpacing) internal pure returns (int24 compressed) {
// compressed = tick / tickSpacing;
// if (tick < 0 && tick % tickSpacing != 0) compressed--;
assembly {
compressed :=
sub(
sdiv(tick, tickSpacing),
// if (tick < 0 && tick % tickSpacing != 0) then tick % tickSpacing < 0, vice versa
slt(smod(tick, tickSpacing), 0)
)
}
}

/// @notice Computes the position in the mapping where the initialized bit for a tick lives
/// @param tick The tick for which to compute the position
/// @return wordPos The key in the mapping containing the word in which the bit is stored
/// @return bitPos The bit position in the word where the flag is stored
function position(int24 tick) internal pure returns (int16 wordPos, uint8 bitPos) {
unchecked {
wordPos = int16(tick >> 8);
bitPos = uint8(int8(tick & (256 - 1)));
assembly {
// signed arithmetic shift right
wordPos := sar(8, tick)
bitPos := and(tick, 0xff)
}
}

Expand Down Expand Up @@ -51,8 +66,7 @@ library TickBitmap {
bool lte
) internal view returns (int24 next, bool initialized) {
unchecked {
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
int24 compressed = compress(tick, tickSpacing);

if (lte) {
(int16 wordPos, uint8 bitPos) = position(compressed);
Expand All @@ -68,7 +82,7 @@ library TickBitmap {
: (compressed - int24(uint24(bitPos))) * tickSpacing;
} else {
// start from the word of the next tick, since the current tick state doesn't matter
(int16 wordPos, uint8 bitPos) = position(compressed + 1);
(int16 wordPos, uint8 bitPos) = position(++compressed);
// all the 1s at or to the left of the bitPos
uint256 mask = ~((1 << bitPos) - 1);
uint256 masked = self[wordPos] & mask;
Expand All @@ -77,8 +91,8 @@ library TickBitmap {
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing
: (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing;
? (compressed + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing
: (compressed + int24(uint24(type(uint8).max - bitPos))) * tickSpacing;
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions test/libraries/TickBitmap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ contract TickBitmapTest is Test, GasSnapshot {
}
}

function test_fuzz_compress(int24 tick, int24 tickSpacing) public pure {
tickSpacing = int24(bound(tickSpacing, 1, type(int24).max));
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--;
assertEq(TickBitmap.compress(tick, tickSpacing), compressed);
}

function test_fuzz_position(int24 tick) public pure {
(int16 wordPos, uint8 bitPos) = TickBitmap.position(tick);
assertEq(wordPos, tick >> 8);
assertEq(bitPos, uint8(int8(tick % 256)));
}

function test_isInitialized_isFalseAtFirst() public view {
assertEq(isInitialized(1), false);
}
Expand Down

0 comments on commit edc9b10

Please sign in to comment.