Categories
All Posts

Storage vs. Memory vs. Stack in Solidity & Ethereum

In this article we focus on the difference between storage, memory, and stack in Ethereum. We will also give a Solidity code sample and calculation examples.

The Ethereum Virtual Machine (EVM) can store data in three different places:

  1. Storage
  2. Memory
  3. Stack

Storage

Data in storage are stored permanently. The storage is a key value store.

Data in the storage are written in the blockchain (hence they change the state), are available between function calls and transactions. They can also be read „manually“ from the blockchain.

That’s why storage is very expensive to use. To occupy a 256 Bit slot costs 20,000 gas. Changing a value of an already occupied slot costs 5,000 gas. When clearing a storage slot a certain amount of gas is refunded.

Also reading from the storage is quite expensive.

As a consequence storage should only be used, if really necessary.

Storage saves data in fields of 256 bit size (32 Byte = word). Cost occur for every used slot, even if it is not fully occupied. So, a 8 bit value is stored in a 256 bit slot and needs to pay for the whole slot. In order to save storage and gas costs many smaller variables can be packed into one 256 slot.

For example, we have the following variables:

  • uint8 var1
  • uint32 var2
  • uint64 var3
  • uint128 var4
  • uint128 var5

Together var1 till var4 occupy 232 bit of space. Hence, they could be stored in one 256 bit slot. var5 however would not fit in anymore and will be stored in a separate 256 bit slot.

The order of packing is determined by the order of occurrence of the variables in the smart contract.

Memory

Memory is a byte array with slot sizes of 256 bit (32 byte). Here data are stored only during function execution. After that they are deleted. They are not saved to the blockchain. Due to its short term nature, memory is compared to storage cheap. Reading or writing a word (256 bit) costs 3 gas. In order to avoid too much work for the miners the costs per operation start to rise after 22 operations.

Additionally, to he read and write costs are expansion costs. They are to be paid whenever additional hitherto unused memory is required. These costs are linear up to 725 bytes and rise after that disproportionally.

The exact formula for read and write operations can be found in the Ethereum yellow paper. (page 26, formula 298)

Formula Memory Costs

Explanation of the symbols:

a: number of occupied 256-bit-memory slots

≡: Congruency

⌊ ⌋: Floor function (⌊2.8 ⌋ = 2)

Calculation examples

a = 1

Cmem(a) ≡ 3*1+⌊1^2/512⌋=3*1+0=3

Average gas costs = 3/1 = 3

a=22

Cmem(a) ≡ 3*1+⌊22^2/512⌋=3*22+0=66

Average gas costs = 66/22 = 3

a=23

Cmem(a) ≡ 3*1+⌊23^2/512⌋=3*23+1=70

Average gas costs = 70/23 = 3.0435

a=512

Cmem(a) ≡ 3*1+⌊512^2/512⌋=3*512+512=2048

Average gas costs = 2048/512 = 4

a=1024

Cmem(a) ≡ 3*1+⌊1024^2/512⌋=3*1024+2048=5120

Average gas costs = 2048/512 = 5

Blue line: total costs for write and read operations of memory (with constant memory size).

Grey line: virtual costs with linear increase of write and read operations.

Stack

The stack is an internal place where temporary variables are stored 32 bit slots. It is usually used for value types in functions. The stack holds those variables which are necessary for immediate processing. It can comprise 1024 values but only the upper 16 ones are easily accessible.

Stack manipulation can only be done by inline assembly. The cost for stack operations are usually lower than those for memory but in some cases can also be higher. For most programmers stack manipulation is not necessary and should be left to the compiler.

Which variables get stored where?

Here we clarify where the Ethereum Virtual Machine stores variables.

  • State variables (declared directly in the smart contract) always storage.
  • Function arguments. Always memory (with array types like string this needs to be stated explicitly)
  • Local variables (in functions) of the types struct, array or mapping are stored either in memory or storage. The place needs to be stated explicitly when declaring the variable.
  • Local variables (in functions) of value types (e. g. uint32) always in stack

Meaning of Storage and Memory for Solidity/Ethereum Programming

Developers have only little influence on where a variable is stored. Though it is in some cases necessary to know the difference. This touches two fields:

  1. Cost: see cost optimization in solidity
  2. Unexpected behavior: See storage pointers

This example shows a behavior which might be not expected.

 pragma solidity ^0.7.1;
contract StorageTest { 
 uint[10] public myArray;
 function function1() public {
  memoryFunction(x); 
  storageFunction(x);
 }
 function memoryFunction (uint[10] memory y) internal pure {
  y[2] = 3;
 }
 function storageFunction (uint[10] storage y) internal { 
  y[2] = 4;
 }
}

If function storageFunction is called, no new memory variable is created. Instead a storage pointer points to “myArray”. The consequence is that myArray[2] == 4.

The example was found here: https://medium.com/loom-network/ethereum-solidity-memory-vs-storage-how-to-initialize-an-array-inside-a-struct-184baf6aa2eb

Further links