Pull to refresh
95
0
Вячеслав Егоров @mraleph

Пользователь

Send message
Полиморфный код можно, конечно, компилировать, но это отнюдь не так просто, как вам кажется — если хочется добиться, действительно быстрого вызова методов. Это только в С++ у вас есть простые явные vtable-s и все просто, уже в Java начинаются всякие интересности если хочется сделать быструю реализацию интерфейсных вызовов (поищите в интернете статьи о реализации invokeinterface).

большинство объектных структур будут вполне статисческими


Это действительно так (хотя и здесь есть свои тонкости). Однако интерес тут представляет не сами объекты, а то, что с ними происходит… Для оптимизации методов, которые работают с этими объектами надо видеть статичность статичность метаструктуры, если можно так выразится. Выше в тексте статьи я приводил уже пример с

function make(x) {
  return { f: function () { return x } }
}
var a = make(0), b = make(1);


здесь не всякая VM может увидеть, что a и b имеют одинаковую метаструктуру — V8, например, не может.
Когда я говорю «насколько Facebook/Inbox/etc выигрывает», я как раз и имею ввиду «выигрывает по производительности», т.е. становится быстрее.

С кодом всяких разных web-приложений все очень не просто — много полиморфизма, сложно оптимизировать, классические оптимизации рассчитанные на мономорфный код часто не окупаются.
вопрос о том, насколько Facebook/Inbox/etc выигрывает от того, что его код оптимизировали, решается отнюдь не всегда в пользу оптимизаций :)

Ну а скоро и WebAssembly подъедет.


и люди сразу перепишут UI Facebook на С++ вместо JS :)
Он утверждал, что смысл 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 по сути дела
В JS скорость парсинга одинаково важна как и скорость исполнения.


Да скорость парсинга важна. Только ведь скорость парсинга тут вообще сбоку припёка — наличие, отсутствие оптимизирующего компилятора не влияет на нее.

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

Конечно, не все так просто. Оптимизации могут полагаться на разного рода информацию собранную во время неоптимизированного исполнения — поэтому нужно так реализовывать сбор этой информации, чтобы производительность не страдала. Можете быть уверены — разработчики JS VMs все это стараются учесть и найти баланс между пиковой производительностью и startup.

Т.е. путь того же Python — простой bytecode interpreter + удобная возможность встраивания native (или тех же bytecodes близких к native) для функций которым нужен CPU на 100%.


Python — это очень странный пример. Люди ведь совсем даже не поголовно довольны его производительностью — существует целый ряд различных компляторов/реализаций, которые стараются решить проблему производительности. Начиная от Cython и заканчивая PyPy. DropBox вот тоже свой собственный JIT пилит.

А так… создавать чисто интерпретирумый язык по своей изначальной природе


Так уж сложилось, что люди которые JavaScript создали, и люди, которые «героически преодолевают его интерпретируемость», это совершенно разные люди.
В таких случая можно баги файлить на GitHub или посылать мне описания непонятных деоптимизаций. Я разьясню деоптимизацию — и куда-нибудь запишу ее как пример для будущих поколений.
В WASM мне не нравится то, что они не поддерживают ничего, что сделало бы этот самый WASM для меня полезным. Меня не интересует возможность запускать C++ браузере. Меня интересует возможность запускать динамические языки в браузере отличные по семантике от JavaScript. Причем я хочу это делать с минимальными издержками. Например, я не хочу компилировать мой написанный на C++ GC в WASM — я хочу полагаться на GC, который уже в браузере есть… при этом, кстати, GC не так-то и просто скомпилировать, потому что я хочу ходить по стеку в поисках корней, а WASM такого не позволяет. Аналогично, я не хочу писать свой JIT — я хочу полагаться на JIT, который в браузере есть — со всеми его мощьными фичами типа адаптивной спекулятивной оптимизации.

Поэтому от WASM я впервую очередь жду таких фич как интеграция с GC и JIT.
Понятия не имею. Меня про asm.js вообще бесполезно спрашивать, я считаю, что это совершенно бесполезная (или даже вредная) «технология» и мир без нее будет лучше.

rumkin внизу правильно замечает, что на замену asm.js пришел WASM… Я, впрочем, не большой фанат WASM в его текущей форме.
Я не большой поклонник компиляции C++ в JS. Если WebAssembly получит распространение и поддержку во всех браузерах, то emscripten станет не нужен, потому что для clang есть уже экспериментальный backend, который умеет выдавать WebAssembly.

Вопрос нужен ли нам WebAssembly я оставлю за рамками — пусть на него отвечают те, кто этим WebAssembly занимаются.
Смысл в глубоких оптимизациях JS очень простой — люди пишут JS-код, надо чтобы он исполнялся эффективно.

Если бы люди JS не писали, то его никто бы и не оптимизировал.

А так выбора нет :)

Для разных машин существуют разные флаги — чтобы посмотреть на сгенерированный код. Тул IRHydra, который я написал, позволяет в более-менее удобоваримом виде изучать информацию, которую умеет дампить V8-овый Crankshaft. Что-то мне подсказывает, что и для других машин есть подобные способы, но разрабочики их не афишируют :)
На русском у меня есть, но мало

Из более-менее свежего: http://mrale.ph/blog/2015/04/12/jsunderhood.html

Из старинного есть:

http://www.youtube.com/watch?v=tCG0aPNvkTs
http://s3.mrale.ph/TechForum2012.pdf
Но ведь именно из-за поддержки этих фич, иначе устроен процессинг


Отнюдь. Различия между re2 и irregexp действительно фундаментальные — но они отнюдь не из-за поддержки этих фич.

Вот, например, lookbehind в irregexp сделали недавно и никакой особой заморочки с этим не было.

Для простых регекспов из этого бенчмарка основное преимущество V8 состоит в том, что irregexp транслирует регулярные выражения в машинный код, а не исполняет их на «интерпретаторе», как многие другие regexp движки.

Ну тут должно быть просто


На самом деле нет. Вот что на это отвечает Russ Cox (автор re2)

The lack of generalized assertions, like the lack of backreferences,
is not a statement on our part about regular expression style. It is
a consequence of not knowing how to implement them efficiently. If
you can implement them while preserving the guarantees made by the
current package regexp, namely that it makes a single scan over the
input and runs in O(n) time, then I would be happy to review and
approve that CL. However, I have pondered how to do this for five
years, off and on, and gotten nowhere.


https://groups.google.com/d/msg/golang-nuts/7qgSDWPIh_E/OHTAm4wRZL0J

Иными словами в re2 выбраны и реализованы только те фичи, которые можно эффективно исполнять гарантируя линейную сложность, без катастрофического отката

А вот что вы имеете в виду под zero-width positive lookahead? Ведь любые lookahead и lookbehind это zero-width match?


Они так просто часто называются.

Кстати, lookbehind (?<=re) re2 тоже не поддерживает, если верить документации :)
lookbehind, atomic groups, possessive quantifiers


Ни одна из этих фич в regex dna не используется.

в V8 это встроенная имплементация с довольно урезаным функционалом


Имплементация V8 — это полноценная имплементация JS RegExp. JavaScriptовые регулярные выражения действительно не поддерживают разные фичи, которые поддерживает re2, но обратное тоже верно — например: JS поддерживает zero-width positive lookahead (?=...) и backreferences \n, а re2 — нет.
Нельзя делать выводы на основе простых измерений, надо хотя бы профилировать и пытаться понять, что же на самом-то деле происходит внутри. Иначе получаются неправильные выводы.

Дело здесь в следующем — создание функций, которые содержат в себе литералы (например, array literal или там object literal), это более тяжелая операция по сравнению с созданием функций, которые в себе литералов не содержат.

Если взять и просто сравнить два профиля, то все тайное становится явным

function find(val){
    function index (value) {
        return [1, 2, 3].indexOf(value);
    }

    return index(val);
}

    8.29%  67  | LazyCompile:*InnerArrayIndexOf native array.js:1020
*   7.67%  62  | v8::internal::JSFunction::set_literals
*   7.05%  57  | v8::internal::Factory::NewFunctionFromSharedFunctionInfo
*   5.81%  47  | v8::internal::Factory::NewFunction
*   4.58%  37  | v8::internal::Factory::New<v8::internal::JSFunction
*   4.33%  35  | v8::internal::Runtime_NewClosure
    4.21%  34  | Stub:FastCloneShallowArrayStub
    4.08%  33  | v8::internal::Heap::AllocateRaw
    3.34%  27  | LazyCompile:~index test.js:2
*   3.34%  27  | v8::internal::Factory::NewFunctionFromSharedFunctionInfo
    3.34%  27  | Builtin:ArgumentsAdaptorTrampoline
    3.09%  25  | v8::internal::Heap::Allocate
*   3.09%  25  | v8::internal::SharedFunctionInfo::SearchOptimizedCodeMap    

function foo() {
  return [1, 2, 3];
}

function find(val){
    function index (value) {
        return foo().indexOf(value);
    }

    return index(val);
}

   13.58%  58  | LazyCompile:*InnerArrayIndexOf native array.js:1020
    9.82%  42  | Builtin:ArgumentsAdaptorTrampoline
    7.49%  32  | LazyCompile:~index test1.js:6
    7.01%  30  | LoadIC:A load IC from the snapshot
*   6.08%  26  | Stub:FastNewClosureStub
    5.62%  24  | Builtin:CallFunction_ReceiverIsNullOrUndefined
    3.74%  16  | LazyCompile:*foo test1.js:1
    3.51%  15  | Builtin:Call_ReceiverIsNullOrUndefined
    3.51%  15  | LazyCompile:*indexOf native array.js:1065        

В первом случае мы ходим много в среду исполнения и там занимаемся всякой тяжелой и малополезной работой (например, клонированием массива литералов привязанного к замыканию), а во втором случае мы быстренько создаем замыкание с помощью FastNewClosureStub. Вот отсюда и основная разница.
Достаточно странный вопрос, я где-то сказал, что «для этого»? Вменяемые библиотеки (включая работу с DOM) — это просто одно из его достоинств, а отнюдь не причина существования.

В таком ключе и я мог бы спросить: а что для этого нужно полифиллить Array.from и вообще компилировать мой код каким-нибудь babel.js? Оказывается да, нужно таки. Потому что ES6 в настоящий момент тоже язык отдельный от того, что поддерживается браузерами.
Скорее «почти так», чем «именно так»: Array.from(document.querySelectorAll(".xyz"), el => el.value), потому что NodeList не является Array и как следствие не «умеет» map.
Часть этих вещей выполняется во время исполнения, компилятор dart2js выкидывает, что может. Если их не исполнять, то другой язык получается.

Большая часть (частично правдивых) слухов о большом рантайме происходят из тех времен, когда первая версия компилятора просто тупо выплевывала кучу кода, не пытаясь даже удалить все что не используется.

Голому Hello World особо «рантайма» не нужно, там только прибавляется кусочек который (лениво) инстанцирует классы.

Основная часть того, что воспринимается как runtime на самом деле в реальных проектах приходит из dart:html. Это библиотека, которая оборачивает DOM и реализует всякие полезные методы, чтобы вы могли писать

document.querySelectorAll(".xyz").map((el) => el.value) или el.onClick.listen((e) { /* ... */ })

вместо голого JSовго [].slice.call(document.querySelectorAll(".xyz")).map(function (el) { return el.value }) и el.addEventListener("click", function () {}), т.е. в некотором смысле dart:html это такая специально заточеная для Dart jQuery.

Компилятор старается выкинуть все что можно, но он же не знает, что там querySelector вернет — поэтому остается всякий потенциально неиспользуемый код. Сравнивать этот «рантайм» нужно, конечно же, не с голым JS, а с размером всех тех либ которые вы будете использовать в своем коде (jQuery там или lodash).
В виде плагина его не добавишь, потому что PPAPI не дает сделать легковесную тесную интеграцию с DOM.
Если вы посмотрите на Dart, то обнаружите, что он не только добавляет, но и убирает.

Семантически Dart достаточно отличается от JavaScript объектной моделью. Вместо концепции «любой объект это словарь с прототипом», в Dart обычные классы с фиксированной структурой. В JavaScript вы читаете свойство, которого нет, и получаете undefined, в Dart вы получаете исключение. В JS вы складываете null + 1 получаете 1 (или undefined + 1 получаете NaN), в Dart вы получаете опять же исключение (и нет никакого undefined). Иными словами то «что не хватает в TS, а есть в dart также» — это в некотром смысле отрицательная величина, потому что Dart упрощает семантику и убирает те части JS, которые скажем так мешают вам поймать ошибку в коде как можно раньше.

Конечно же, Dart не только убирает, но и довавляет. Например, в Dart можно переопределить операторы + или допустим [], или каждый класс может иметь метод noSuchMethod, который позволяет динамически регировать на вызов несуществующих методов и менять поведение объекта (можно, например, легко написать proxy объект на основе этого метода).

Плюс Dart это не только язык, это еще и платформа с библиотеками спроектированными так, чтобы хорошо работать с его семантикой. В TypeScript библиотек вообще нет, люди просто .d.ts декларациями к тому, что есть в JavaScript.

Information

Rating
Does not participate
Location
Дания
Date of birth
Registered
Activity