Pull to refresh
85
Karma
0
Rating
Александр Слесарев @nuald

Разработчик

  • Followers 11
  • Following 2

Сравнение встраиваемых ЯП по размеру в исполняемом файле

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

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Добавил тест и обновил статью. Документации достаточно мало, плюс нет API для взаимодействия, поэтому написал небольшой менеджер памяти для обмена данных (у меня совсем вылетело из головы, что в WASM-е строк и вообще работы с памятью толком нет, и многие решения основаны на том, что все запускается в JS).


В принципе, качество работы вполне удовлетворительное (правда, C++ биндинги подкачали, но это дело наживное). Будем обсуждать с коллегами, но лично я вижу два главных препятствия для того, чтобы wasm3 стал первичным кандидатом для нас:


  • большой расход памяти (проверил valgrind-ом, утечек нет, просто расход большой);
  • [не специфично для wasm3] требуется большой рантайм в самом WASM для хорошей функциональности (я попробовал два варианта: 1) no_std дал размер в несколько десятков байт, но весьма скудная функциональность; 2) полноценный Rust добавил сразу большой рантайм, и хотя стало комфортно программировать, WASM-файл занял несколько сотен килобайт ). Я пытался урезать, но толку мало, все-равно достаточно большие файлы без no_std.

Конечно, компилятор Rust не единственный, можно и Emscripten, но он тоже добавляет свой рантайм, а функциональность будет намного хуже чем в Rust. Впрочем, к самому проекту это отношения не имеет, это отдельная история, и можно рассмотреть те же v-lang, nim и т.п. Дополнительно остается, конечно, вопрос производительности, но этим я буду заниматься в рамках других проектов. Еще раз спасибо за ссылку, при всех сомнениях wasm3 все-равно остается хорошим кандидатом для встраивания.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Спасибо за ссылку, действительно интересный проект. Судя по всему, он удовлетворяет нас по всем требованиям, и есть возможность биндинга нативных функций, пусть даже еще не задокументированная (https://github.com/wasm3/wasm3/pull/71). Насчет прода я не особо переживаю, если проект стоящий, то можно найти ресурсы или спонсоров, чтобы его допилили.


Я постараюсь включить его в тесты, и дам вам знать результаты.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Добавил Python (с примером использования Cython: https://gitlab.com/nuald-grp/embedded-langs-footprint#user-content-using-cython ) и MicroPython. Как я и предполагал, Python достаточно тяжелый (и тестовая программа даже не включала всю стандартную библиотеку, а только базовый необходимый набор).


К моему удивлению, MicroPython очень тяжело встраивать, пришлось использовать различные трюки, чтобы заставить его работать без написания больших Makefile-ов.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Мы поддерживаем компиляцию в WASM, но внедрение его VM — это совершенная другая история. Мы рассматривали это, но не подошло, в частности из-за того, что не нашли подходящую под наши требования VM. Все, что мы нашли, были реализованы:


  • как надстройка к node.js -> V8. Слишком тяжелый рантайм, плюс сборка V8 — это отдельное приключение (возможно, Blink собирается проще, но все-равно тяжеловесный);
  • на Go (например, https://github.com/perlin-network/life). Go добавляет свой собственный рантайм, плюс может иметь проблемы с биткодом (https://github.com/golang/go/issues/22395 — возможно, это решено сейчас, но зная Apple, они могут это сломать в любой момент)
  • на Rust (например, https://github.com/bytecodealliance/wasmtime). Проблем с рантаймом нет, но зато есть с биткодом (https://github.com/rust-lang/rust/issues/35968)
  • компилируют на ходу (например, https://github.com/WAVM/WAVM). Включают очень тяжелый LLVM.

К сожалению, мы не нашли реализацию на чистом C/C++, но если вы знаете какую-либо, дайте знать пожалуйста.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

tcc уже несколько лет как фактически заморожен, т.к. его автор больше над ним не работает (https://bellard.org/tcc/):


[Note: I am no longer working on TCC. Check the mailing list to get up to date information.]

Плюс он компилирует в машинный код, что нам не особо подходит (например, не будет работать в WASM VM).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

GPL-лицензия, не подходит для коммерческого использования.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Object Pascal не является таким переносимым, как Си — например, так просто скомпилировать его в WASM, или для микроконтроллеров (как вышеупомяный stm32) вряд ли получится (даже если решения есть, они не такие распространенные, и могут иметь проблемы).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Думаю, лучше привести примеры кода (JS):


function f1() {}
function f2() {}

Можно объединить без разделителей (а ";" в выражениях и так нужна, хотя она и опциональная): function f1() {}function f2() {}
В Scheme/Lisp вообще нет разделителей:


(define a 1)
(define b 1)

Объединяются в одну строку: (define a 1)(define b 1)


Так что все зависит от ЯП.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Под «чувствителен к новым строкам» я подразумеваю, что если полность удалить переносы строк, код перестанет работать. Замены на пробелы потенциально может работать, но насколько я понимаю, это особенность Lua (признаюсь, про которую я не знал).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

DOM — это отдельная история, переносы строк на сам рендеринг HTML не влияют (по-крайней мере, наши тестеры и клиенты из Fortune 500 не жаловались, при всем разнообразии поддерживаемых браузеров включая IE6). Естественно, могут быть нюансы, но кросс-браузерная разработка давно заставила всех использовать только безопасное API с правильными селекторами (или просто использовать библиотеки, где это все учитывается).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Ruby чувствителен к переносам строк, так что его реализации были исключены из списка. Но т.к. я включил Lua и собираюсь включить Python, могу и включить mruby в список, если интересно. Но парсер и компилятор будут включены, т.к. необходимо позволять редактировать скрипты в любое время, при этом не требуя дополнительных инструментов.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Не все встраиваемые ЯП поддерживают байткод. Более того, возможно мы немного по-разному понимаем обфускацию. Для меня это возможность минимизировать и запутать код (вплоть до изменения хода выполнения кода путем вставления и реорганизации вызовов). Байткод не обязательно обфусцированный, хотя конечно, он компактнее исходного кода. luac -l -l -p дает достаточно читабельное представление байткода Lua, хотя, конечно, я верю, что есть специальные обфускаторы. Однако все это весьма специфично для конкретных ЯП и если есть возможность обфусцировать на уровне исходного кода, это предпочтительнее для нас (но, естественно, может быть неудобно для других и им лучше использовать байткод и сторонние утилиты).


Согласен, что неплохо было бы показать использование RAM. К сожалению, для "Hello world" примера числа будут весьма "бестолковые" тоже, т.к. только специфические конструкции или библиотеки могут вызывать большой расход памяти. Однако какие-то числа лучше чем вообще никакие, так что я добавлю это в тесты (https://gitlab.com/nuald-grp/embedded-langs-footprint/-/issues/4)

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Под обычный Linux — некоторые ЯП достаточно экзотичны и имеют весьма скудную документацию. Собственно, некоторые я смог завести (чтобы подключить их в виде библиотеки) только изучив их исходный код, так что поддержка кросс-компиляции — это отдельная история, на которую у меня, к сожалению, нет времени.


Область применения — массовые потребительские устройства (включая бюджетные типа телефонов на KaiOS), микроконтроллеры и SoC у нас есть в планах, но далеких. Т.к. на некоторых устройствах (типа той же KaiOS) нет возможности исполнять нативный код, то одна из поддерживаемых target — это WASM, и засовывать туда 5 мегабайт пайтона сверху (которые надо будет пользователю скачать в условиях ограниченного хранилища и дорогого интернета) — это роскошь.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Насколько я вижу, Umka чувствителен к новым строкам, их нельзя просто удалить, так что требования к возможности минификации скриптов не удовлетворяются. Насчет предпочтений процедурных vs функциональных — абсолютно нет. Если уж совсем захочется каких-то возможностей, то всегда можно организировать транспиляцию (и тогда возможность ЯП работать с машинно-генерируемым скриптом тоже пригодится, и я это включаю в категорию "дружелюбность к минификации").


Прошу заметить, что я не считаю, что у вашего продукта есть какие-то недостатки. Естественно, у него есть своя ниша и он там занимает достойное положение. Просто немного не подходит под мои требования. Впрочем, т.к. я уже включил Lua и собираюсь включить Python, я могу включить и ваш ЯП, если есть интерес.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

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

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Переносы строк — это просто один из примеров, конечно использование do/end тоже может сильно влиять. Более того, замена на пробел != полное удаление. В одном из моих проектов мы делали минификацию встроенного HTML, и простое удаление переносов позволило сэкономить 5% от суммарного размера исходных файлов.


Обновлено, 30% — полная минификация, удаление новых строк дало 5% (HTML был автогенерирован, так что там было достаточно много новых строк).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

После обсуждения с коллегой решил включить Python/Cython и MicroPython тесты. Добавлю ответ, когда закончу.

Сравнение встраиваемых ЯП по размеру в исполняемом файле

Lua не поддерживает минификацию исходных скриптов, т.к. чувствителен к переносу строк (да и в целом к форматированию, хотя конечно не так сильно как Python). Поэтому он был исключен из рассмотрения.

Что касается других языков, я не говорил, что они мне не понравились. Везде есть свои плюсы и минусы, но если сравнивать по возможностям при минимальном размере, то Chibi-Scheme в лидерах. Lua + LuaRocks — достаточно интересный вариант тоже, но с другой стороны, он уже включен в проект и все желающие могут сами с ним поиграться (хотя я думаю, все заинтересованные лица уже давно знают про Lua).

Сравнение встраиваемых ЯП по размеру в исполняемом файле

У меня очень богатый опыт внедрения Python (и написание расширений на Cython), и могу сразу сказать, что он имеет достаточно большой вклад в размер конечного бинарника (мы его встраивали в железку под урезанной версией Linux). Нам пришлось его сильно резать, включая изменения исходников (насколько я помню, он требовал pthread и эта библиотека не была нам доступна, но не уверен что современный Python имеет те же требования). К сожалению, Python не удовлетворяет нашим требованиям, но в принципе я могу его включить в проект, если это интересно.

Information

Rating
Does not participate
Location
Alberta, Канада
Date of birth
Registered
Activity