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: function ordering #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cache/
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
compare :; forge test --match-contract $(c)Test
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,66 @@
# golf-course
# golf-course
A list of common Solidity optimization tips and myths.

## Table of contents
- [Tips](#tips)
- [Operators](#-operators)
- [Compiler](#-compiler)
- [Myths](#myths)

## Tips

### Right Shift Instead of Dividing By 2
### ➗ Operators

#### Right Shift Instead of Dividing By 2

The `SHR` opcode is 3 gas cheaper than `DIV` and also bypasses Solidity's division by 0 prevention overhead.

- [Gas Usage]()
- [Full Example]()

```solidity
// Unoptimized:
// 🚩 Unoptimized
uint256 two = 4 / 2;

// Optimized:
// 🏌️ Optimized
uint256 two = 4 >> 1;
```

### 🤖 Compiler

#### Function ordering

The compiler orders public and external members of a contract by their Method ID.

You can get the Method ID of a function as follows:

```solidity
bytes4 methodId = bytes4(keccak256("<function_signature>"));
```

Calling a function at runtime will be cheaper if the function is positioned earlier in the order (has a relatively lower Method ID) because 22 gas are added to the cost of a function for every position that came before it. The caller can save on gas if you prioritize most called functions.

[This tool](https://emn178.github.io/solidity-optimize-name/) helps you find alternative function names with lower Method IDs.

- [Gas Usage]()
- [Full Example]()

```solidity
// 🚩 Unoptimized
// Method ID: 0x13216062 (position: 1, gas: 98)
bytes32 public occasionallyCalled;
// Method ID: 0xd0755f53 (position: 3, gas: 142)
function mostCalled() external {}
// Method ID: 0x24de5553 (position: 2, gas: 120)
function leastCalled() external {}

// 🏌️ Optimized
// Method ID: 0x13216062 (position: 2, gas: 120)
bytes32 public occasionallyCalled;
// Method ID: 0x0000a818 (position: 1, gas: 98) 👈
function mostCalled_41q() external {}
// Method ID: 0x24de5553 (position: 3, gas: 142)
function leastCalled() external {}
```

## Myths
13 changes: 13 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[default]
src = 'src'
out = 'out'
libs = ['lib']
fuzz_runs = 256
optimizer = true
optimizer_runs = 1000000
remappings = [
'ds-test/=lib/ds-test/src/',
]
verbosity = 3

# See more config options https://github.com/gakonst/foundry/tree/master/config
1 change: 1 addition & 0 deletions lib/ds-test
Submodule ds-test added at 0a5da5
27 changes: 27 additions & 0 deletions src/FunctionOrdering.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

contract UnoptimizedFunctionOrdering {
// Method ID: 0x13216062
bytes32 public occasionallyCalled;

// 🚩 Unoptimized
// Method ID: 0xd0755f53
function mostCalled() external {}

// Method ID: 0x24de5553
function leastCalled() external {}
}

contract OptimizedFunctionOrdering {
// Method ID: 0x13216062
bytes32 public occasionallyCalled;

// 🏌️ Optimized
// Method ID: 0x0000a818
function mostCalled_41q() external {}

// Method ID: 0x24de5553
function leastCalled() external {}
}
7 changes: 7 additions & 0 deletions src/Operators.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

contract Operators {
// function unoptimizedDivideByTwo() external pure returns (uint256 two) { ...
}
34 changes: 34 additions & 0 deletions src/test/FunctionOrdering.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

import {UnoptimizedFunctionOrdering, OptimizedFunctionOrdering} from "src/FunctionOrdering.sol";
import "ds-test/test.sol";

contract FunctionOrderingTest is DSTest {
UnoptimizedFunctionOrdering unoptimizedFunctionOrdering;
OptimizedFunctionOrdering optimizedFunctionOrdering;

function setUp() public {
unoptimizedFunctionOrdering = new UnoptimizedFunctionOrdering();
optimizedFunctionOrdering = new OptimizedFunctionOrdering();
}

function testUnoptimizedFunctionOrdering() public {
unoptimizedFunctionOrdering.mostCalled();
unoptimizedFunctionOrdering.mostCalled();
unoptimizedFunctionOrdering.mostCalled();
unoptimizedFunctionOrdering.occasionallyCalled();
unoptimizedFunctionOrdering.occasionallyCalled();
unoptimizedFunctionOrdering.leastCalled();
}

function testOptimizedFunctionOrdering() public {
optimizedFunctionOrdering.mostCalled_41q();
optimizedFunctionOrdering.mostCalled_41q();
optimizedFunctionOrdering.mostCalled_41q();
optimizedFunctionOrdering.occasionallyCalled();
optimizedFunctionOrdering.occasionallyCalled();
optimizedFunctionOrdering.leastCalled();
}
}
10 changes: 10 additions & 0 deletions src/test/Operators.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.4;

import {Operators} from "src/Operators.sol";
import "ds-test/test.sol";

contract OperatorsTest is DSTest {
// ...
}