7

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

1

5 Answers 5

3

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 gas
  • EXP costs 60 gas

I executed transaction on rinkeby. Have a look at GETH Trace for TxHash.

8
  • Do you mean the numbers e.g. 58211 doesn't match with 55k? Feb 16, 2018 at 21:19
  • 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
  • 1
    I 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
4

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.

1

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

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: 20
  • bar: 11
  • first: 1

The next slot will then consist of 1 byte for each of second through eighth i.e. 7 bytes.

0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.