Pull to refresh

Comments 43

Если я правильно понял, у вас немного перепутано количество знаков в конечной статистике. Сейчас выглядит как будто на порядок время лучше, на самом деле (если я правильно понял), всего на 3 секунды лучше (сравнивать надо первую и последнюю — они для 10^7 знаков, результат в 1.8с — для 10^6).
Спасибо, исправил. В первом случае взял время не из той консоли.
В конечной статистике разная единица измерения (секунды и мс). А сравнивать надо по сути второе значение от программы и результат работы браузера. Целиком вывел для наглядности.
Результат мягко говоря впечатляет
но процесс написания с костылями — не очень
UFO just landed and posted this here
Чё-то как-то технология asm.js выглядит сыроватой.
Не могу не согласиться) По-моему в этот язык инвестировала мозилла с целью использования своей платформы, как игровой. Поэтому есть emcc, чтобы компилить обычный сишный код в asmjs. Поэтому и WebGL + asm.js. А где игры, там и юзеры)
Да, asm.js конечно это костыль костылем, но надеюсь, что в него смогут компилироваться другие языки и не надо будет его дополнительно изучать.
В следующем посте рассмотрю компиляцию на основе lljs > asm.js.
Собираюсь посмотреть в сторону Emscripten.
Первое язык, который можно скомпилировать в asm.js, а второе инструмент для компиляции сишного кода в asm.js)
У меня ощущение, что asm.js разработал skynet, чтобы люди не смогли понять когда их начнут захватывать.
А по делу, интересно конечно узнать из первых рук, что это такое, хоть и не понятно как программистское сообщество до такого докатилось.
За статью спасибо.
Насколько я знаю, asm.js сделан для трансляторов с других языков и не предназначен к тому, чтобы на нём руками писали.
В этой статье мы видели пример того, как пришлось напрямую писать на asm.js. Кроме того есть куча библиотек, которые захотят улучшить производительность в критических местах. Им же не переходить ради этого на другие языки. Поэтому в результате получится, что очень многим программистам придется напрямую копаться в таком коде.
Ну да. Я, вот, программирую на Си время от времени. А когда хочется улучшить производительность, я лезу в Ассеблер и не ною на синтаксис.
Я бы сказал, что синтаксис простого ассемблера более читабелен. И здесь не было никаких проблем сделать синтаксис основанный, например, на неком наборе функций, который был бы понятен и людям.
А ещё один вариант — аннотации.
В общем, тут есть свобода для более красивой реализации.
asm.js сделан так, чтобы код мог интерпретироваться браузером без поддержки asm.js, просто с меньшей скоростью. Это исключает поддержку функций.
Не то чтобы совсем исключает. Просто нужну было бы подключать библиотеку с функциями заглушками и это бы особо не тормозило браузеры без поддержки asm.js.
А вариант с аннотациями точно был бы немного быстрее (опять же без поддержки asm), т.к. не приходилось бы интерпритировать/оптимизировать кучу странных конструкций. Плюс к этому не нужно было бы вспоминать, что (1|0) — это int, т.к. можно было бы написать
/*int*/ var i = 1 или
var i = /*int*/ 1
Если добавить сюда вывод типов (не знаю, реализованно ли это сейчас), то избыточность была бы минимальной.
Сам долго думал что же мешало сделать в таком виде. И нет ответа.
К тому же эти перепрыгивания типов fixnum->int->intish->signed вообще выглядят магически.
Во-первых, комментарии не для этого. Не дело их трогать.
Во-вторых, это чересчур многословно. Просто кошмар.
В-третьих, переходы между типами сделаны, чтобы всё работало эквивалентно движкам без asm.js, только быстрее.

И, кстати, не нужно вспоминать, что variable|0 — это int, это просто надо знать. В обычном JS variable|0 тоже даёт int, это же известный приём, попробуйте.
Ещё скажите, что +'123' даст мне 123)
1) ну команда |0 тоже не для того была придумана, чтобы создавать эквивалент int i, а скорее изначально это… = (int) i; т.е. приведение типов.
2) ну вообще говорить о многословности, когда есть популярное объявление вида public static int, например… В общем на 4 символа больше, зато очевиднее.
3) тут вы не правы) Переходы между типами в обычном js есть и так. Т.е. если мы убираем все эти трансформации в фикснум, инт, сигн и назад получаем… Рабочую версию. Они зачем-то нужны компилятору, но зачем вопрос остался)

Комментарии имеют только одно тонкое место, которое я вижу: минификаторы) Они их просто вырежут и вот снова фигскомпилишь язык на выходе)
На счет комментариев и минификаторов только добавлю, что выход есть и тут. Либо все аннотации начинать со спец. символа (как например в java @), чтобы минификаторы понимали, что их не нужно вырезать. Либо же аннотации были бы все описаны в стандарте и минификаторы опять же могли смотреть, что вырезать, а что нет.
На счет многословности могу сказать только одно. Она предотвращает огромное количество ошибок. И как я сказал уже, если добавить выведение типов по Хиндли-Милнеру, то останется только самое необходимое. Но даже без него оверхед по сравнению с текущим синтаксисом процентов на 50%, что прямо скажем не кошмар.
Спасибо! У меня есть несколько вопросов:
1. Как сделать то же самое с помощью Emscripten?
2. Можно ли вызывать Asm.js функции из обычных JS функций?
3. Как определить, понимает ли браузер Asm.js (не в режиме совместимости)?
1, Как компилировать код в asm.js вроде написано, поэтому берём pi.c и emcc pi.c -o pi.js. Но как с этим работать не разбирался пока что.
2. Да, вы можете вернуть из модуля объект с функциями. Тут, например можно в конце модуля написать
return {
series: series,
expm: expm
}
и потом в функциях js обращаться как к прочим методам (например в данном случае было бы series.series(...) or series.expm(...))
3.Озадаченный этим вопросом я так и не нашёл ответа, кроме разве что по-дурацки проверить время работы функции. В консоль в FF выводится «Error: successfully compiled asm.js code». Но никакие ухищрения не помогли мне вытащить эти заветные слова.
Т.е. там время выполнения проверяется?
Да. Увы иначе только если грубо браузерное имя и версию) Но в сумме эта проверка и 70мс не займёт, так что если нужно, (а бывает ну очень нужно) подождать) А вот как поступать потом дело ваше)
function isASMSupporting(){
 'use asm';
 var i = 0.0;
 try {
  i = "";
  return false;
 }
 catch(e){
  return true;
 }
}

При условии, что там есть try-catch. Если нет — можно вынести за предел асм-кода.

Сомневаюсь, что это вообще валидный асм, но идея ясна :)
Ваш код на FF Nightly не проходит asm.js-верификацию c сообщениями типа

[12:43:56.056] TypeError: asm.js type error: non-expression-statement call must be coerced @ pi.js:91


(надо писать +floor, +pow везде)

А теперь внимание, сюрприз, производительность при этом у него не страдает. Как следствие, если взять и аккуратно переписать этот код на простой человеческий JavaScript получим ту же самую производительность и читаемый код

[13:02:08.076] Error: successfully compiled asm.js code (total compilation time 0ms) @ pi.js
[13:02:08.084] "pi (asm.js) start"
[13:02:30.776] "{"hex":"7AF5863EFF","fraction":1.4803089050105553}"
[13:02:30.776] "pi (asm.js) took 22692 ms."
[13:02:30.786] "pi (pure JS) start"
[13:02:51.523] "{"hex":"7AF5863EFF","fraction":1.4803089050105553}"
[13:02:51.523] "pi (pure JS) took 20736 ms."


Как из всего этого следует вывод? Без надобности тут вам asm.js.

Это, впрочем, и из кода видно — нет там ничего такого, что вменяемый JIT не осилил бы оптимизировать самостоятельно, без назойливых ручных |0 и +foo
Да, на счёт +floor читал, но у меня не ночная, так что увидев, что работает, решил, что атавизм)

Какое число переданно в пи? Хэша 7AF5863EFF нет ни на 100 000 ни на 10 000
Сейчас перепроверил, вот взял ваш код и ничегошеньки не менял, 71,5 сек от 1'000'000.
console
— [01:34:18.041] console.time('njs');
console.dir(pi(1000000));
console.timeEnd('njs');
[01:34:18.045] undefined
[01:34:18.046] njs: таймер запущен
— [01:35:29.505] [object Object]
[01:35:29.506] njs: 71459.41мс

и мой итог 1,9 сек от 1'000'000
console
— [01:38:17.177] console.time('asmjs');
console.dir(pi(1000000));
console.timeEnd('asmjs');
[01:38:17.180] undefined
[01:38:17.181] asmjs: таймер запущен
[01:38:19.050] [object Object]
[01:38:19.050] asmjs: 1868.46мс
Число переданное в функцию: 10000000

Кстати походу FF замедляет нормальные функции (гонит их только на интерпретаторе?) если их выполнять прямо из консоли.

Я выполнял из контекста страницы (добавил в конец JS файла)

  function measure(name, f) {
    console.log(name + " start")
    var start = Date.now();
    var result = f(10000000);
    var end = Date.now();
    console.log(JSON.stringify(result));
    console.log(name + " took " + (end - start) + " ms.");
  }

measure("pi (pure JS)", pi);
консоль работает немного по другому, на ней не рекомендуется проверять тонкие вещи, можно почитать об этом подробнее.
Это мы уже заметили)
Хотя прирост, в итоге обнаружен, всё же он не так велик)
Судя по всему 10 000 000, но что-то тут не так, ваш код на 10^7 я просто даже боюсь запускать. Даже на шестом порядке больше минуты. Ваше pure точно pure?
Точно pure. Замеряно на FF Nightly и Chrome Dev Channel на Mac Book. Но замер запущен не из консоли, а из самого JS скрипта.
Перепроверил, правда ваша, разница не столь разительна, всего 75% асм от чистого. Интересно, конечно, почему так, ну что же, коварная консоль)
Два урока сразу: и по asm.js, и методы проверки надо проверять)
у меня вообще разницы нет (asm.js на самом деле чуть медленнее) в FF Nightly, какая у вас версия FF?

Интересно, конечно, почему так, ну что же, коварная консоль)


Про консоль я выясню у Мозиловцев.
FF 23 из репы федоры.
Больше всего меня удивило, что скомпилированная программа паботает дольше asm.js-а.
А как вы эту программу собирали? Допустим если я собираю как

$ gcc -o pi -O3 -m32 -msse4 -mfpmath=sse pi.c


то особой разницы не вижу

$ time ./pi
 position = 10000000
 fraction = 1.480308905010555
 hex digits =  7AF5863EFF
./pi 10000000  20.38s user 0.01s system 99% cpu 20.391 total
от gcc -lm main.c до gcc -O3 -lm main.c -o pi -msse4 -mfpmath=sse (gcc 4.7.2)
результат примерно одинаков:
10^6 2162ms, 10^7 25201ms
Интересно, а в чём смысл этих магических сдвигов индекса в массиве на 3 бита? Это что, способ декларации типа такой? Типа, если 3 — то индекс целочисленный?
смысл очень простой: asm.js вырос из emscripten, который фактически компилирует LLVM биткод в JS. Биткод работает с указателями и сырой памятью, но в JS указателей нет, поэтому память эмулируется типизированными массивами, которые все указывают на один и тот же ArrayBuffer, а указатели эмулируют целочисленной переменной. При этом поддерживается только правильно выравненные указатели, т.е. int16 должен быть выравнен на 2 байта, а int32 — на 4 байта и так далее.

Теперь если представить что i — это правильно выравненный указатель на double, то как прочитать double по «адресу» i? Надо взять Float64Array представление кучи и прочитать из него элемент с индексом (i / 8) (double занимаем 8 байт).

Отсюда и требование на сдвиг — это деление на размер элемента в преположении что остальное это «адрес», потому что emscripten всегда такой код рожает. В рукописном коде выглядит, конечно же, сюрреалистично
А реально ли прямо в браузере интерпретировать классический код на С++ (начиная с #include ...)? И если да, реально ли в нём обращаться к функциям из javascript-библиотек?
Вроде нет, но теоретически возможно: можно сбилдить все библиотеки в asm.js и дальше выгрузить в браузер, пускай компилит, но смысла вэтом не много: куда проще и быстрее изолированная песочница для компиляций. Но можете поискать, может где есть инструмент)
Sign up to leave a comment.

Articles