company_banner

Вопрос на миллион

    Возможно, многие из вас задавались вопросом: как изменится поведение смарт-контракта, если его данные будут весить сотни мегабайт и хранить сотни тысяч или миллионы записей? Будут ли дорожать транзакции? Как это повлияет на сеть в целом? Будут ли одни типы переменных в solidity справляться с подобной задачей лучше, чем другие? Мы решили лично узнать ответы на эти вопросы и провести эксперимент в нашей приватной сети Ethereum, смоделировав описанные ситуации. Что из этого получилось читайте дальше в статье.


    Описание теста


    Мы хотели выяснить, как повлияет загрузка на один смарт-контракт миллиона транзакций и большого объёма данных.

    Замерялись такие параметры:

    • стоимость одной транзакции в kgas;
    • длительность создания одного блока;
    • размер одного блока;

    Параметры блокчейна

    В нашем распоряжении имелся приватный PoA-блокчейн с двумя авторизованными пишущими узлами и одним «пассивным», с которого и отправлялись транзакции.

    Все три узла были запущены на идентичных серверах:

    • Процессор: 2 Intel Xeon E5-2670 2,60 ГГц.
    • ОЗУ: 8 Гб.
    • ОС: Windows Server 2012 R2 Datacenter (64-битная).

    Описание смарт-контрактов

    Было создано два смарт-контракта. Один имел в качестве поля mapping bytes32 => bytes32, другой — одномерный массив bytes32. Каждый из контрактов содержал функцию, принимающую в качестве параметра значение bytes32 и сохраняющую это значение как элемент соответствующего mapping'a или массива (по сути, значение сохранялось в хранилище блокчейна).

    Краткое описание контракта с mapping'ом

    contract TesterMapping {
        mapping (bytes32 => bytes32) StoragedData;
     
        function Storing(bytes32 data) {
            if(StoragedData[data]!= "Y")
                StoragedData[data] = "Y";
        }
    }
    

    Краткое описание контракта с массивом

    Con tract TesterArray {
        bytes32[] StoragedArray;
     
        function Storing(bytes32 data) {
            for(uint256 i = 0; i < StoragedArray.length; i++) {
                if(StoragedArray[i] == data)
                    return;
            }
            StoragedArray.push(data);
        }
    }
    

    Ход и результаты тестирования


    Сперва тестировался смарт-контракт с полем-mapping'ом. Раз в секунду на него посылалась транзакция со случайным 32-байтовым значением. Из-за технических проблем тестирование заняло несколько больше времени, чем планировалось, и миллионная транзакция была отправлена спустя три недели после отправки первой. Затем был протестирован смарт-контракт с массивом.

    Mapping—контракт

    В течение всего эксперимента ни стоимость транзакции, ни размер блока не колебались на сколько-нибудь значимые величины. Каждая транзакция обрабатывалась практически мгновенно, как первая, так и миллионная. Длительность создания блока периодически менялась от 1 до 9 секунд, но всегда симметрично заданному в генезис-блоке значению в 5 сек (если между созданием блока n и n+1 проходила одна секунда, то блок n+2 появлялся через 9 сек), то есть средняя длительность создания блока оставалось равной 5 секундам. Однако какой-то закономерности в возникновении этих флуктуаций замечено не было и, возможно, это было связано с работой нашей сети или вспомогательным программным обеспечением серверов (антивирусы и прочее).

    Контракт с массивом

    В данном варианте из-за наличия цикла перебора массива (для поиска совпадающих значений) стоимость одной транзакции выросла с 40 тысяч KGas до более чем 2 миллионов KGas уже за первые две тысячи транзакций. При этом продолжительность обработки одной транзакции уже за первые несколько сотен транзакций стала больше длительности создания одного блока. Из-за этого размер одного блока за приблизительно 500 транзакций упал до минимального и больше не увеличивался, так как на один блок стала приходиться в лучшем случае одна транзакция. Длительность обработки очень быстро стала настолько большой, что после отправки всего трёх тысяч транзакций «разгребание» получившейся очереди заняло около четырёх часов, при этом работа сети была бы парализована (конечно, если у отправителя в «боевой» сети хватило бы средств оплачивать такое количество столь дорогих транзакций).

    Выводы



    • Размер mapping'а не влияет на быстродействие работы с ним или на стоимость транзакции (как минимум, до 1 миллиона элементов).
    • Виртуальная машина Solidity крайне неэффективна при работе с итерационными циклами.
    • Для работы с большим количеством записей лучше использовать mapping.

    К сожалению, мы не нашли способа определить, какой объем данных в хранилище занимают данные конкретного смарт-контракта при использовании mapping-массива. Возможно, кто-нибудь из читателей сможет подсказать свой вариант.

    Райффайзенбанк

    107,16

    Развеиваем мифы об IT в банках

    Поделиться публикацией
    Комментарии 11
      0
      Когда уже это исправят?
      safari OSX — take.ms/rWNTA
      chrome OSX — take.ms/1mbM8
        +2
        Какой объем хранилища занимает маппинг надо смотреть в исходниках клиента, который вы используете geth или parity. Конкретно изучать trie деревья, кодирование в RLP и код записи и чтения базы данных.
          0

          если не ошибаюсь, где-то в официальных доках Ethereum прямо говорится о квадратичной сложности создания массивов и рекомендуется их избегать (в смысле, длинных массивов)

            0
            Плюс, любой массив, не ограниченный сверху — это потенциальная дыра. Если количество элементов дорастет до некоторого большого значения, а внутри цикла будут потребляться вычислительные ресурсы, можно упереться в лимит по газу (больше определенного числа вычислений нельзя выполнить в транзакции). Запись значения в storage стоит десятки тысяч газа, hardlimit — вроде сейчас 8млн, так что особо много циклами не сделаешь. Только в вычислительных задачах, причем довольно простых. Поэтому смарт-контракт — это набор крайне легковесных функций
            0
            Виртуальная машина Solidity крайне неэффективна — на этом можно закончить. Вообще любой юзкейс отлично кодируется внутри кода блокчейна, смысла в виртуальной машине кодить что либо внутри своего приватном permissioned чейна ноль. Оверхед у ВМ адовый же.
              –1
              Возможно, кто-нибудь из читателей сможет подсказать свой вариант.

              Миллион дадите?
                0
                А что если использовать массив вот так:
                contract TesterBoolArray {
                  bool[2 ** (32 * 8)] StoragedArray;
                 
                  function Storing(bytes32 data) {
                    uint256 i = uint256(data);
                    if (!StoragedArray[i])
                      StoragedArray[i] = true;
                    }
                  }
                }
                

                ?
                +1
                Странный тест — поведение маппингов и массивов подробно описано в доке. Зачем статью из этого делать — неясно
                  0

                  Ну, собственно тесты и предназначены для того, чтобы проверить соответствие того, что написано в доке тому, что получилось в жизни. Ведь оно не всегда совпадает...

                    –1
                    А я вот никогда эти биткойны и блокчены не понимал.

                    В школе учили нас закону сохранения энергии — если где-то что-то прибывает, значит это где-то убывает. Если у меня появились 100 евро. значит они где-то пропали. Если я намыл золото в реке. значит его в ней стало меньше. А биткойны из воздуха майнятся? У кого они пропадают?

                    Не, думаю, Райффайзен и тут народ дурит…

                    image

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое