Почему мономорфное и полиморфное значение в JavaScript?
Я читал несколько статей об обнаружении изменений, и все они говорят, что мономорфные функции намного быстрее, чем полиморфные. Например, вот цитата:
(..) Причина в том, что он должен быть написан динамически, чтобы он мог проверять каждый компонент независимо от того, как выглядит его модель. Виртуальным машинам не нравится такой динамический код, потому что они не могут его оптимизировать. Это считается полиморфным, поскольку форма объектов не всегда одинакова. Angular создает классы детекторов изменений во время выполнения для каждого компонента, которые являются мономорфными, потому что они точно знают, какова форма модели компонента. Виртуальные машины могут прекрасно оптимизировать этот код, что делает его очень быстрым для выполнения. Хорошо, что нам не нужно об этом слишком беспокоиться, потому что Angular делает это автоматически.(..)
Теперь я пытался найти примеры monomoprhic vs polymorphic, но нигде не мог его найти. Кто-нибудь может объяснить разницу, и почему это быстрее?
2 ответа
Ответ заключается в том, что виртуальные машины могут выполнять эвристическое обнаружение "горячих функций", то есть кода, который выполняется сотни или даже тысячи раз. Если счетчик выполнения функции превышает предопределенный предел, оптимизатор виртуальных машин может взять этот бит кода и попытаться скомпилировать оптимизированную версию на основе аргументов, переданных функции. В этом случае предполагается, что ваша функция всегда будет вызываться с аргументами одного и того же типа (необязательно одинаковыми объектами).
Причина этого хорошо задокументирована в этом руководящем документе для v8, где объясняется оптимизация целых и общих чисел. Скажем, у вас есть:
function add(a, b) { return a + b; }
... и вы всегда вызываете эту функцию с целыми числами, этот метод может быть оптимизирован путем компиляции функции, которая выполняет целочисленное суммирование на процессоре, что быстро. Если после оптимизации вы передадите ей нецелое значение, то виртуальная машина деоптимизирует функцию и вернется к неоптимизированной версии, поскольку она не сможет выполнить целочисленное суммирование нецелых чисел, и функция выдаст ошибочные результаты.
В языках, где вы указываете перегруженные мономорфные методы, вы можете обойти эту проблему, просто скомпилировав несколько версий одного и того же имени метода с разными сигнатурами аргументов, которые затем оптимизируются сами по себе. Это означает, что вы вызываете разные оптимизированные методы, потому что использование аргументов с разными типами требует от вас использования другого перегруженного метода, поэтому не возникает вопроса о том, какой метод вы используете.
Вы можете подумать, что можете хранить несколько копий оптимизированных функций в ВМ и проверять типы, чтобы определить, какую оптимизированную скомпилированную функцию использовать. Теоретически это работало бы, если бы проверка типов перед вызовом метода была бесплатной или очень недорогой. На практике это обычно не имеет место, и вы, вероятно, захотите сбалансировать вещи с реальным кодом, чтобы определить лучший порог компромисса.
Вот более обобщенное объяснение оптимизирующего компилятора v8 в частности (из Google I / O 2012):
https://youtu.be/UJPdhx5zTaw?t=26m26s
Вкратце: функции, которые вызываются с одними и теми же типами снова и снова, оптимизируются в JIT-компиляторе, следовательно, быстрее.
Насколько мне известно, мономорфизм это очень необычный термин. Я лично никогда не слышал, чтобы это использовалось для кодирования. Чтобы выяснить, что такое мономорфизм, я думаю, что мы можем сделать вывод, что это значит, посмотрев, что такое полиморфизм.
Полиморфизм: идея о том, что многие (поли) разные объекты могут быть представлены одним и тем же типом машине / среде выполнения / интерпретатору. Например, в C# у вас может быть столько классов, сколько вы хотите, чтобы реализовать ICloneable
и любой из них может быть использован в конструкторе копирования для общего связанного списка (например). Полный непроверенный класс здесь как пример если вам интересно
Хорошо, так что значит мономорфный?
Мономорфность для меня означает, что интерпретатор объекта обрабатывает ожидаемый тип EXACT, и никакое наследование или модификации ожидаемого типа невозможны. В этом контексте, с языком JavaScript, типизированным утиным, VM говорит, что "этот объект javascript имеет эти точные свойства, которые имеют эти точные типы и названы точно так же, как они". В C#, если бы мы хотели быть мономорфными, ограничения общего типа были бы невозможны, потому что 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)
Суть цитаты из ссылки, которую вы предоставили, заключается в том, что AngularJS из коробки предоставляет код, который делает все свои аргументы функции javascript "мономорфными", потому что объекты, передаваемые в них, имеют одинаковую структуру при каждом вызове.,