Tuesday, August 2, 2011

5. О том, что нас спасёт.


(если вы сразу пришли на эту страницу, вам рекомендуется читать сию историю с самого начала, .)

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

var yi = SOME_ROUTINE();
if (isGenerator(yi)) {
for (var i in yi) yield i;
}

чтобы уменьшить количество этих ненужных yield, расположенных в таких циклах. Но я думаю, что их там всё же останется порядочно.

Поэтому уже после Хакатона, было решено покопать в ортогональном направлении, и ... портануть на джаваскрипт интерпретатор джаваскрипта, работающий не в глубину стека, а на одном уровне, и эмулирующий стек в куче, и таким образом легко и прекрасно реализующий как continuations, так и co-routines (yield).

Этим интерпретатором оказался mozilla rhino. Это java-проект, который используется я не знаю где еще, но я им уже пользовался много лет назад. Для того, чтобы убедиться, что он не умрёт на больших файлах, я отключил тамошний оптимизатор, и запустил только интерпретатор. Да, главный цикл интерпретатора простой, никто никуда вглубь не идет, и по тексту программы видны сладкие слова: continuation, tail call и yield. Это оно.

С оптимизатором оно умеет генерировать .class файлы, и именно там я и увидел вывернутые наизнанку и порезанные функции, содержавшие yield. Я думаю, что мы не будем искушать судьбу, и не будем запускать оптимизатор на нашем тетрисе, а воспользуемся интерпретатором.

Вдобавок, перед нами еще стоит задача портировать эту махину на Javascript, чтобы оно работало под бровзером. К счастью, волшебная палочка, делающая сие уже есть, и она называется GWT (Google Web Toolkit). Статьи не хватит, чтобы описать всю крутизну сего гуглового проекта, в котором гугл четырьмя меткими ударами соединил трудносоединимое и получил то что есть. (дальше 4 пункта рекламы) Эти четыре удара:
  1. использование java как базового языка (а не C, или C# или Php) , это позволяет использовать лучшую из IDE - IntelliJ IDEA, и полноценный компилятор и линкер из java в javascript, с базовыми java libraries. Использование языка со строгой типизацией (java) позволяет писать огроменные проекты для бровзера.
  2. whole program optimization и permutations, генерящие очень ёмкий javascript конкретно под каждый бровзер (можно использовать другой, или в дополнение к этому, базис)
  3. отладка прямо в бровзере программы на Java, не выходя из любимого IDE! При 99.99% гарантированном совпадении конечного результата с тем, что наблюдается в отладке, и это не кропотливая работа индусов, проверяющих точность какой-нибудь эмуляции, а выбранный гуглом более правильный подход, который не даст иного результата.
  4. направление в компиляции, позволяющее делить или соединять куски программы, картинок, текста и данных в любом требуемом варианте. Например, одной из самых первых фич была добавлена возможность на этапе компиляции склеивать статические картинки участвующие в дизайне страницы так, чтобы потом программно они виделись разными, а на сайт ехали одним .gif -ом или jpeg-ом, и уже на месте прозрачно для программиста становились background-ами с требуемым offset-om, чтобы было видно только нужную часть этой большой картинки. Поразительным при этом было то, как мало требуется от программиста для того, чтобы перейти к этой технологии.
Продолжаем, нас пока интересует только компилятор из java в js. Естественно, он не поддерживает кучу java функционала, который неприменим на вебе. Нам придется изрядно покоцать интерпретатор, чтобы он скушался компилятором GWT..

От него ушел весь .class-генерящий оптимизатор, часть интерпретатора, которая работает с сорцами в потоках (супротив строк), всё security, все модули, требуемые по стандарту ECMA, включая функцию (или оператор?) require, ушла вся интеграция с Java native functions посредством reflection, потому что только reflection нам и не хватало (GWT строится на code reachability, как краеугольном принципе, строя графы вызовов, оттуда отсутствие поддержки reflection). Ушла вся оптимизация плавающей точки, потому что она юзала платформенное представление точки по битам, а в JS этого нету. Отправились в небытие также NativeRegexp и XML расширения скрипта, чтобы не перегружать runtime, осталось всего полтора мегабайта кода, или 46000 строк, и я верю, что можно отрезать добрую половину.

Целый день происходила битва с rhino, и вот он готов.

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

Что мы имеем в результате?

  1. PROG.PAS ----> Хаскильный транслятор -----> PROG.JS
  2. PROG.JS + RHINO.JAVA ----> GWT -----> Executor.JS (embedded PROG.JS as string)
  3. Executor.JS + Browser + Canvas -----> PROFIT!

Написана также еще одна ветка:

2. RHINO.JAVA + PROG.JS --> JAVA runtime --> анимация на GUI

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

Сорцы прилагаются:

paporotn.js (глючный, там продублировалась ф-я)


Итак, теперь результат. Повторяю, вы видите как работает интерпретатор многопоточного (yield) Javascript, сконвертированного (via Haskell) из Turbo Pascal, сам интерпретатор Javascript-а "Rhino" написан на Java, портирован (via gwt + ножницы) в Javascript.
Внизу работающая программа, проверено под Chrome, Mozilla. Если в опере загрузить ее отладчик DragonFly, то там отчего-то тоже начинает работать.
В программе можно кликнуть на папоротник, и нажать на ESC, чтобы прекратило кушать CPU.
Продолжение следует.

No comments:

Post a Comment