Comments 34
Не знаю как кому, но мне вот представляется что гораздо продуктивнее было бы иметь возможность сделать что-то типа inline C или Dart islands в JS:
"C" int ray_tracer(int a) {
... C code, compiled into native assembly ...
}
// normal JS
var r = ray_tracer(42);
Как-то представляется что это позволит получить более предсказуемые результаты. И уж точно исполняться будет эффективнее.
Да и сократить размер всего этого хозяйства опять же. А то вот получается что весь мой Sciter который есть фактически встраиваемый browser включающий JS superset по размеру меньше чем одна V8.dll ( sciter32.dll — 3.7mb, V8.dll — 4.7mb ).
Если бы люди JS не писали, то его никто бы и не оптимизировал.
А так выбора нет :)
По идее JS в UI это клей связывающий выход одной native функции со входом другой. Т.е. в принципе JS это язык описания event routing / dispatching. Смысла JITить какой-нибудь click event handler немного если он срабатывает ровно один раз за время жизни страницы. А вот парсить их надо быстро.
Понятно что в условиях browser sandbox JS это единственная опция написать какой-нибудь ray tracer. Но вот эта единственность и есть основная проблема.
Представляется что гораздо эффективнее было бы иметь тот же embedded С например или вообще абстрактную bytecode VM типа JavaVM которую можно кормить compiled bytecodes + удобный interop из JS с этим хозяйством.
Т.е. путь того же Python — простой bytecode interpreter + удобная возможность встраивания native (или тех же bytecodes близких к native) для функций которым нужен CPU на 100%.
Как-то такое решение представляется более грамотным с инженерной точки зрения.
А так… создавать чисто интерпретирумый язык по своей изначальной природе, а потом героически преодолевать его интерпретирумость…
В JS скорость парсинга одинаково важна как и скорость исполнения.
Да скорость парсинга важна. Только ведь скорость парсинга тут вообще сбоку припёка — наличие, отсутствие оптимизирующего компилятора не влияет на нее.
Оптимизатор чаще всего сидит в отдельном потоке и оптимизирует только горячий код, как раз из соображения, что затраты на оптимизацию должны окупаться.
Конечно, не все так просто. Оптимизации могут полагаться на разного рода информацию собранную во время неоптимизированного исполнения — поэтому нужно так реализовывать сбор этой информации, чтобы производительность не страдала. Можете быть уверены — разработчики JS VMs все это стараются учесть и найти баланс между пиковой производительностью и startup.
Т.е. путь того же Python — простой bytecode interpreter + удобная возможность встраивания native (или тех же bytecodes близких к native) для функций которым нужен CPU на 100%.
Python — это очень странный пример. Люди ведь совсем даже не поголовно довольны его производительностью — существует целый ряд различных компляторов/реализаций, которые стараются решить проблему производительности. Начиная от Cython и заканчивая PyPy. DropBox вот тоже свой собственный JIT пилит.
А так… создавать чисто интерпретирумый язык по своей изначальной природе
Так уж сложилось, что люди которые JavaScript создали, и люди, которые «героически преодолевают его интерпретируемость», это совершенно разные люди.
Когда кто-то реально упирается в его производительность то пишут native extension. NumPy как пример.
То же происходит и в JS когда он работает в среде node.js. В обоих случаях есть относительно простой механизм встраивания native code.
Собственно я про это и говорю. Ну не надо писать ray tracers в JS. Гораздо продуктивнее было бы иметь возможность встраивания чего-то что лучше ложится на регистры и архитектуру CPU.
Ну а скоро и WebAssembly подъедет.
и люди сразу перепишут UI Facebook на С++ вместо JS :)
С кодом всяких разных web-приложений все очень не просто — много полиморфизма, сложно оптимизировать, классические оптимизации рассчитанные на мономорфный код часто не окупаются.
invokeinterface
). большинство объектных структур будут вполне статисческими
Это действительно так (хотя и здесь есть свои тонкости). Однако интерес тут представляет не сами объекты, а то, что с ними происходит… Для оптимизации методов, которые работают с этими объектами надо видеть статичность статичность метаструктуры, если можно так выразится. Выше в тексте статьи я приводил уже пример с
function make(x) {
return { f: function () { return x } }
}
var a = make(0), b = make(1);
здесь не всякая VM может увидеть, что
a
и b
имеют одинаковую метаструктуру — V8, например, не может.function f(x) {
return x.f() // (*)
}
f(a);
f(b);
то V8 спотыкается о то, что она при отслеживании метаструктуры рассматривает функции как нечто неделимое, т.е. она будет считать, что вызов в точке
(*)
полиморфный. А если бы она учитывала, что функция это тело+контекст, то было бы лучше. Чинится очень просто: отказом от использования замыканий таким образом на горячих путях.
function Classic(x) {
this.x = x;
}
Classic.prototype.f = function () { return this.x; }
function make(x) {
return new Classic(x);
}
var a = make(0), b = make(1);
Но это только один пример…
Можно рассмотреть полиморфизм другого толка:
function A() { this.x = "a"; this.a = 0; }
function B() { this.x = "b"; this.b = 0; }
var a = new A(), b = new B();
function use(o) {
return o.x;
}
use(a);
use(b);
Как скомпилировать
use(o)
во что-то умнее чем (псевдокод):function use(o) {
if (o.hiddenClass === ClassA) {
return o.x // load from some fixed offset
} else if (o.hiddenClass === ClassB) {
return o.x // load from some fixed offset
} else {
return /* either generic load or deopt */
}
}
вопрос открытый
Вопрос нужен ли нам WebAssembly я оставлю за рамками — пусть на него отвечают те, кто этим WebAssembly занимаются.
Никогда, теперь есть WASM.
WebAssembly поддержит Форт
Цитата:
github.com/WebAssembly/design/blob/master/AstSemantics.md пишет: Multiple return value calls will be possible
— это приниципиальный момент в возможности поддержки Форта в этой новой виртуальной машине.
Expression trees offer significant size reduction by avoiding the need for set_local / get_local pairs in the common case of an expression with only one, immediate use.
— ну а это уже неприкрытый Форт Инфиксные выражения исходного языка, которые парсятся в expression trees, рекурсивно разворачиваются при компиляции, и полученный код работает со стеком последовательно без трактовки их в качестве именованных локальных переменных. Так же как locals в Форте не выполняют load/store, в/из стека куда-то ещё, а могут использоваться напрямую и «анонимно» в последовательном вычислении.
Ср. www.forth.org.ru/~ac/rationale/FORTH.TXT
P.S. Чем это полезно в плане производительности и как это лучше «утилизировать» в JS?
Поэтому от WASM я впервую очередь жду таких фич как интеграция с GC и JIT.
У меня тут спор возник с одним разработчиком. По поводу применения const
с объектами.
Он утверждал, что смысл const
это просто заменить данный идентификатор значением константы. И его нельзя использовать с объектами.
Это может быть верно для компиляции Си или Ассемблера, но в мире JS имхо делается фиксированная привязка на область памяти с данным значением. И в моем понимании const
скорее означает "я не хочу присвоить данному идентификатору что-то еще", нежели какие-то конкретные детали реализации.
Вот рассудите, как человек знающий потроха реализации JS.
ЗЫ. Про то, что свойства объекта-константы все равно можно менять, я знаю. Речь не про это.
Дык это же новый js-holywar. Раньше про ;
и ,
споры были. А теперь вот про const
:) Правда если рассматривать const
как "заменить данный идентификатор значением константы", то он с этой задачей справляется довольно паршиво.
Он утверждал, что смысл const это просто заменить данный идентификатор значением константы.
Разумеется в JavaScript семантика у
const
отнюдь не такая. Самый наглядный пример того, что это все-таки неизменяемая привязка переменной к значению:(function () {
console.log(f()); // prints <?>
const X = '<!>';
console.log(f()); // prints <!>
function f() {
try {
return X;
} catch (e) {
return '<?>'
}
}
})();
С другой стороны виртуальная машина может и должна бы использовать это самое свойство неизменяемости, и уж по крайней мере при оптимизирующей компиляции стараться подставить значение «переменной» прямо в код, как константу.
V8 не использует, к сожалению. А JSC, например, использует. Код
const X = 10;
function bar() { return X * X }
компилируется в
return 100
по сути дела
Что браузеры делают с вашим JavaScript-кодом: об оптимизациях в JS-движках на примере V8