Почему uint8 стоит больше газа, чем uint256?

contract A {
  uint8 a = 0;
}

стоит 20150 + 2000 газа при создании.

по сравнению с

contract A {
  uint a = 0;   // or uint256
}

стоимость 5050 + 2000 газа при создании

Странно, что переменная, занимающая меньше места для хранения, стоит больше газа. Почему это так?

Ответы (4)

EVM работает со словами 256 бит/32 байта (спорное конструктивное решение). Каждая операция основана на этих базовых единицах. Если ваши данные меньше, необходимы дальнейшие операции для уменьшения масштаба с 256 бит до 8 бит, поэтому вы видите увеличение затрат.

Кстати, если вы переключите «Подробности» в онлайн-компиляторе Solidity, он даст вам точный дамп сборки, откуда берутся дополнительные коды операций. У меня не было времени интерпретировать их, но если вы это сделаете и обнаружите что-то еще, я уверен, что команда Solidity будет рада добавить оптимизации, чтобы обойти их.

Да, я видел инструкцию по сборке. Я собирался изучить его дальше, если не смог получить здесь никаких ответов. Спасибо за ваш ответ. Странное решение для начала uint256. Следующий вопрос Интересно, всегда ли имеет смысл просто придерживаться uint256 и никогда меньше.
@uzyn Использование меньших значений для хранения имеет смысл, потому что запись в хранилище обходится дорого, а компилятор упаковывает несколько меньших аргументов. Для параметров функций и переменных памяти это имеет смысл только в том случае, если вы хотите ограничить диапазон значений: нет упаковки, поэтому это не дороже и не дешевле.
Когда мы создаем переменную uint32внутри a structвместо того , чтобы uint256помещать ее в массив, это стоит гораздо меньше газа.

Однако следует отметить, что в структуре uint8 ДЕЙСТВИТЕЛЬНО стоит меньше, чем традиционный uint, из-за функции плотной упаковки. Также убедитесь, что ваши uint находятся рядом с другими вашими uint, а байты рядом с байтами и т. д. Это еще больше увеличивает плотно упакованные функции.

Что вы подразумеваете под «убедитесь, что ваши uints находятся рядом с другими вашими uints, а байты — рядом с байтами и т. д.». Итак, в structследует uintsопределить один за другим и другие типы, а почему? @VoR0220
Он рекомендует это, потому что порядок, в котором вы объявляете свои переменные состояния, может повлиять на общее количество слотов хранения, используемых вашим контрактом. Если вы объявите 2 uint128, а затем uin256, это будет использовать 2 256-битных слота для хранения; 1 слот для 2 uint128 и 1 слот для uint256. Если вы объявите 1 uint128, затем uint256, затем uint128, ваш контракт будет использовать 3 слота для хранения, потому что он не может поместить среднюю переменную состояния uint256 в слот для хранения ни с одним из uint128, поэтому каждая переменная должна иметь собственный слот для хранения.

SOLC 0.4.18: теперь разница небольшая

https://ethfiddle.com/6lt852gx7K

Контракт A с uint8затратами 75414 на развертывание

Контракт B с uint256затратами 73867 на развертывание

Разница 1547 газ.

все еще верно в новейших версиях Solidity (одинаковая точная стоимость для 0.6.11 и 0.7.1)
На Solidity 0.8.10 развертывание Unit8 примерно на 100 газов дороже, чем uint256.

Обновление в декабре 2021 года:

Если вы протестируете что-то вроде этого: 5 переменных вместо 1, что может быть больше похоже на реальный сценарий с несколькими переменными, а не только с одной.

contract A {
  uint8 a = 0;
  uint8 b = 0;
  uint8 c = 0;
  uint8 d = 0;
  uint8 e = 0;
}

Тогда это дешевле, чем его версия uint256.

Из ремикса:

uint8: 69484газ uint256: 78420газ

Обновление от июня 2022 г. (0.8.15)

uint8: 83524газ uint256: 90183газ

Таким образом, вы экономите на газе, используя uint меньшего размера, как говорится в других комментариях, если это массивы, вы экономите больше.