Как стать автором
Обновить

Комментарии 46

Зачем вы пытаетесь добавить свой GC и обработку исключений, если можно использовать их от JS? Разве нельзя их считать частью платформы? Почему нельзя реализовать исключения, наследование и виртуальные функции через механизмы JS?

Кто сказал, что Web Assembly это именно низкоуровневая платформа, аналогичная аппаратной? Это высокоуровневая платформа, и подход к ней должен быть соответствующий.

Представьте, что вы решаете обратную задачу - перевод какого-то другого языка в байт-код JVM. Согласитесь, что было бы странно слышать упрёки в отсутствии доступа к таблице виртуальных функций и невозможности раскручивать стек исключений вручную? А всё потому, что данные механизмы уже реализованы в имеющейся платформе и нужно использовать именно их, а не пытаться создать новые. Мне кажется, что тут таже ошибка. WA создавался и существует сугубо как расширение экосистемы JS, и в обозримом будущем будет существовать только в таком качестве.

Так ведь недоступны эти механизмы JS в WASM-то, иначе бы их все и правда использовали.

Почему нельзя реализовать исключения, наследование и виртуальные функции через механизмы JS?

Так в статье я и пишу о том, что перевод из JVM в JS - это гораздо лучше работающий вариант. Вот прямо на конкретных примерах разбираю. И задаю вопрос: а если JS всё равно работает лучше для этой задачи, зачем мне WebAssembly?

Кто сказал, что Web Assembly это именно низкоуровневая платформа, аналогичная аппаратной?

Хм, как минимум спека WebAssembly.

Представьте, что вы решаете обратнуюПредставьте, что вы решаете обратную задачу - перевод какого-то другого языка в байт-код JVM. задачу - перевод какого-то другого языка в байт-код JVM.

Ой, тогда это либо изначально язык, создававшийся для исполнения на JVM (и названный в честь острова рядом с Питером), либо я получу результат с довольно плохой производительностью и плохо вписывающийся как в местную экосистему, так и в свою собственную. Даже JavaScript невозможно эффективно реализовать в JVM.

ээээ, как?

Обычно если я встречаю чьи-то попытки объяснить, чем WebAssembly плох для реализации JVM (а так же, CLR, JavaScript и прочих динамических сред), то они сводятся к следующему: "Java (.NET, JavaScript, ваш вариант) — это управляемый язык со сборкой мусора и исключениями, так что приходится тащить с собой гигантский рантайм". Что же, на самом деле, ситуация несколько сложнее, а размер рантайма вовсе не такой страшный и не является основным источником бед.

Понятно, что JVM и dotnet — не лучшие платформы для WASM, но рынок требует, и разработчики пытаются реализовать такой таргет в своих рантаймах.



WebAssembly не реализует чего-то аналогичного POSIX.

А чем WASI не "POSIX"?

Понятно, что JVM и dotnet — не лучшие платформы для WASM, но рынок требует, и разработчики пытаются реализовать такой таргет в своих рантаймах.

ИМХО самый лучший софт - тот который написан непосредственно для данной платформы. А вот когда веб тащат на десктоп, или наоборот - получается нечто странное.

Есть ровно два сценария для задачи "запустить Java в браузере": есть уже достаточно толстая нетривиальная логика, которую нет вообще никакой возможности переписать на JS/TS (например, у нас это OT, чей код строго верифицирован в Coq) или есть уже достаточно большое приложение, которое и так работает на Android и iOS (через Graal Native Image) и хочется запускать его в web (и при этом нативный look&feel вообще не нужен - часто это сценарий для игр). Признаю, такие сценарии редки, но они встречаются в природе.

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

А где по ссылкам "рынок требует"? Я как раз по первой ссылке вижу, что "какие-то странные чуваки пришли и зачем-то создали issue", а им сказали "мы не хотим над таким работать".

Ко мне часто приходили какие-то странные чуваки и доказывали, что если вот Java вместо JVM запускать в WASI, то от этого Java станет в 1000 раз лучше. Я подозреваю, что это чуваки из стартапов, которые разрабатывали или предоставляли в аренду виртуальные рантамы, как докер. Потом, видимо, кто-то из пришедших оказался инженером и прямо выкатил PR. Но в тот конкретный момент мне было совсем не до код-ревью больших PR, и чувак тогда сделал "friendly fork", написал пару статей, выступил на конференциях и... всё заглохло. Подозреваю, что бизнесу попытались объяснить, зачем ему это, бизнес покупать не захотел.

А чем WASI не "POSIX"?

Тем, что WASI - это жалкий обрубок POSIX. В частности, там нет сигналов, нет mmap, mprotect и других очень любимых авторами рантаймов вещей. И ещё WASI в браузере нет, для этого нужно тащить довольно здоровенный polyfill. И ещё полноценным POSIX WASI не станет никогда, т.к. безопасность.

А где по ссылкам "рынок требует"?

У меня нет каких-то маркетинговых исследований, доказывающих это. Говоря про рынок, я ссылался на то что языки (котлин, c# и потенциально GraalVM) вкладывают ресурсы своих разработчиков, для того чтобы поддержать этот таргет. Кроме того, в последнее время было довольно много конференций (как чисто по WASM, так и языковых), где опять таки WASM был популярным (это может быть просто хайп, а не реальный спрос, но, как один из сигналов — можно рассмотреть).


Кстати, вы больше пишите про запуск Java в браузерах. Но я вижу популярность (на уровней статей/докладов) у запуска WASM вне браузера (serverless и в виде плагинов, например — https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/wasm_filter )

но рынок требует

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

Не переживайте, всему свое время. Всё началось с jsp/jsf, потом пришел клиентский рендеринг в gwt, он стал педалить появился angularjs, он стал педалить появились angular, react и тд . Сейчас и они начали педалить и активно развивается серверный рендеринг (уже в node.js) которому сто лет в обед. Через какое-то время UI фрэймворки стабилизируется и просто напишут новый серверный фрэймворк на подобие JSF/GWT который будет java wrapper на компоненты и компилировать java классы в тот же angular/react.

Например тот же vaadin уже это делает.

Кстати, интеропиться из Java в react - довольно сомнительная затея. Я бы мог после отпуска эту мысль развернуть. На практике лучше написать свой react на Java, я даже пилил такое, да забросил из-за нехватки ресурсов

Я думаю будет какая-то обёртка на ангуляр, тк это полноценный фрэймворк, реакт всё-таки там больше велосипедов и вариаций конфигурации. Кстати для ангуляр уже были попытки писать jsf рендереры, была библиотека но её забросил разработчик.

Тем не менее, wasm для dotnet довольно сносно работает, значит эти проблемы решаемы

Насколько бы blazor был лучше/хуже, если бы транслировал C# в JavaScript? Кто-то проводил замеры? А так и Java в WebAssembly работает "вполне сносно", и если бы у меня было желание продолжать развитие, фиксить баги и т.д. то работало бы прямо "вообще сносно". Вот только это "сносно" - всё равно достаточно плохо по сравнению с нативом (скоростью) и JS (размером бинарника).

C# это язык, а blazor исполняет скомпиленные бинарные сборки, т.е. байткод clr. Если вопрос Ваш был в том, насколько blazor был бы лучше прямой компиляции C# в байткод V8, то ответ - он был бы идентичен JS, т.к. его бы исполняла та же машина. Или я не понял ваш вопрос...

Нет никакого "байт-кода V8". V8 внутри себя использует IR, и даже не один, но никакого стандартного кросс-браузерного способа его сгенерировать нет. Я про генерацию JS. Собственно, раз уж у меня получилось генерировать вполне эффективный JS из байт-кода JVM, то не вижу никаких причин, почему нельзя было бы аналогичную задачу решить для CIL. Так вот, если я правильно понимаю, blazor берёт CIL и генерирует wasm. Интересно, а в MS пробовали генерировать JS? Где-то есть в публичном доступе сравнения размера/производительности и тулинга?

Мне про такие сравнения неизвестно. То что "байткода V8" не существует я знаю, но надо же как-то было это назвать коротко

Интересные проблемы. Видимо WebAssembly задумывался для компиляции из какого-нибудь C++ и о компиляции Java в него никто даже не думал.

Изначально да, задумывался для компиляции C++, хотя и у C++ были проблемы, например, с теми же исключениями. Забавно было смотреть, какой огород они городят для обхода данного небольшого ограничения. Забавно смотреть, как они реализуют ссылки на локальные переменные с упомянутым в статье shadow stack.

Но сейчас эта ситуация изменилась. Писать спеку наняли очень крутых учёных, которые пишут на Haskell и OCaml, они и взялись писать среду, в которой очень удобно исполнять Haskell и OCaml.

У меня нет никаких сведений об участии ядра коммитета WebAssembly в данных проектах. Я больше о другом: сам стандарт обладает лёгким душком некой функциональщины. Вот они не поддержали такие важные для рантаймов вещи, которые я описал, зато радостно бросились реализовывать хвостовую рекурсию. И вроде часть тулинга, вроде верификатора WebAssembly, они изначально писали на OCaml, вроде как частично спеку с его помощью генерируют. Да и саму спеку почитайте, часть про семантику инструкций - явно написана матёрыми функциональщиками.

Что Хабр посоветует для перевода крохотного Java приложения полностью в браузер? Ключевой код тут. Краткое описание: берём zip, подменяем в нём файл, подписываем получившийся zip. Был опыт компиляции C++ в WASM/JS через Emscripten - работает, но переписывать Java-код не хочется.

Попробуйте TeaVM.

А впрочем, нет, TeaVM не подойдёт. Я смотрю, у вас там криптография, а TeaVM криптографию не поддерживает.

А что если через j2cl?

Можно, наверное, самостоятельно их привнести, а нативную часть этих классов портировать на C++/Wasm?

Ну так и в TeaVM тогда несложно привнести

j2cl супер штука, но родная среда bazel, из-за этого мы пилим j2cl-maven-plugin. Возможно в течении месяца замерджим и wasm backend.

С каких это пор j2cl поддерживает WebAssembly?

Примерно с тех же, когда и kotlin. Собстевнно, а что им мешает? в данный момент они поддерживают js, kotlin и wasm.

Я слегка адаптировал свои yaml marshallers чтобы протестировать чтото не helloWorld, в хроме с влюченными GC и stringref вполне прилично пашет. Не сильно обгоняя js, но работает.

Поиск по словам j2cl дал только вот эту issue, которая ещё не закрыта. Можно ссылку на новость или на документацию по поддержке WebAssembly в j2cl?

А, увидел, что они добавили поддержку, но только экспериментальную и только для хрома под флагом

Насколько я понемаю, гугл совершенно не собирается продвигать j2cl в шарнармассы, по моему они вполне довольны нынешним состоянием дел.

Вы прямо сейчас можете проверить мои слова https://github.com/google/j2cl/tree/master/samples/wasm.

Ну и да, посмотрите историю коммитов, они достаточно активно улучшают компиляцию в wasm.

Здесь можно посмотреть про платы относительно будующего j2cl: https://github.com/google/j2cl/issues/93

А зачем? Раз приложение крохотное, то его проще на JS/TS переписать и в какую-нибудь обёртку вроде Chrome Extension завернуть.

его проще на JS/TS переписать

Если там есть библиотеки для создания и подписания zip.

и в какую-нибудь обёртку вроде Chrome Extension завернуть.

Это зачем? Просто сайт будет (надеюсь что будет когда-то).

да есть таи криптография. И работа с zip. Иначе nodeJS развалился бы не стартовав.

Не всё что доступно в NodeJS доступно в браузере.

Естественно не все. Но криптография и zip есть для JS и было бы КРАЙНЕ удивительно, если бы не была.

Итак, прежде всего про GC. Начнём с того, что де-факто его нет: прошло уже 6 лет с тех пор, как я о нём услышал, а в спецификацию он до сих пор не попал и реализован только в Google Chrome под экспериментальным флагом.

Ну да его долго нетрогали, но на текущий момент спека по сути готова, в драфте и реализация пилится.
Судя по статье приходом GC и Exception Handling половина фундаментальных проблем исчезает.
Вторая половина не так акутальная если смотреть не на оригинальный хотспот с его динмаикой, а грааль с его AOT компиляцией.
На текущий момент под грааль скомпилится большая часть библиотек, под него заточены фреймворки-так что такой поддержки было бы достаточно ооочень многим.

Насчёт стека, как я понял, проблема в том, что JVM стековая VM, а WASM регистровая. Но ведь существует тот же Dalvik, который берёт обычный JVM байткод и превращает в свой регистровый байткод. Значит это реализуемо, более того, не удивлюсь, если есть какой-нибудь общеизвестный оптимальный алгоритм преобразования. Зачем эти извращения с shadow stack.

Насчёт проверок на null, наверняка можно провести анализ графа исполнения и убрать часть проверок (нет смысла проверять локальную переменную на null, если мы уже проверили её до этого и не меняли).

Насчёт ленивой инициализации классов - в WASM нельзя писать самоизменяющийся код, но вроде есть указатели на функции? Можно сделать все статические методы ленивоинициализируемого класса "типа виртуальными" (вызывать через таблицу методов где-то в памяти, а не напрямую). И исходно таблица указывает на версию методов, которая вначале выполняет инициализацию класса. Но в момент инициализации класса указатели на методы подменяются на методы только делающие свою работу, без инициализации.

С GC и исключениями однозначных решений нет. Хотя я не уверен, что это узкое место. Живут же как-то языки без исключений типа Rust, Go. Насчёт оверхеда по памяти GC в WASM - это нормально. Посмотрите на минимальный размер объекта в Java. Там у каждого объекта есть помимо vtable ещё и monitor, который не нужен в однопоточном wasm. Короче, у Java тоже большой оверхед. Вам не обязательно есть памяти меньше Java. А ещё, возможно, можно скомбинировать vtable и структуру GC, просто поместив данные vtable после структуры GC, компилятор же знает её длину. А без vtable Java всё равно не оттранслировать, поэтому она у вас в любом случае должна быть.

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

Насчёт стека, как я понял, проблема в том, что JVM стековая VM, а WASM регистровая

Неправильно поняли. Это вообще не проблема. TeaVM работает в SSA. Читайте внимательно.

Насчёт проверок на null, наверняка можно провести анализ графа исполнения и убрать часть проверок (нет смысла проверять локальную переменную на null, если мы уже проверили её до этого и не меняли).

Я так и делаю. Но на практике всё равно остаётся весьма много ситуаций, когда ничего про значение сказать нельзя.

Насчёт ленивой инициализации классов - в WASM нельзя писать самоизменяющийся код, но вроде есть указатели на функции? Можно сделать все статические методы ленивоинициализируемого класса "типа виртуальными" (вызывать через таблицу методов где-то в памяти, а не напрямую). И исходно таблица указывает на версию методов, которая вначале выполняет инициализацию класса. Но в момент инициализации класса указатели на методы подменяются на методы только делающие свою работу, без инициализации.

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

Посмотрите на минимальный размер объекта в Java. Там у каждого объекта есть помимо vtable ещё и monitor, который не нужен в однопоточном wasm.

WebAssembly вполне себе многопоточный. Кроме того, TeaVM поддерживает потоки с помощью green threads. Так что ссылку на monitor приходится тащить. И за счёт различных ухищрений в TeaVM размер заголовка всего 8 байт. Меня совсем не устраивает, что WebAssembly добавляет ещё.

просто поместив данные vtable после структуры GC, компилятор же знает её длину.

И как же это должно выглядеть? И почему это должно помочь?

если отойти от дословного перевода джавы в WASM, а проводить статический анализ и делать эквивалентные преобразования кода

TeaVM вовсе не дословно переводит. А статический анализ, если только не тратить тыщу лет (буквально) на каждую компиляцию, вовсе не всесилен

Посмотрите на минимальный размер объекта в Java.

Давайте посмотрим.
Нижняя граница для 64-битных систем начинается с 12-16 байтов.


Там у каждого объекта есть помимо vtable ещё и monitor, который не нужен в однопоточном wasm.

Монитор есть, если он есть. Если его нет, то его нет. Спецификация Java позволяет использовать любой объект как примитив синхронизации, но не указывает, как именно это должно быть реализовано.


До тех пор, пока объект не используется как примитив синхронизации, а львиная доля объектов никогда в этом качестве не используются, тратить память на полноценный монитор не имеет смысла. Достаточно пары бит на флаги, указывающие, что монитора нет. Именно так реализовано в Oracle/OpenJDK HotSpot JVM.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории