Gas Optimizations #39
Labels
bug
Something isn't working
G (Gas Optimization)
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Summary
Gas savings are estimated using the gas report of existing
FORGE_GAS_REPORT=true forge test
tests (the sum of all deployment costs and the sum of the costs of calling all methods) and may vary depending on the implementation of the fix. I keep my version of the fix for each finding and can provide them if you need.Some optimizations (mostly logical) cannot be scored with a exact gas quantity.
Gas Optimizations
modifier
withfunction
storage
pointer to a structure is cheaper than copying each value of the structure intomemory
, same forarray
andmapping
private
rather thanpublic
for constants, saves gastype
instead of astruct
that has only one member<x> = <x> + 1
even more efficient than pre incrementreturns
for local variables where it is possiblex = x + y
is cheaper thanx += y;
revert
operator should be in the code as early as reasonably possibleTotal: 83 instances over 15 issues
Replace
modifier
withfunction
(6 instances)modifiers make code more elegant, but cost more than normal functions
Deployment Gas Saved: 460 154
All modifiers except
permissioned
due to unresolved error flowstorage
pointer to a structure is cheaper than copying each value of the structure intomemory
, same forarray
andmapping
(7 instances)Deployment Gas Saved: 188 639
Method Call Gas Saved: 5 032
It may not be obvious, but every time you copy a storage
struct
/array
/mapping
to amemory
variable, you are literally copying each member by reading it fromstorage
, which is expensive. And when you use thestorage
keyword, you are just storing a pointer to the storage, which is much cheaper.fix(the same for others):
Using
private
rather thanpublic
for constants, saves gas (8 instances)If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table
Deployment Gas Saved: 45 857
Method Call Gas Saved: 308
Use elementary types or a user-defined
type
instead of astruct
that has only one member. (1 instances)Deployment Gas Saved: 30 714
Method Call Gas Saved: 1 037
State variables should be cached in stack variables rather than re-reading them from storage
Deployment Gas Saved: 24 021
Method Call Gas Saved: 614
SLOADs are expensive (100 gas after the 1st one) compared to MLOADs/MSTOREs (3 gas each). Storage values read multiple times should instead be cached in memory the first time (costing 1 SLOAD) and then read from this cache to avoid multiple SLOADs.
fix:
fix(similar for other policies):
Using bools for storage incurs overhead (6 instances)
Deployment Gas Saved: 23 611
Method Call Gas Saved: 4 485
Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past
Important: This rule doesn't always work, sometimes a bool is packed with another variable in the same slot, sometimes it's packed into a struct, sometimes the optimizer makes bool more efficient. You can see the @note in the code for each case
Expensive method calls:
It's just to show which bool is better left in the code
State variables can be packed into fewer storage slots (3 instances)
If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables can also be cheaper
NOTE: one slot = 32 bytes
Deployment Gas Saved: 23 292
Method Call Gas Saved: 1 711
uint256(32), address(20), bool(1)
fix:
NOTE: PRICE is Module, Module is KernelAdapter, so real first variable in PRICE is kernel from KernelAdapter
uint256(32), uint32(4), uint48(6), uint8(1), array(32), address(20), bool(1)
fix:
uint32(4), uint8(1), address(20), bool(1)
fix:
Expressions that cannot be overflowed can be unchecked (5 instances)
Deployment Gas Saved: 23 016
Increment optimization (18 instances)
For a uint256 i variable, the following is true with the Optimizer enabled at 10k:
Increment:
i += 1 is the most expensive form
i++ costs 6 gas less than i += 1
++i costs 5 gas less than i++ (11 gas less than i += 1)
Decrement:
i -= 1 is the most expensive form
i-- costs 11 gas less than i -= 1
--i costs 5 gas less than i-- (16 gas less than i -= 1)
Deployment Gas Saved: 400
NOTE: in case of 670 675 686 691 not applicable and gas will be lost
<x> = <x> + 1
even more efficient than pre increment.(18 instances)Deployment Gas Saved: 14 217
Use named
returns
for local variables where it is possible (3 instances)Deployment Gas Saved: 5 400
fix:
x = x + y
is cheaper thanx += y;
(6 instances)Deployment Gas Saved: 5 000
Usually does not work with struct and mappings
Deleting a struct is cheaper than creating a new struct with null values. (1 instances)
Deployment Gas Saved: 4 207
Method Call Gas Saved: 40
fix:
delete activeProposal;
Don't compare boolean expressions to boolean literals (2 instances)
Deployment Gas Saved: 1 607
revert
operator should be in the code as early as reasonably possible (3 instances)Deployment Gas Saved: 200
Method Call Gas Saved: 1 559+
Duplicated require()/revert() checks should be refactored to a modifier or function
Method Call Gas Saved: 8 111
The text was updated successfully, but these errors were encountered: