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

feat: adds a few more tips #1

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3afa909
✨ Add a few more tips
devtooligan Dec 14, 2021
0d796cb
docs: update descriptions and formatting
devtooligan Dec 15, 2021
5b1cad0
dapp init GolfCourse
devtooligan Dec 18, 2021
244182e
dapp install ds-test
devtooligan Dec 18, 2021
fc1882b
feat: adds examples and tests
devtooligan Dec 18, 2021
889b45f
docs: update readme.md with links and gas
devtooligan Dec 18, 2021
fb1f401
feat: work on formatting and add a bunch of examples
devtooligan Feb 13, 2022
48b3b6e
fix: typo
devtooligan Feb 14, 2022
d33debe
feat: update comments
devtooligan Feb 16, 2022
b814df2
feat: add RequireNeZero tip
devtooligan Feb 16, 2022
f45ed35
tweak: update gitignore
devtooligan Feb 16, 2022
70d07b7
fix: update directory structure to eliminate differences in contract …
devtooligan Feb 16, 2022
5606811
fix: update gas usage from new ctrct names
devtooligan Feb 16, 2022
52b02b0
fix: update links in readme
devtooligan Feb 16, 2022
00d2a4f
feat: add payable functions tip
devtooligan Feb 16, 2022
c4cfffb
fix: format code in markdown
ZeroEkkusu Feb 16, 2022
fc0707f
docs: add more info to "Make functions `payable`"
ZeroEkkusu Feb 16, 2022
d4dabe3
Merge pull request #4 from ZeroEkkusu/work-1
devtooligan Feb 16, 2022
da9e4de
fix: add tests
devtooligan Feb 17, 2022
bfac3ce
Merge branch 'some-tips-to-start' of github.com:Rari-Capital/golf-cou…
devtooligan Feb 17, 2022
964c888
docs: update explanation
devtooligan Feb 17, 2022
7b32928
feat: new tip re: array +=
devtooligan Feb 17, 2022
6bb2a4a
feat: ArayPlus contracts
devtooligan Feb 17, 2022
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sol linguist-language=Solidity
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/out
3 changes: 3 additions & 0 deletions .gitmodules
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
4 changes: 4 additions & 0 deletions Makefile
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
72 changes: 64 additions & 8 deletions README.md
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)
Copy link
Collaborator Author

@devtooligan devtooligan Dec 18, 2021

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?

Copy link
Contributor

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...

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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea i think we should do that instead of test tbh wdyt

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think cuz of

  • function overhead in dapptools tests
  • we're using constants and not dynamic values so the compiler can optimize at compile time

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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???

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I figured it out. It only saves gas within a require()

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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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
1 change: 1 addition & 0 deletions lib/ds-test
Submodule ds-test added at 0a5da5
66 changes: 66 additions & 0 deletions src/GolfCourse.sol
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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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) {
Copy link
Collaborator Author

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 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;
}

}
46 changes: 46 additions & 0 deletions src/GolfCourse.t.sol
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);
}
}