Wasm Hot Reloading Experiment: опровержение предположений и как указать, где находится раздел данных?

Во-первых, чтобы это не выглядело как проблема XYZ, я хотел бы дать некоторый контекст (обратите внимание, что я не использую Emscripten):

Я пытаюсь понять, смогу ли я реализовать некую форму горячей перезагрузки для программ Wasm, написанных на C ++, размещенных в Интернете. Для этого мне нужен раздел памяти, который я называю своим «мировым состоянием» (для всех, кто смотрел Handmadehero.org/" rel="nofollow noreferrer"> https://handmadehero.org/), это будет вам знакомо):

struct State {
// put everything here
} state;

Обычно для полной программы C ++ со слоем платформы вы выделяете эту структуру на стороне платформы и передаете указатель на эту память через указатель функции в перезагружаемой части кода / dll / dylib. Перезагружаемый код помещает ВСЕ в эту постоянную память, поэтому, если код необходимо перекомпилировать и перезагрузить, все состояние будет продолжать существовать, поскольку память была выделена в той части программы, которая не была перезагружена. Насколько я могу судить, в Wasm это невозможно. Во-первых, верно ли мое предположение, что я должен использовать WebAssembly.Memory? - или я могу выделить uint8array в js и использовать его для своего постоянного состояния отдельно от программной памяти? Если да, то это медленнее?

Так что это будет работать до тех пор, пока я не использую динамический распределитель, такой как WASI, а вместо этого использую распределитель push, которым я могу управлять. (Я думаю, это потому, что, предположим, я использую malloc для получения адресов памяти и перезагрузки - внутреннее состояние malloc будет перезагружено и будет думать, что вся память кучи доступна, когда ее нет, поэтому будущие выделения могут затмить предыдущие.) После перезагрузки я могу сначала скопируйте структуру во временный буфер на стороне js, перезагрузите, получите местоположение структуры в памяти из Wasm (мне потребуется, чтобы она существовала) и скопируйте сохраненную память из js обратно в позицию.

Однако это развалится, если я использую указатели, потому что, если я изменю программу (в этом суть) __data_end может измениться, что приведет к смещению всех адресов! Я проверил флаги компоновщика здесь https://lld.llvm.org/WebAssembly.html, чтобы посмотреть, что я могу контролировать. Я могу указать, что стек идет перед сегментом данных, но куча все равно будет после этого, что приводит к той же проблеме. Я также могу указать, где расположены глобальные данные, но я считаю, что это не тот сегмент данных, поэтому сегмент данных переменного размера может по-прежнему компенсировать все мои адреса. Вот хорошая страница, которая может помочь нам визуализировать память Wasm: https://dassur.ma/things/c-to-webassembly/

Есть ли у кого-нибудь мысли о том, как добиться того, чего я хочу? Единственные варианты, о которых я могу думать, включают использование памяти вне памяти Wasm (возможно, более медленное или невозможное), с использованием только стековой памяти и без указателей (нереально, если я не могу автоматически пересчитать все смещения указателей после перекомпиляции, что было бы болезненно и ошибочно -prone) или найти способ сделать так, чтобы сегмент данных шел после стека и кучи по фиксированному адресу, что тогда гарантировало бы, что сегменты стека и кучи не будут смещены, если сегмент данных должен расти. Другой вариант, если это возможно, - исправить максимальный размер сегмента данных. Спецификация / документация Wasm не очень хороша, когда дело доходит до подобного рода манипуляций с памятью, поэтому я также был бы признателен за некоторые пояснения относительно того, что возможно. Наконец, может быть, я мог бы использовать два модуля Wasm (но разве такое косвенное обращение не будет медленным)? Возможно, мне не хватает чего-то важного, связанного с разметкой памяти.

Пожалуйста, дайте мне знать, если вам понадобится дополнительная информация. Как я уже упоминал, я уже делал что-то подобное на C, и это обычная техника быстрой итерационной разработки игр. В основном я пытаюсь воссоздать это в Wasm.

РЕДАКТИРОВАТЬ: По-видимому, вы можете напрямую вызывать функции Wasm из другого модуля. Во-первых, как вы это делаете, а во-вторых, каковы будут характеристики производительности для доступа к памяти другого модуля?

EDIT2: Может быть, какая-то форма динамического связывания, если она поддерживается? https://webassembly.org/docs/dynamic-linking/


person synchronizer    schedule 14.01.2020    source источник


Ответы (2)


Модули WebAssembly хранят состояние переменных в трех разных местах:

  • Линейная память
  • Локальные переменные, связанные со стеком выполнения
  • Глобальные переменные

Из них только глобальные переменные и линейная память доступны для среды хоста и потенциально могут быть сериализованы для их кэширования при горячей перезагрузке модуля. Конечно, нет возможности напрямую получить доступ к текущему стеку вызовов и сохранить его.

Если бы я хотел добиться этого, я бы создал свой собственный конечный автомат в WebAssembly, сохраняя его в известном месте в линейной памяти.

person ColinE    schedule 14.01.2020
comment
Хорошо, но это не дает прямого ответа на мои опасения. Я не хочу сериализовать что-либо, кроме моей структуры данных, но переменный размер раздела данных может смещать адреса, что может стать проблемой. Тогда ни одно из мест не будет «известно», да? Есть ли идея предположить, что раздел данных будет иметь определенный максимальный размер (даже если его нет) и просто начнет кучу по определенному известному адресу? Затем я бы создал свой собственный malloc, который «знает» это. Это было бы намного проще, если бы я мог контролировать расположение раздела данных. Помогло бы, если бы я нарисовал несколько картинок? - person synchronizer; 14.01.2020
comment
Кроме того, чтобы уточнить, я просто хочу знать, можно ли указать, где раздел данных / глобальные данные находятся в памяти. По умолчанию: [данные | стек | куча]. Я знаю, что вы можете указать компоновщику сделать это: [стек | данные | куча], но мне нужно следующее, где размер кучи постоянен в моем случае использования: [стек | куча | данные]. Еще лучше было бы, если бы данные можно было разместить в другом модуле. - person synchronizer; 14.01.2020

Wasm разбит на модули, а модули определяют четыре соответствующих типа сущностей: функции, память, таблицы, глобальные объекты. Код находится в функциях, а остальные три представляют состояние модуля.

Интересно то, что все четыре типа сущностей можно импортировать и экспортировать. Более того, все они могут быть созданы вне модуля, например, с помощью JS API.

Следовательно, способ эмуляции подкачки кода - это настроить модуль так, чтобы все три части состояния создавались извне и импортировались в модуль. Таким образом, вы можете поддерживать их в рабочем состоянии извне и передавать их обновленному модулю, когда он станет доступным. (Вам также необходимо убедиться, что обновленный модуль не использует сегменты данных / элементов или функции запуска таким образом, чтобы перекрыть уже существующее состояние.)

Конечно, это работает только в том случае, если форма состояния модуля не меняется между обновлениями. Например, никаких новых глобальных объектов, никакого нового макета данных в памяти, иначе новый код не поймет старое состояние. На самом деле это самая сложная часть проблемы, но она не зависит от специфики Wasm.

person Andreas Rossberg    schedule 15.01.2020
comment
Самая сложная проблема - это указатели. Если размер раздела данных изменится, то куча будет смещена, поэтому, если ранее куча начиналась с адреса A, и у меня был указатель на A, у меня были бы проблемы, если бы размер раздела данных увеличился, потому что теперь мои указатели будут направлено на что-то в разделе данных. Есть ли способ разместить раздел данных после кучи или можно ограничить размер раздела данных? В противном случае я мог бы предположить максимальный размер раздела данных и после этого запустить свою виртуальную кучу. - person synchronizer; 15.01.2020
comment
Боюсь, это только верхушка айсберга проблемы. Но в любом случае Wasm ничего не знает о ваших данных или куче. Это просто дает вам линейное пространство памяти, организация которого зависит от вас. - person Andreas Rossberg; 16.01.2020
comment
Я думал, что раздел данных, стек и куча все еще находятся в определенных регионах, как показано здесь: dassur.ma/things/c-to-webassembly (прокрутите вниз до изображения.) - person synchronizer; 16.01.2020
comment
@synchronizer, именно так LLVM использует память (и аналогично другие компиляторы / среды выполнения), но сам Wasm не заботится. - person Andreas Rossberg; 16.01.2020
comment
Итак, я предполагаю, что вопрос в том ... есть ли какой-то скрытый способ получить clang и компоновщик, чтобы поместить раздел данных в другое место? Я могу только найти флаг, чтобы сначала переместить стек. Похоже, мне придется подождать, пока не будет добавлено динамическое связывание. Затем (если я правильно понимаю) я мог бы иметь один постоянный модуль, обрабатывающий память и разделяющий ее (надеюсь?) Бесплатно с перезагружаемым модулем. - person synchronizer; 16.01.2020
comment
Я готов поспорить, что не существует практического способа сделать так, чтобы LLVM поддерживала безопасное обновление горячего кода, для Wasm или как-то иначе. Это сверхсложная проблема, и в таком сложном компиляторе, который даже не предназначен для этого, слишком много движущихся частей. Марк Миллер прекрасно об этом говорит: erights.org/data/serial/jhu- paper / upgrade.html - person Andreas Rossberg; 16.01.2020
comment
Что ж, если бы у меня был подход с двумя модулями, это сработало бы. Я думаю, что это содержится в предложении по динамической компоновке, но не уверен. - person synchronizer; 16.01.2020