-
Notifications
You must be signed in to change notification settings - Fork 15
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
feat: adds a few more tips #1
base: main
Are you sure you want to change the base?
Changes from 6 commits
3afa909
0d796cb
5b1cad0
244182e
fc1882b
889b45f
fb1f401
48b3b6e
d33debe
b814df2
f45ed35
70d07b7
5606811
52b02b0
00d2a4f
c4cfffb
fc0707f
d4dabe3
da9e4de
bfac3ce
964c888
7b32928
6bb2a4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.sol linguist-language=Solidity |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/out |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "lib/ds-test"] | ||
path = lib/ds-test | ||
url = https://github.com/dapphub/ds-test |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
all :; dapp build | ||
clean :; dapp clean | ||
test :; dapp test | ||
deploy :; dapp create GolfCourse |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,77 @@ | ||
# golf-course | ||
A list of common Solidity optimization tips and myths. | ||
|
||
## Tips | ||
# Tips | ||
|
||
### Right Shift Instead of Dividing By 2 | ||
- - - - | ||
### 1. When dividing by two, use `>> 1` instead of `/ 2` ### | ||
|
||
```solidity | ||
/// 🤦 Unoptimized (gas: 1401) | ||
uint256 two = 4 / 2; | ||
|
||
/// 🚀 Optimized (gas: 1372) | ||
uint256 two = 4 >> 1; | ||
``` | ||
|
||
The `SHR` opcode is 3 gas cheaper than `DIV` and also bypasses Solidity's division by 0 prevention overhead. | ||
- [Gas Usage]() | ||
- [Full Example](https://github.com/Rari-Capital/golf-course/blob/fc1882bacfec50787d9e9435d59fed4a9091fb21/src/GolfCourse.t.sol#L15) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you mentioned we should link to the test, which is what I've done here, but I was also thinking maybe it would be good to link to the example code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea i think we should do that instead of test tbh wdyt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we could even break convention and put the example code in the test file... |
||
|
||
|
||
- - - - | ||
|
||
### 2. Use `>=` and `<=` instead of `>` and `<` ### | ||
```solidity | ||
/// 🤦 Unoptimized (gas: 1401) | ||
bool positive = 1 > 0; | ||
|
||
/// 🚀 Optimized (gas: 1337) | ||
bool positive = 1 >= 1; | ||
``` | ||
|
||
`if (x >= y) {}` is algebraically equivalent to !(x < y) which gets compiled to `LT .. JUMPI` which is more efficient than `x > y` which contains an extra `ISZERO` | ||
|
||
- [Gas Usage]() | ||
- [Full Example](https://github.com/Rari-Capital/golf-course/blob/fc1882bacfec50787d9e9435d59fed4a9091fb21/src/GolfCourse.t.sol#L23) | ||
|
||
- - - - | ||
|
||
### 3. Using `!=` is usually cheaper than `>` or `<` ### | ||
```solidity | ||
/// 🤦 Unoptimized (gas: 1423) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. haha, I couldn't get this "tip" to work ... maybe it's a myth? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think cuz of
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried it with dynamic values for both sides of the equation. Also tried different values (like zero and non zero) No matter what I do, I was unable to repro gas savings. Taking this one off the list as well althought I'd be happy to be proved wrong on any of these. Also, maybe because I'm using pragma 0.8.11??? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I figured it out. It only saves gas within a |
||
notThirtyTwo = 31 < 32; | ||
|
||
/// 🚀 Optimized (gas: 1426) | ||
notThirtyTwo = 31 != 32; | ||
``` | ||
Similar to `>=` and `<=` - the `!=` gets compiled without an extra ISZERO in many cases (One notable exception being `x != 0` gets compiled the same as `x > 0`) | ||
|
||
- [Gas Usage]() | ||
- [Full Example]() | ||
- [Full Example](https://github.com/Rari-Capital/golf-course/blob/fc1882bacfec50787d9e9435d59fed4a9091fb21/src/GolfCourse.t.sol#L31) | ||
|
||
|
||
|
||
- - - - | ||
|
||
### 4. Use `<address>.code.length` instead of assembly `extcodesize` to avoid variable overhead ### | ||
```solidity | ||
// Unoptimized: | ||
uint256 two = 4 / 2; | ||
/// 🤦 Unoptimized (gas: 1535) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In spite of the increased variable overhead, the gas is still cheaper using the assembly. This kinda goes against our idea of putting in controls to ensure that every "optimization" is less gas than the unoptimized version. Wdyt? Perhaps we could have separate section for optimizing for variable overhead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very possible its not a saving technique on its on, the savings do come in tho if u're accessing codesize via a library like Address.sol which has function call overhead associated. but yeah maybe not good for this guide then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removing it from tips, we'll put this on the back burner |
||
uint256 size; | ||
address address_ = address(this); | ||
assembly { | ||
size := extcodesize(address_) | ||
} | ||
return size; | ||
|
||
// Optimized: | ||
uint256 two = 4 >> 1; | ||
/// 🚀 Optimized (gas: 1582) | ||
return address(this).code.length; | ||
``` | ||
Solidity [0.8.1](https:///github.com/ethereum/solidity/blob/develop/Changelog.md#081-2021-01-27) implemented `<address>.code.length` so the assembly is no longer needed | ||
|
||
- [Gas Usage]() | ||
- [Full Example](https://github.com/Rari-Capital/golf-course/blob/fc1882bacfec50787d9e9435d59fed4a9091fb21/src/GolfCourse.t.sol#L39) | ||
|
||
- - - - | ||
|
||
## Myths | ||
# Myths |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.6; | ||
|
||
contract GolfCourse { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for now I've put all these tests in one contract in one file, but I believe you would like to do one contract/file per example - please confirm |
||
function unoptimizedDivideByTwo() external pure returns (uint256 two) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you think of the function naming? we could also consider a numbering system |
||
/// 🤦 Unoptimized | ||
two = 4 / 2; | ||
} | ||
|
||
function optimizedDivideByTwo() external pure returns (uint256 two) { | ||
/// 🚀 Optimized | ||
two = 4 >> 1; | ||
} | ||
|
||
function unoptimizedPreferGteLteOverGtLt() | ||
external | ||
pure | ||
returns (bool positive) | ||
{ | ||
/// 🤦 Unoptimized | ||
positive = 1 > 0; | ||
} | ||
|
||
function optimizedPreferGteLteOverGtLt() | ||
external | ||
pure | ||
returns (bool positive) | ||
{ | ||
/// 🚀 Optimized | ||
positive = 1 >= 1; | ||
} | ||
|
||
function unoptimizedUseCodeLength() external view returns (uint256) { | ||
/// 🤦 Unoptimized | ||
address address_ = address(this); | ||
uint256 size; | ||
assembly { | ||
size := extcodesize(address_) | ||
} | ||
return size; | ||
} | ||
|
||
function optimizedUseCodeLength() external view returns (uint256) { | ||
/// 🚀 Optimized | ||
return address(this).code.length; | ||
} | ||
|
||
function unoptimizedPreferNotEqualOverGtLt() | ||
external | ||
pure | ||
returns (bool notThirtyTwo) | ||
{ | ||
/// 🤦 Unoptimized | ||
notThirtyTwo = 31 < 32; | ||
} | ||
|
||
function optimizedPreferNotEqualOverGtLt() | ||
external | ||
pure | ||
returns (bool notThirtyTwo) | ||
{ | ||
/// 🚀 Optimized | ||
notThirtyTwo = 31 != 32; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.6; | ||
|
||
import "ds-test/test.sol"; | ||
|
||
import "./GolfCourse.sol"; | ||
|
||
contract GolfCourseTest is DSTest { | ||
GolfCourse course; | ||
|
||
function setUp() public { | ||
course = new GolfCourse(); | ||
} | ||
|
||
function testUnoptimizedDivideByTwo() public { | ||
assertEq(course.unoptimizedDivideByTwo(), 2); | ||
} | ||
|
||
function testOptimizedDivideByTwo() public { | ||
assertEq(course.optimizedDivideByTwo(), 2); | ||
} | ||
|
||
function testUnoptimizedPreferGteLteOverGtLt() public { | ||
assertTrue(course.unoptimizedPreferGteLteOverGtLt()); | ||
} | ||
|
||
function testOptimizedPreferGteLteOverGtLt() public { | ||
assertTrue(course.optimizedPreferGteLteOverGtLt()); | ||
} | ||
|
||
function testUnoptimizedPreferNotEqualOverGtLt() public { | ||
assertTrue(course.unoptimizedPreferNotEqualOverGtLt()); | ||
} | ||
|
||
function testOptimizedPreferNotEqualOverGtLt() public { | ||
assertTrue(course.optimizedPreferNotEqualOverGtLt()); | ||
} | ||
|
||
function testUnoptimizedUseCodeLength() public { | ||
assertTrue(course.unoptimizedUseCodeLength() > 0); | ||
} | ||
|
||
function testOptimizedUseCodeLength() public { | ||
assertTrue(course.optimizedUseCodeLength() > 0); | ||
} | ||
} |
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.
what do you think of showing the gas from the test in the comment here?
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.
love it, tho may get hard to maintain after sol upgrades etc...