Ответ 1
Тайна производительности x[k]
в Chrome (V8) находится в этом фрагменте сборки из ic-ia32.cc
. Короче говоря: V8 поддерживает глобальный кеш, который сопоставляет пару (map, name)
с index
определяющим местоположением свойства. Карта - это внутреннее имя, используемое в V8 для скрытых классов, другие JS-модули называют их по-разному (фигуры в SpiderMonkey и структуры в JavaScriptCore). Этот кеш заполняется только для собственных свойств объектов быстрого режима. Быстрый режим - это представление объекта, который не использует словарь для хранения свойств, но скорее похож на C-структуру со свойствами, занимающими фиксированные смещения.
Как вы видите, как только кеш заполняется в первый раз, когда выполняется ваш цикл, он всегда будет попадать на последующие повторы, что означает, что поиск свойств всегда будет обрабатываться внутри сгенерированного кода и никогда не войдет во время выполнения, потому что все свойства benchmark на самом деле существуют на объекте. Если вы просматриваете код, вы увидите следующую строку:
256 31.8% 31.8% KeyedLoadIC: A keyed load IC from the snapshot
и сброс счетчиков собственных кодов покажет это (фактическое число зависит от количества повторений, которые вы повторяете):
| c:V8.KeyedLoadGenericLookupCache | 41999967 |
который показывает, что кэш действительно попадает.
Теперь V8 фактически не использует один и тот же кеш для x.hasOwnProperty(k)
или k in x
, на самом деле он не использует кеш и всегда в конечном итоге вызывает время выполнения, например. в профиле для случая hasOwnProperty
вы увидите много методов С++:
339 17.0% 17.0% _ZN2v88internal8JSObject28LocalLookupRealNamedPropertyEPNS0_4NameEPNS0_12LookupResultE.constprop.635
254 12.7% 12.7% v8::internal::Runtime_HasLocalProperty(int, v8::internal::Object**, v8::internal::Isolate*)
156 7.8% 7.8% v8::internal::JSObject::HasRealNamedProperty(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::Name>)
134 6.7% 6.7% v8::internal::Runtime_IsJSProxy(int, v8::internal::Object**, v8::internal::Isolate*)
71 3.6% 3.6% int v8::internal::Search<(v8::internal::SearchMode)1, v8::internal::DescriptorArray>(v8::internal::DescriptorArray*, v8::internal::Name*, int)
и основная проблема здесь не в том, что это методы С++, а не рукописная сборка (например, KeyedLoadIC), но эти методы выполняют одинаковый поиск снова и снова без кэширования результата.
Теперь реализации могут быть совершенно разными между машинами, поэтому, к сожалению, я не могу дать полное объяснение того, что происходит на других машинах, но я предполагаю, что любой движок, демонстрирующий более высокую производительность x[k]
, использует аналогичный кеш (или представляет собой x
в качестве словаря, что также позволяет быстро исследовать сгенерированный код), и любой движок, который показывает эквивалентную производительность между случаями, либо не использует никакого кэширования, либо использует тот же кеш для всех трех операций (что будет иметь смысл).
Если V8 проверил один и тот же кеш перед тем, как переходить к времени выполнения для hasOwnProperty
и in
, то в вашем тесте вы видели бы эквивалентную производительность между случаями.