Почему мономорфная и полиморфная материя в JavaScript?
Я читал некоторые статьи об обнаружении изменений, и все они говорят, что мономорфные функции намного быстрее, чем полиморфные. Например, вот цитата:
(..) Причиной этого является то, что он должен быть написан динамически, поэтому он может проверять каждый компонент независимо от его структуры модели как. VM не любят такой динамический код, потому что они не могут оптимизируйте его. Его рассматриваемая полиморфная форма формы не всегда одно и то же. Angular создает классы детекторов изменений в для каждого компонента, которые являются мономорфными, потому что они знают точно, какова форма модели компонентов. Виртуальные машины могут отлично оптимизируйте этот код, который делает его очень быстрым. Добро Дело в том, что нам не нужно заботиться об этом слишком много, потому что Angular делает это автоматически. (..)
Источник
Теперь я пытался найти примеры мономорических и полиморфных, но не мог найти их нигде. Может ли кто-нибудь уяснить разницу и почему это происходит быстрее?
Ответы
Ответ 1
Ответ заключается в том, что виртуальные машины могут выполнять эвристическое обнаружение "горячих функций", что означает код, который выполняется сотни или даже тысячи раз. Если счет выполнения функции превышает заданный предел, оптимизатор виртуальных машин может забрать этот бит кода и попытаться скомпилировать оптимизированную версию на основе аргументов, переданных функции. В этом случае предполагается, что ваша функция всегда будет вызываться с одним и тем же типом аргументов (не обязательно одни и те же объекты).
Причина этого хорошо документирована в этом v8-специфическом документе руководства, где объясняется оптимизация целого числа и общего числа. Скажите, что у вас есть:
function add(a, b) { return a + b; }
... и вы всегда вызываете эту функцию с целыми числами, этот метод может быть оптимизирован путем компиляции функции, которая выполняет целочисленное суммирование на CPU, что происходит быстро. Если после оптимизации вы подаете нечетное значение, тогда виртуальная машина деоптимизирует функцию и возвращается к неоптимизированной версии, поскольку она не может выполнять целочисленное суммирование по нецелым числам, и функция вернет ошибочные результаты.
В языках, где вы указываете перегруженные мономорфные методы, вы можете обойти эту проблему, просто компилируя несколько версий одного и того же имени метода с разными сигнатурами аргументов, которые затем оптимизируются самостоятельно. Это означает, что вы вызываете разные оптимизированные методы, потому что использование разных типизированных аргументов требует использования другого перегруженного метода, поэтому нет вопроса о том, какой метод вы используете.
Вы можете подумать, что в VM можно сохранить несколько копий оптимизированных функций и проверить типы, чтобы определить, какую оптимизированную скомпилированную функцию использовать. Теоретически это будет работать, если проверка типов перед вызовом метода была бесплатной или очень недорогой. На практике это, как правило, не так, и вы, вероятно, захотите сбалансировать ситуацию с реальным кодом, чтобы определить лучший порог компромиссов.
Здесь более обобщенное объяснение v8, оптимизирующее компилятор, в частности (от Google I/O 2012):
https://youtu.be/UJPdhx5zTaw?t=26m26s
Вкратце: функции, вызываемые с одинаковыми типами снова и снова, оптимизируются в компиляторе JIT, поэтому быстрее.
Ответ 2
Насколько я знаю, мономорфизм - очень необычный термин. Я лично никогда не слышал, чтобы он использовался для кодирования. Чтобы выяснить, что такое мономорфизм, я думаю, мы можем сделать вывод о том, что это означает, глядя на то, что такое полиморфизм.
Полиморфизм: идея, что многие (поли) разные объекты могут быть представлены одним и тем же типом для машины/времени выполнения/интерпретатора. Например, в С# у вас может быть столько классов, сколько требуется для реализации ICloneable
, и любой из них может быть использован в конструкторе копирования для общего связанного списка (например). Полный класс без проверки здесь в качестве примера, если вы заинтересованы
Хорошо, так что означает мономорфность?
Мономорфный мне означает, что интерпретатор объекта обрабатывает тип EXACT, который он ожидает, и наследование или модификация ожидаемого типа не возможны. В этом контексте, с утиным языком javascript, VM говорит: "Этот объект javascript имеет эти точные свойства, которые относятся к этим точным типам и называются точно сильные > как они есть". В С#, если бы мы хотели быть мономорфными, ограничения типа общего типа были бы невозможны, потому что T
должен был быть того же типа все время.
Эта ссылка дает хорошее руководство относительно того, почему это имеет значение для производительности. Для меня это можно суммировать ниже.
Двигатели Javascript хотели бы избежать поиска таблиц для свойств и вместо этого выполнять смещения указателя объекта. При мономорфизме смещение объекта для объектов в заданной строке кода всегда будет одинаковым, и виртуальная машина с легкостью выяснит, как выполнять поиск с добавлением указателя, а не поисками в таблице.
В действительности, двигатели пытаются обрабатывать небольшое количество разных объектов, передаваемых одной и той же функции, но виртуальная машина будет быстрее, если объект всегда выглядит одинаково в одной строке кода.
Пример для ясности
Следующий пример - это действительный javascript, но аргумент o
для функции f1
НЕ является мономорфным, так как виртуальной машине нужно обрабатывать два объекта различной формы, которые передаются.
function f1(o) {
console.log(o.prop1)
console.log(o.prop2)
}
// ...
o1 = { prop1: 'prop1', prop2: 'prop2' }
o2 = { prop1: 'prop1', prop2: 'prop2', prop3: 'prop3' }
f1(o1)
f1(o2)