I am trying to pack tightly my structure data type.
The question that arose is how much storage size does the bool variable take?
And if we suppose that it will take 1 byte, if I put consecutive up to 8 bool variables, will the EVM try to pack them together in 1 byte?
-
5bool is nothing but uint8 internally.– Jitendra Kumar. BallaFeb 16, 2018 at 17:22
5 Answers
NOTE: This answer includes only experimental data
Please read @medvedev1088's answer before, because my answer is only the addition and some data from tests.
From my tests in remix I got this data:
- Creating 8-
true
-bool
struct
costs 58211 gas; 20k + 7 * 5k = 55k gas; - Creating 16-
true
-bool
struct
costs 100807 gas; 20k + 15 * 5k = 95k gas; - Creating 32-
true
-bool
struct
costs 186505 gas; 20k + 31 * 5k = 175k gas; - Creating 33-
true
-bool
struct
costs 206811 gas; 2 * 20k + 31 * 5k = 195k gas;
From this data it is clear that the maximal amount of bool's you can store in one slot is 32.
[Update 1]
You may noticed mismatches in numbers in those lines. Those mismatches are actually caused by some smaller fees payed for different opcodes (which are executed for every new bool in our struct
). Let me list the most costly ones:
SLOAD
costs 200 gasEXP
costs 60 gas
I executed transaction on rinkeby. Have a look at GETH Trace for TxHash.
-
-
I'm also wondering why they don't pack 256 bools into a single slot. Feb 16, 2018 at 21:20
-
1@medvedev1088 I agree. Those mismatches are linearly growing with the number of bools in the struct. But still would be nice to find out which opcodes exactly being executed. Feb 16, 2018 at 21:30
-
1I find etherscan's transaction tracer very useful when analysing gas rinkeby.etherscan.io/…. Would be nice to have something similar in Remix. There is debugger there but it doesn't show all steps with corresponding gas. Feb 16, 2018 at 21:43
-
1@medvedev1088 I executed transaction on rinkeby. Have a look here. Pretty much I used the code like one that you provided but without
memory ex
. Feb 16, 2018 at 21:54
I tested this simple contract:
pragma solidity 0.4.20;
contract Test {
struct Example {
bool v1;
bool v2;
bool v3;
bool v4;
bool v5;
bool v6;
bool v7;
bool v8;
}
Example example;
function test() public {
Example memory ex = Example({
v1: true,
v2: true,
v3: true,
v4: true,
v5: true,
v6: true,
v7: true,
v8: true
});
example = ex;
}
}
In the generated assembly there are 8 SSTORE
ops and they all point to the same storage slot. The gas for test()
is ~76k which is 21k + 20k + 5k * 7
. This confirms that the generated bytecode first writes to the slot and then updates it 7 times.
Adding more bools to the struct adds 5k gas to test()
. As @Roman Frolov noted in his answer up to 32 bools can fit into a single slot.
Note that a bool is a uint8 under the hood, this means it's using up 8 bits while it only needs 1 bit.
It's more efficient to pack multiple booleans in a uint256, and extract them with a mask. You can store 256 booleans in a single uint256 (in a struct, you can adjust the size of the uint to match what you need).
You can use the following pattern
function getBoolean(uint256 _packedBools, uint256 _boolNumber)
public view returns(bool)
{
uint256 flag = (_packedBools >> _boolNumber) & uint256(1);
return (flag == 1 ? true : false);
}
function setBoolean(
uint256 _packedBools,
uint256 _boolNumber,
bool _value
) public returns(uint256) {
if (_value)
_packedBools = _packedBools | uint256(1) << _boolNumber;
return _packedBools;
else
_packedBools = _packedBools & ~(uint256(1) << _boolNumber);
return _packedBools;
}
Correct, a bool
will use at least 1 byte of storage. This applies when storing a bool in a structs or the raw primitive type.
Note I say at least because storing a single bool
will use an entire 32 byte slot if there are no other variables or packing is not possible.
In your example foo
and bar
will take up the first 31 bytes of a slot. It will also pack the bool first
as 1 byte such that it now uses 32 bytes.
foo
: 20bar
: 11first
: 1
The next slot will then consist of 1 byte for each of second
through eighth
i.e. 7 bytes.
Results are different in solidity 0.6 and running on post Istanbul EVM (Istanbul included gas consumption logic update for storage operations). Using same sample contract as medvedev1088 suggested in a previous answer.
gas consumption is much smaller: 21512 gas for setting 8 bools.