Основное различие между # 4 и всеми другими случаями состоит в том, что первый раз, когда вы используете замыкание в качестве конструктора, всегда довольно дорогой.
Он всегда обрабатывается во время выполнения V8 (не в сгенерированном коде), и переход между скомпилированным кодом JS и средой выполнения C ++ довольно дорог.Последующие распределения обычно обрабатываются в сгенерированном коде.Вы можете взглянуть на Generate_JSConstructStubHelper
в builtins-ia32.cc
и заметить, что он падает до Runtime_NewObject
, когда у замыкания нет начальной карты.(см. http://code.google.com/p/v8/source/browse/trunk/src/ia32/builtins-ia32.cc#138)
Когда замыкание используется в качестве конструктора впервые, V8 должен создать новую карту (или скрытый класс) и назначить ее в качестве исходной карты об этом закрытии см. http://code.google.com/p/v8/source/browse/trunk/src/heap.cc#3266. Здесь важно отметить, что карты размещаются в отдельном пространстве памяти. Это пространство не может быть очищено быстрым частичным сборщиком scavenge . Когда пространство карты переполняется, V8должен выполнить относительно дорогую полную mark-sweep GC.
Когда вы впервые используете замыкание в качестве конструктора, происходит пара других вещей, но 1 и2 являются основными причинами медлительности тестового примера № 4.
Если мы сравним выражения № 1 и № 4, то различия будут следующими:
- # 1 не выделяет новое замыканиекаждый раз;
- # 1 не вводит время выполнения каждый раз: после получения закрытия начальное построение карты обрабатывается по быстрому пути сгенерированного кода. Обработка всей конструкции в сгенерированном коде намного быстреезатем r, перемещаясь назад и вперед между средой выполнения и сгенерированным кодом;
- # 1 не выделяет новую начальную карту для каждого нового замыкания каждый раз;
- # 1 не вызывает развертки меткипереполнение пространства карты (только дешевые мусоры).
Если мы сравним # 3 и # 4, то различия будут следующие:
- # 3 не выделяет новую начальную карту для каждогоновое закрытие каждый раз;
- # 3 не вызывает развертки меток из-за переполнения пространства карты (только дешевые очистки);
- # 4 делает меньше на стороне JS (без Function.prototype.call, нет Object.create, нет поиска Object.prototype и т. д.) на стороне C ++ (# 3 также входит в среду выполнения каждый раз, когда вы делаете Object.create, но очень мало там).
Нижняя строка здесьчто первый раз, когда вы используете замыкание в качестве конструктора, обходится дороже по сравнению с последующими вызовами конструирования того же замыкания , что и , потому что V8 должен настроить некоторые параметры.Если мы немедленно отбрасываем замыкание, мы в основном отбрасываем всю работу, проделанную V8 для ускорения последующих вызовов конструктора.