-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit with first 7 tested optimizations
- Loading branch information
Showing
20 changed files
with
531 additions
and
57 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegDontInitDefVal is IdRegUnop { | ||
function generateIds(uint256 numIds, address[] memory owners) external override { | ||
if(numIds != owners.length) | ||
revert NumIdsOwnersLengthMismatch(numIds, owners.length); | ||
|
||
// @audit don't initialize loop variable to zero as solidity | ||
// automatically initializes variable to their default value | ||
// note: test shows no gas savings but does eliminate useless code | ||
for(uint256 i; i<numIds; i++) { | ||
// read next id from storage | ||
uint256 newId = nextId; | ||
|
||
// first id should start at 1 | ||
if(newId == 0) newId = 1; | ||
|
||
// update the mapping | ||
idToOwner[newId] = owners[i]; | ||
|
||
// update storage to increment next id | ||
nextId = newId + 1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegInitPastDefVal is IdRegUnop { | ||
// @audit since first id is always 1 initialize | ||
// storage past default 0 value | ||
constructor() { | ||
nextId = 1; | ||
} | ||
|
||
function generateIds(uint256 numIds, address[] memory owners) external override { | ||
if(numIds != owners.length) | ||
revert NumIdsOwnersLengthMismatch(numIds, owners.length); | ||
|
||
for(uint256 i; i<numIds; i++) { | ||
// read next id from storage | ||
uint256 newId = nextId; | ||
|
||
// @audit don't need to check against 0 every time | ||
// as we now initialize past 0 | ||
|
||
// update the mapping | ||
idToOwner[newId] = owners[i]; | ||
|
||
// update storage to increment next id | ||
nextId = newId + 1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegCalldataArrayInput is IdRegUnop { | ||
constructor() { | ||
nextId = 1; | ||
} | ||
|
||
// @audit use `calldata` for read-only array inputs | ||
function generateIds(uint256 numIds, address[] calldata owners) external override { | ||
if(numIds != owners.length) | ||
revert NumIdsOwnersLengthMismatch(numIds, owners.length); | ||
|
||
for(uint256 i; i<numIds; i++) { | ||
// read next id from storage | ||
uint256 newId = nextId; | ||
|
||
// update the mapping | ||
idToOwner[newId] = owners[i]; | ||
|
||
// update storage to increment next id | ||
nextId = newId + 1; | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/04-read-write-storage-once/IdRegReadWriteStorageOnce.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegReadWriteStorageOnce is IdRegUnop { | ||
constructor() { | ||
nextId = 1; | ||
} | ||
|
||
function generateIds(uint256 numIds, address[] calldata owners) external override { | ||
if(numIds != owners.length) | ||
revert NumIdsOwnersLengthMismatch(numIds, owners.length); | ||
|
||
// @audit read `nextId` from storage once | ||
// read next id from storage | ||
uint256 newId = nextId; | ||
|
||
for(uint256 i; i<numIds; i++) { | ||
// update the mapping | ||
// @audit use cached `newId` to set idToOwner storage | ||
// then increment cached `newId` | ||
idToOwner[newId++] = owners[i]; | ||
} | ||
|
||
// @audit write final `newId` to `nextId` storage once | ||
nextId = newId; | ||
|
||
// instead of reading from and to writing `nextId` storage | ||
// during every loop iteration, we now only read and write | ||
// storage once which is cheaper | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegDelToDefVal is IdRegUnop { | ||
function resetId(uint256 id) external override { | ||
// @audit instead of writing the default value to storage, | ||
// use `delete` for a potential gas refund | ||
delete idToOwner[id]; | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/06-dont-cache-calldata-length/IdRegDontCacheCalldataLength.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegDontCacheCalldataLength is IdRegUnop { | ||
function getOwnersForIds(uint256[] calldata ids) external view override returns(address[] memory) { | ||
// @audit don't cache calldata length | ||
// allocate output array in memory | ||
address[] memory owners = new address[](ids.length); | ||
|
||
// populate output array | ||
for(uint256 i; i<ids.length; i++) { | ||
owners[i] = idToOwner[ids[i]]; | ||
} | ||
|
||
// return output array | ||
return owners; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnop} from "../IdRegUnop.sol"; | ||
|
||
contract IdRegUseNamedReturn is IdRegUnop { | ||
function getOwnersForIds(uint256[] calldata ids) external view override returns(address[] memory owners) { | ||
// @audit using named return, allocate output array in memory | ||
owners = new address[](ids.length); | ||
|
||
// populate output array | ||
for(uint256 i; i<ids.length; i++) { | ||
owners[i] = idToOwner[ids[i]]; | ||
} | ||
|
||
// @audit removed explicit `return` statement | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
interface IIdReg { | ||
// errors | ||
error NumIdsOwnersLengthMismatch(uint256 numIds, uint256 ownersLength); | ||
|
||
// public API | ||
// | ||
// view functions | ||
function nextId() external view returns(uint256); | ||
function idToOwner(uint256 id) external view returns(address); | ||
function getOwnersForIds(uint256[] calldata ids) external view returns(address[] memory); | ||
|
||
// non-view functions which change state | ||
function generateIds(uint256 numIds, address[] memory owners) external; | ||
function resetId(uint256 id) external; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import { IIdReg } from "./IIdReg.sol"; | ||
|
||
// unoptimized implementation | ||
contract IdRegUnop is IIdReg { | ||
// next available id | ||
uint256 public nextId; | ||
|
||
// mapping of valid ids to their owner | ||
mapping(uint256 id => address owner) public idToOwner; | ||
|
||
// creates `numIds` new valid ids in ascending | ||
// order from previously created ids | ||
// @audit external and public result in same gas cost, tested | ||
// manually as this would break compilation with existing structure | ||
function generateIds(uint256 numIds, address[] memory owners) external virtual { | ||
if(numIds != owners.length) | ||
revert NumIdsOwnersLengthMismatch(numIds, owners.length); | ||
|
||
for(uint256 i=0; i<numIds; i++) { | ||
// read next id from storage | ||
uint256 newId = nextId; | ||
|
||
// first id should start at 1 | ||
if(newId == 0) newId = 1; | ||
|
||
// update the mapping | ||
idToOwner[newId] = owners[i]; | ||
|
||
// update storage to increment next id | ||
nextId = newId + 1; | ||
} | ||
} | ||
|
||
// reset an id back to no owner | ||
function resetId(uint256 id) external virtual { | ||
idToOwner[id] = address(0); | ||
} | ||
|
||
// get owners for given list of ids | ||
function getOwnersForIds(uint256[] calldata ids) external view virtual returns(address[] memory) { | ||
// cache length | ||
uint256 idsLength = ids.length; | ||
|
||
// allocate output array in memory | ||
address[] memory owners = new address[](idsLength); | ||
|
||
// populate output array | ||
for(uint256 i; i<idsLength; i++) { | ||
owners[i] = idToOwner[ids[i]]; | ||
} | ||
|
||
// return output array | ||
return owners; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnopTest} from "../IdRegUnopTest.sol"; | ||
import {IdRegDontInitDefVal} from "../../src/01-dont-init-def-val/IdRegDontInitDefVal.sol"; | ||
|
||
// Optimizer ON, 10000 runs | ||
// ======================== | ||
// Pre : 142049 gas | ||
// forge test --optimizer-runs 10000 --match-contract IdRegUnopTest --match-test test_generateIds -vvv | ||
// Post : 142049 gas (no improvement) | ||
// forge test --optimizer-runs 10000 --match-contract IdRegDontInitDefValTest --match-test test_generateIds -vvv | ||
// | ||
// Optimizer OFF | ||
// ============= | ||
// Pre : 142325 gas | ||
// forge test --match-contract IdRegUnopTest --match-test test_generateIds -vvv | ||
// Post : 142325 gas (no improvement) | ||
// forge test --match-contract IdRegDontInitDefValTest --match-test test_generateIds -vvv | ||
// | ||
// Conclusion | ||
// ========== | ||
// `for(uint256 i=0;)` costs the same as `for(uint256 i;)` | ||
contract IdRegDontInitDefValTest is IdRegUnopTest { | ||
function setUp() external override { | ||
idReg = new IdRegDontInitDefVal(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnopTest} from "../IdRegUnopTest.sol"; | ||
import {IdRegInitPastDefVal} from "../../src/02-init-past-def-val/IdRegInitPastDefVal.sol"; | ||
|
||
// Optimizer ON, 10000 runs | ||
// ======================== | ||
// Pre : 142049 gas | ||
// forge test --optimizer-runs 10000 --match-contract IdRegUnopTest --match-test test_generateIds -vvv | ||
// Post : 124829 gas (12% cheaper) | ||
// forge test --optimizer-runs 10000 --match-contract IdRegInitPastDefValTest --match-test test_generateIds -vvv | ||
// | ||
// Optimizer OFF | ||
// ============= | ||
// Pre : 142325 gas | ||
// forge test --match-contract IdRegUnopTest --match-test test_generateIds -vvv | ||
// Post : 125105 gas (12% cheaper) | ||
// forge test --match-contract IdRegInitPastDefValTest --match-test test_generateIds -vvv | ||
// | ||
// Conclusion | ||
// ========== | ||
// Initializing values past their default is cheaper | ||
contract IdRegInitPastDefValTest is IdRegUnopTest { | ||
function setUp() external override { | ||
idReg = new IdRegInitPastDefVal(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
test/03-calldata-array-input/IdRegCalldataArrayInputTest.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.28; | ||
|
||
import {IdRegUnopTest} from "../IdRegUnopTest.sol"; | ||
import {IdRegCalldataArrayInput} from "../../src/03-calldata-array-input/IdRegCalldataArrayInput.sol"; | ||
|
||
// Optimizer ON, 10000 runs | ||
// ======================== | ||
// Pre : 124829 gas | ||
// forge test --optimizer-runs 10000 --match-contract IdRegInitPastDefValTest --match-test test_generateIds -vvv | ||
// Post : 124554 gas (0.22% cheaper) | ||
// forge test --optimizer-runs 10000 --match-contract IdRegCalldataArrayInputTest --match-test test_generateIds -vvv | ||
// | ||
// Optimizer OFF | ||
// ============= | ||
// Pre : 125105 gas | ||
// forge test --match-contract IdRegInitPastDefValTest --match-test test_generateIds -vvv | ||
// Post : 124827 gas (0.22% cheaper) | ||
// forge test --match-contract IdRegCalldataArrayInputTest --match-test test_generateIds -vvv | ||
// | ||
// Conclusion | ||
// ========== | ||
// Using `calldata` for array inputs is cheaper than `memory` | ||
contract IdRegCalldataArrayInputTest is IdRegUnopTest { | ||
function setUp() external override { | ||
idReg = new IdRegCalldataArrayInput(); | ||
} | ||
} |
Oops, something went wrong.