Ведущий разработчик Яндекс.Деньги Андрей Мелихов (также редактор/переводчик сообщества devSchacht) на примере движка V8 рассказывает о том, как и через какие стадии проходит программа, прежде чем превращается в машинный код, и зачем на самом деле нужен новый компилятор.
Материал подготовлен на основе доклада автора на конференции HolyJS 2017, которая проходила в Санкт-Петербурге 2-3 июня. Презентацию в pdf можно найти по этой ссылке.
Несколько месяцев назад вышел фильм «Последний убийца драконов». Там, если протагонист убивает дракона, то в мире исчезает магия. Я хочу сегодня выступить антагонистом, я хочу убить дракона, потому что в мире JavaScript нет места для магии. Все, что работает, работает явно. Мы должны разобраться, как оно устроено, чтобы понимать, как оно работает.
Я хочу поделиться с вами своей страстью. В какой-то момент времени я осознал, что плохо знаю, как устроен под капотом V8. Я начал читать литературу, смотреть доклады, которые в основном на английском, накопил знания, систематизировал и хочу их довести до вас.
Интерпретируемый или компилируемый у нас язык?
Я надеюсь, все знают отличие, но повторю. Компилируемые языки: в них исходный код преобразуется компилятором в машинный код и записывается в файл. В них используется компиляция до выполнения. В чем преимущество? Его не нужно повторно компилировать, он максимально автоматизирован для той системы, под которую скомпилирован. В чем недостаток? Если у вас меняется операционная система, и если у вас нет исходников, вы теряете программу.
Интерпретируемые языки – когда исходный код исполняется программой-интерпретатором. Преимущества в том, что легко достичь кроссплатформенности. Мы поставляем наш исходный код как есть, и если в этой системе есть интерпретатор, то код будет работать. Язык JavaScript, конечно, интерпретируемый.
Погрузимся в историю. В 2008 выходит браузер Chrome. В том году Google презентовал новый движок V8. В 2009 году на том же самом движке была представлена Node.js, которая состояла из V8 и библиотеки libUV, которая обеспечивает io, т. е. обращение к файлам, сетевые какие-то вещи и т.д. В общем, две очень важные вещи для нас построены на движке V8. Посмотрим, из чего он состоит.
В 2008 году движок был довольно прост внутри. Ну, относительно прост — схема его была простая. Исходный код попадал в парсер, из парсера — в компилятор, и на выходе мы получали полуоптимизированный код. Полуоптимизированный, потому что хорошей оптимизации тогда еще не было. Возможно, в те годы приходилось писать лучший JavaScript, потому что нельзя было надеяться на то, что оптимизатор его сам внутри заоптимизирует.
Для чего в этой схеме нужен парсер?
Парсер нужен для того, чтобы превращать исходный код в абстрактное синтаксическое дерево или AST. AST – такое дерево, в котором все вершины — операторы, а все листья — это операнды.
Посмотрим на примере математического выражения. У нас такое дерево, все вершины – операторы, ветви – операнды. Чем оно хорошо — тем, что из него очень легко генерировать позже машинный код.
Кто работал с Assembler, знает, что чаще всего инструкция состоит из того, что сделать и с чем сделать.
И вот здесь как раз мы можем смотреть, являемся ли мы в текущей точке оператором или операндом. Если это оператор, смотрим его операнды и собираем команду.
Что в JavaScript происходит, если у нас есть, например, массив, и мы запрашиваем из него элемент по индексу 1? Появляется такое абстрактное синтаксическое дерево, у которого оператор «загрузить свойство по ключу», а операнды – это объект и ключ, по которому мы загружаем это свойство.
Зачем в JavaScript компилятор?
Как я сказал, язык у нас интерпретируемый, но в его схеме мы видим компилятор. Зачем он? На самом деле есть два типа компиляторов. Есть компиляторы (ahead-of-time), которые компилируют до выполнения, и компиляторы JIT, которые компилируют во время выполнения. И за счет JIT-компиляции получается хорошее ускорение. Для чего это нужно? Давайте сравним.
Есть один и тот же код. Один на Pascal, другой на JavaScript. Pascal — прекрасный язык. Я считаю, что с него и надо учиться программировать, но не с JavaScript. Если у вас есть человек, который хочет научиться программировать, то покажите ему Pascal или C.
В чем отличие? Pascal может быть и компилируемым и интерпретируемым, а JavaScript требует уже интерпретации. Самое важное отличие – это статическая типизация.
Потому что, когда мы пишем на Pascal, мы указываем переменные, которые необходимы, а потом пишем их типы. Потом компилятору легко построить хороший оптимизированный код. Как мы обращаемся к переменным в память? У нас есть адрес, и у нас есть сдвиг. Например, Integer 32, то мы делаем сдвиг на 32 по этому адресу в память и получаем данные.
В JavaScript нет, у нас типы всегда меняются во время выполнения, и компилятор, когда выполняет этот код, первый раз он его выполняет как есть, но собирает информацию о типах. И второй раз, когда он выполняет ту же самую функцию, он уже основываясь на данных, которые он получил в прошлый раз, предположив, какие были там типы, может сделать какую-то оптимизацию.
Если с переменными все понятно, они определяются по значению, то что у нас с объектами?
Ведь у нас JavaScript, у него прототипная модель, и классов для объектов у нас нет. На самом деле есть, но они не видны. Это так называемые Hidden Classes. Они видны только самому компилятору.
Как создаются Hidden Classes?
У нас есть point – это конструктор, и создаются объекты. Сначала создается hidden class, который содержит только сам point.
Дальше у нас устанавливается свойство этого объекта x и из того, что у нас был hidden class, создаётся следующий hidden class, который содержит x.
Дальше у нас устанавливается y и, соответственно, мы получаем еще один hidden class, который содержит x и y.
Так мы получили три hidden class. После этого, когда мы создаем второй объект, используя тот же самый конструктор, происходит то же самое. Уже есть hidden classes, их уже не нужно создавать, с ними необходимо только сопоставить. Для того, чтобы позже мы знали, что эти два объекта одинаковы по структуре. И с ними можно похоже работать.
Но что происходит, когда мы позже еще добавляем свойство в объект p2? Создается новый hidden class, т.е. p1 и p2 уже не похожи. Почему это важно? Потому что, когда компилятор будет перебирать в цикле point, и вот у него будут все такие же, как p1, он их крутит, крутит, крутит, натыкается на p2, а у него другой hidden class, и компилятор уходит в деоптимизацию, потому что он получил не то, что ожидал.
Это так называемая утиная типизация. Что такое утиная типизация? Выражение появилось из американского сленга, если что-то ходит как утка, крякает как утка, то это утка. Т.е. если у нас p1 и p2 по структуре одинаковы, то они принадлежат к одному классу. Но стоит нам добавить в структуру p2 еще, и эти утки крякают по-разному, соответственно, это разные классы.
И вот мы получили данные о том, к каким классам относятся объекты, и получили данные о том, какого рода переменные, где эти данные использовать и как их хранить. Для этого используется система Inline Caches.
Рассмотрим, как происходит создание Inline Caches для этой части. Сначала, когда у нас код анализируется, он дополняется такими вызовами. Это только инициализация. Мы еще не знаем, какого типа будет у нас наш Inline Caches.
Мы можем сказать, что вот в этом месте проиницилизируй его, вот здесь загрузка this.primes:
Вот здесь загрузка по ключу:
А дальше операция BinaryOperation — это не значит, что она двоичная, это значит что она бинарная, а не унарная операция. Операция, у которой есть левая и правая части.
Что происходит во время выполнения?
Когда код доходит, это все подменяется на кусочки кода, которые уже у нас есть внутри компилятора, и компилятор знает, как хорошо работать с этим конкретным случаем, если мы имеем информацию о типе. То есть вот здесь подменяется на вызов кода, который знает, как получить primes из объекта:
Вот здесь подменяется на код, который знает, как получить элемент из массива SMI:
Здесь на код, который знает как посчитать остаток от деления двух SMI:
Он уже будет оптимизирован. И вот так примерно работал компилятор, насыщая такими кусочками.
Это, конечно, дает некоторый overhead, но и дает производительность.
У нас интернет развивался, увеличивалось количество JavaScript, требовалось большая производительность, и компания Google ответила созданием нового компилятора Crankshaft.
Старый компилятор стал называться FullCodegen, потому что он работает с полной кодовой базой, он знает весь JavaScript, как его компилировать. И он производит неоптимизированный код. Если он натыкается на какую-то функцию, которая вызывается несколько раз, он считает, что она стала горячей, и он знает, что компилятор Crankshaft может ее оптимизировать. И он отдает знания о типах и о том, что эту функцию можно оптимизировать в новый компилятор Crankshaft. Дальше новый компилятор заново получает абстрактное синтаксическое дерево. Это важно, что он получает не от старого компилятора AST, а снова идет и запрашивает AST. И зная о типах, делает оптимизацию, и на выходе мы получаем оптимизированный код.
Если он не может сделать оптимизацию, он сваливается в деоптимизацию. Когда это происходит? Вот как я сказал раньше, например, у нас в цикле Hidden Class крутится, потом неожиданное что-то и мы вывалились в деоптимизацию. Или, например, многие любят делать проверку, когда у нас есть что-то в левой части, и мы берем, например, длину, т.е. мы проверяем, есть ли у нас строка, и берем ее длину. Чем это плохо? Потому, что когда у нас строки нет, то в левой части у нас получается Boolean и на выходе получается Boolean, а до этого шел Number. И вот в этом случае мы сваливаемся в деоптимизацию. Или он встретил код, не может его оптимизировать.
На примере того же кода. Вот у нас был код насыщенный инлайн-кэшами, он весь инлайнится в новом компиляторе.
Он вставляет это все инлайново. Причем этот компилятор является спекулятивным оптимизирующим компилятором. На чем он спекулирует? Он спекулирует на знаниях о типах. Он предполагает, что если мы 10 раз вызвали с этим типом, то и дальше будет этот тип.
Везде есть такие проверки на то, что пришел тот тип, который он ожидал, и когда приходит тип, которого он не ожидал, то он сваливается в деоптимизацию. Эти улучшения дали хороший прирост производительности, но постепенно команда, занимающаяся движком V8, поняла, что все надо начать с нуля. Почему? Вот есть такой способ разработки ПО, когда мы пишем первую версию, а вторую версию мы пишем с нуля, потому что мы поняли, как надо было писать. И создали новый компилятор – Turbofan в 2014 году.
У нас есть исходный код, который попадает в парсер, далее в компилятор FullCodegen. Так было до этого, никаких отличий. На выходе мы получаем неоптимизированный код. Если мы можем сделать какую-либо оптимизацию, то мы уходим в два компилятора, Crankshaft и Turbofan. FullCodegen сам решает, может ли оптимизировать конкретные вещи компилятор Turbofan, и если может, то отправляет в него, а если не может, то отправляет в старый компилятор. Туда постепенно стали добавлять новые конструкции из ES6.
Начали с того, что заоптимизировали asm.js в него.
Зачем нужен новый компилятор?
Улучшить базовую производительность
Сделать производительность предсказуемой
Уменьшить сложность исходного кода
Что значит «улучшить базовую производительность»?
Старый компилятор был написан в те годы, когда у нас стояли мощные десктопы. И его тестировали на таких тестах, как octane, синтетических, которые проверяли пиковую производительность. Недавно была конференция Google I/O, и там менеджер, управляющий разработкой V8, завил, что они отказались в принципе от octane, потому что он не соответствует тому, с чем на самом деле работает компилятор. И это привело к тому, что у нас была очень хорошая пиковая производительность, но очень просела базовая, т.е. были не заоптимизированы вещи в коде, и когда код, хорошо работающий, натыкался на такие вещи, то шло значительное падение производительности. И таких операций скопилось много, вот несколько из них: forEach, map, reduce. Они написаны на обычном JS, нашпигованы проверками, сильно медленней, чем for. Часто советовали использовать for.
Медленная операция bind – она реализована внутри, оказывается, совершенно ужасно. Многие фреймворки писали свои реализации bind. Часто люди говорили, что я сел, написал на коленке bind и он работает быстрее, удивительно. Функции, содержащие try{}catch(e){}(и finally), – очень медленные.
Часто встречалась такая табличка, что лучше не использовать, чтобы не просела производительность. На самом деле код работает медленно, потому что компилятор работает неправильно. И с приходом Turbofan можно забыть об этом, потому что все уже заоптимизировано. Также очень важно: была улучшена производительность асинхронных функций.
Поэтому все ждут релиза новой node’ы, которая недавно вышла, там важна как раз производительность с async/await’ами. У нас язык асинхронный изначально, а пользоваться хорошо мы могли только callback’ами. И кто пишет с promise, знают, что их сторонние реализации работают быстрее, чем нативная реализация.
Следующей была задача сделать производительность предсказуемой. Была ситуация такая: код, который отлично показывал себя на jsPerf, при вставке в рабочий код показывал уже совсем другую производительность. Но и другие такие же случаи, когда мы не могли гарантировать, что наш код будет работать так же производительно, как мы изначально предполагали.
Например, у нас есть такой довольно простой код, который вызывает mymax, и если мы проверим его (при помощи ключей trace-opt и trace-deopt – показывают, какие функции были оптимизированы, а какие нет).
Мы можем запустить это с node, а можем и с D8 – специальной средой, где V8 работает отдельно от браузера. Она нам показывает, что оптимизации были отключены. Потому что слишком много раз запускался на проверку. В чем проблема? Оказывается, псевдомассив arguments — слишком большой, и внутри, оказывается, стояла проверка на размер этого массива. Причем эта проверка, как сказал Benedikt Meurer (ведущий разработчик Turbofan), не имела никакого смысла, она просто copypaste-ом с годами переходила.
И почему ограничена длина? Ведь не проверяется размер стека, ничего, вот просто так была ограничена. Это неожиданное поведение, от которого необходимо было избавляться.
Другой пример, вот у нас есть dispatcher, который вызывает два callback. Так же, если мы его вызовем, то увидим, что он был деоптимизирован. В чем здесь проблема? В том, что одна функция является strict, а вторая не strict. И у них в старом компиляторе получаются разные hidden classes. Т.е. он считает их разными. И в этом случае он так же уходит на деоптимизацию. И этот, и предыдущий код, он написан в принципе правильно, но он деоптимизируется. Это неожиданно.
Еще был вот такой пример в твиттере, когда оказалось, что в некоторых случаях цикл for в chrome работал даже медленнее, чем reduce. Хотя мы знаем, что reduce медленнее. Оказалось, проблема в том, что внутри for использовался let – неожиданно. Я поставил даже последнюю версию на тот момент и результат уже хороший – исправили.
Следующий пункт был — уменьшить сложность. Вот у нас была версия V8 3.24.9 и она поддерживала четыре архитектуры.
Сейчас же V8 поддерживает девять архитектур!
И код копился годами. Он был написан частично на C, Assembler, JS, и вот так примерно ощущал себя разработчик, который приходил в команду.
Код должен легко изменяться, чтобы можно было реагировать на изменения в мире. И с введением Turbofan количество архитектурно-специфичного кода уменьшилось.
С 2013 по 2017 года стало на 29% меньше архитектурно-специфичного кода. Это произошло за счет появления новой архитектуры генерации кода в Turbofan.
Они ее сделали управляемой данными, т.е. у нас есть граф управляющего потока, который содержит данные и знание о том, что с ними должно произойти. И он попадает в общий селектор команд, потом происходит резервирование регистров и далее генерация кода под разные архитектуры. Т.е. разработчику уже не надо знать, как написано все под конкретные архитектуры, а можно сделать более общий код. Вот так все происходило, хорошо улучшали код, но постепенно через несколько лет, после того как написали компилятор для интерпретируемого языка, оказалось, что все же нужен интерпретатор.
А в чем причина? Причину держит в руках Стив Джобс.
Это, конечно, не сам iPhone, а те смартфоны, которые породил iPhone, которые дали удобный доступ в интернет. И это привело к тому, что количество пользователей на мобильных устройствах превысило количество на десктопах.
А первоначально компиляторы разрабатывались для мощных десктопов, а не для мобильных устройств.
Вот схема времени первичного анализа 1МБ JavaScript. И недавно был вопрос, почему ВКонтакте делает серверный рендеринг, а не клиентский. Потому что время, потраченное на анализ JS, может быть в 2-5 раз больше на мобильных устройствах. И это мы говорим о топовых устройствах, а люди зачастую ходят с совсем другими.
И еще одна проблема: у многих китайских устройствах памяти 512 МБ, а если посмотреть, как происходит распределение памяти V8, то появляется еще одна проблема.
Память делится на объекты (то что использует наш код) и кодовые объекты (это то, что использует сам компилятор — например, хранит там inline caches). Получается, что 30% памяти занято виртуальной машиной для поддержки внутреннего использования. Мы не можем этой памятью управлять, ее сам компилятор потребляет.
С этим необходимо было что-то делать, и в 2016 году команда разработчиков Android из Лондона ответила созданием нового интерпретатора Ignition.
Вы могли заметить, что код, оптимизированный компилятором Turbofan, не обращается за синтаксическим деревом в парсер, а получает что-то из интерпретатора. Он получает байт-код.
Теперь абстрактное синтаксическое дерево парсится в байт-код, и этот парсинг JavaScript происходит один раз, дальше используется байткод.
Если кто-то не знает, байткод – это компактное платформонезависимое представление программы. Это чем-то похоже на assembler, только платформонезависимый. Так он называется, потому что все инструкции занимают один байт.
Посмотрим, как для такого кусочка программы генерируется байткод.
У нас есть программа, у нас есть сгенерированный байткод и у нас есть набор регистров.
Так мы устанавливаем значения для этих регистров входные, которые попадут в нашу программу. На первом этапе мы загружаем в специальный регистр accumulator (он нужен для того, чтобы не расходовать лишний раз регистры, а участвует только в вычислениях) smi integer равный 100.
Следующая команда нам говорит, что нужно вычесть из регистра a2 (в табличке видим там 150) предыдущее значение аккумулятора (100). В accumulator мы получили 50.
Дальше нам команда говорит, что нужно сохранить в r0. Он связан с переменной d.
Дальше становится более понятно. Снова загружаем значение из b, умножаем на значение accumulator, добавляем a0 и получаем на выходе, соответственно, 105.
И вся наша программа превращается в длинную цепочку из байткода. Таким образом уменьшилось потребление памяти, которое уходило на хранение нашего кода.
Была вторая проблема, это память, которую потребляли inline caches. Для этого перешли на новые кеши – Data-driven IC, которые уменьшают стоимость медленного пути. Медленный путь – это как работает не оптимизированный код, быстрый код – когда он оптимизирован.
Слева мы видим старую схему. Когда нам нужно найти какое-либо поле в объекте, мы храним знание о том, где оно лежит в объекте, где-то храним этот объект и умеем с ним обращаться. В новой схеме существует управляющий вектор, в котором есть данные и команды, и знание о том, что с этими командами делать. И он проходит по загрузке inline caches, на быстрый путь, если деоптимизация, то на медленный путь. И, соответственно, эта схема уже не требует хранения обращений к объектам, и она получается компактней. В итоге после внедрения схемы потребление памяти на одинаковом коде уменьшилось.
И наконец в этом году схема сильно упростилась.
Здесь мы всегда работаем в компиляторе Turbofan. Можно заметить, что раньше компилятор FullCodegen знал весь JS, а компилятор Crankshaft — только часть JS, а теперь компилятор Turbofan знает весь JS и работает со всем JS. Если он не может оптимизировать, он выдает неоптимизированный код, если может, то, соответственно, оптимизированный. И за кодом он обращается в интерпретатор.
У нас есть классы, которые не видны (многие знают, что в ES6 есть новые классы, но это просто сахар). За ними необходимо следить, ибо код для хорошей производительности должен быть мономорфным, а не полиморфным. Т.е. если у нас изменяются входящие в функцию классы, у них меняется hidden class – у объектов, которые попадают в нашу функцию, то код становится полиморфным и он плохо оптимизируется. Если у нас объекты приходят одного типа hidden class, то, соответственно, код мономорфный.
В V8 код проходит через интерпретатор и JIT-компилятор. Задача JIT-компилятора — сделать код быстрее. JIT-компилятор прогоняет наш код в цикле и каждый раз, основываясь на данных, которые получил в прошлый раз, пытается сделать код лучше. Он наполняет его знанием о типах, которые получает при первом прогоне, и за счет этого делает какие-то оптимизации. В нем внутри лежат кусочки, которые ориентированы для работы с максимальной производительностью. Если у нас в коде есть a+b – это медленно. Мы знаем, что это number+number или string+string, мы можем сделать это быстро. Вот этим занимается JIT-компилятор.
Чем лучше оптимизация, тем выше накладные расходы (время, память). Задача же интерпретатора – уменьшить накладные расходы на память. Даже с приходом Turbofan отказались от некоторых оптимизаций, которые были ранее, потому что решили повышать базовую производительность и немного снижать пиковую.
В компиляторе есть два режима работы – холодный и горячий. Холодный, это когда наша функция запущена в первый раз. Если функция запускалась несколько раз, то компилятор понимает, что она уже горяча, и пытается ее оптимизировать. Здесь есть засада с тестами. Когда разработчики гоняют тесты по много раз и получают какие-либо данные, то это уже оптимизированный горячий код. А в реальности этот код может быть вызван один-два раза и показывать совсем другую производительность. Это необходимо учитывать.
С мономорфным кодом такой же пример. То есть когда мы пишем код, мы можем помочь нашему компилятору. Мы можем писать код так, словно у нас типизированный язык (не переопределять переменные, не записывать в них разные типы, возвращать всегда один и тот же тип).
Вот и все основные секреты.
Ссылки для чтения github.com/v8/v8/wiki/TurboFan http://benediktmeurer.de/ http://mrale.ph/ http://darksi.de/ https://medium.com/@amel_true
Если вы любите JS так же, как мы, и с удовольствием копаетесь во всей его нутрянке, вам могут быть интересные вот эти доклады на грядущей московской конференции
HolyJS
:
Двигатель JavaScript V8
V8 — это название движка JavaScript, на котором работает Google Chrome. Это то, что берет наш JavaScript и выполняет его при просмотре в Chrome.
V8 предоставляет среду выполнения, в которой выполняется JavaScript. ВДОМ, и другиеAPI веб-платформыпредоставляются браузером.
Замечательно то, что движок JavaScript не зависит от браузера, в котором он размещен. Эта ключевая особенность способствовала ростуNode.js. V8 был выбран в качестве движка, на котором работал Node.js еще в 2009 году, и когда популярность Node.js резко возросла, V8 стал движком, который теперь поддерживает невероятное количество серверного кода, написанного на JavaScript.
Экосистема Node.js огромна, и благодаря ей V8 также поддерживает настольные приложения с такими проектами, как Electron.
Другие движки JS
В других браузерах есть собственный движок JavaScript:
Firefox имеетОбезьяна паук
Safari имеетJavaScriptCore(также называется нитро)
Edge имеетЧакра
и многие другие тоже существуют.
Все эти двигатели реализуют стандарт ECMA ES-262, также называемыйECMAScript, стандарт, используемый JavaScript.
В поисках производительности
V8 написан на C ++ и постоянно совершенствуется. Он портативен и работает на Mac, Windows, Linux и некоторых других системах.
В этом введении V8 я проигнорирую детали реализации V8: их можно найти на более авторитетных сайтах (например, на официальном сайте V8), и они со временем меняются, часто радикально.
V8 постоянно развивается, как и другие движки JavaScript, чтобы ускорить Интернет и экосистему Node.js.
В Интернете идет гонка за производительностью, которая продолжается годами, и мы (как пользователи и разработчики) извлекаем большую выгоду из этой конкуренции, потому что год за годом мы получаем более быстрые и оптимизированные машины.
Компиляция
JavaScript обычно считается интерпретируемым языком, но современные движки JavaScript больше не просто интерпретируют JavaScript, они его компилируют.
Это происходит с 2009 года, когда в Firefox 3.5 был добавлен компилятор JavaScript SpiderMonkey, и все последовали этой идее.
Это может показаться нелогичным, но с момента появления Google Maps в 2004 году JavaScript превратился из языка, который обычно выполнял несколько десятков строк кода для завершения приложений с тысячами и сотнями тысяч строк, выполняемых в браузере.
Наши приложения теперь могут работать в течение нескольких часов в браузере, а не представляют собой всего лишь несколько правил проверки форм или простых скриптов.
В этомНовый мир, компиляция JavaScript имеет смысл, потому что, хотя для JavaScript может потребоваться немного больше времени,готовы, после завершения он будет намного более производительным, чем чисто интерпретируемый код.
Больше руководств по инструментам разработчика:
Движок JavaScript: что внутри. Разберёмся, как работает движок V8 и… | by Андрей Шагин | NOP::Nuances of Programming
Чтобы разобраться в том, как работает механизм обработки кода (иначе говоря, движок JavaScript), надо понять, что происходит при выполнении кода. Такие знания помогают программистам писать лучший, более быстрый и умный код.
Движки JavaScript — это не что иное, как программы, преобразующие код на JavaScript в код более низкого уровня, который компьютер сможет понять. Эти движки встроены в браузеры и веб-серверы (Node.js), что даёт возможность выполнять код и осуществлять компиляцию во время выполнения.
Разве JavaScript — это не интерпретируемый язык?
Краткий ответ: это зависит от реализации. Обычно JavaScript относят к интерпретируемым языкам, хотя вообще-то он компилируется. Современные компиляторы JavaScript фактически выполняют JIT-компиляцию, т.е. компиляцию «на лету», которая осуществляется во время работы программы.
Существует множество разных движков. У каждого из них внутри есть что-то вроде конвейера с интерпретатором и конвейера с оптимизатором и компилятором. Интерпретатор генерирует байт-код, а оптимизатор выдаёт оптимизированный машинный код. Далее в статье в качестве примера будет использоваться движок V8.
V8 — это высокопроизводительный движок от Google с открытым исходным кодом JavaScript и WebAssembly, написанный на языке C++.Он используется в Chrome, Node.js и других платформах и реализует ECMAScript и WebAssembly (см.v8.dev).
Всякий раз, когда JavaScript-код отправляется в движок V8, он проходит ряд этапов для отображения console.log:
Парсер
Движок выполняет то, что мы называем лексическим анализом. Это первое, что происходит с файлом JavaScript при попадании в движок. Код разбивается на части, называемые токенами, для выявления их назначения, после чего мы узнаём, что код пытается сделать.
Абстрактное синтаксическое дерево (AST)
На основе этих токенов создаётся то, что мы называем AST. Синтаксическое дерево — это древовидное представление синтаксической структуры кода JavaScript, и мы можем использовать этот инструмент для анализа преобразования кода AST.
Интерпретатор
Интерпретатор читает файлы JavaScript построчно и преобразовывает их на ходу (во время работы программы, не прерывая её выполнение). На основе сгенерированного кода AST интерпретатор начинает быстро создавать байт-код. Никаких оптимизаций здесь не выполняется, так что байт-код этот неоптимизированный.
Байт-код не является таким низкоуровневым, как машинный код, но всё же может быть интерпретирован движком JavaScript для выполнения кода.
Профайлер
Профайлер отвечает за проверку кода. Он вызывает специальное средство контроля, которое отслеживает код и наблюдает за ходом его выполнения, обращая наше внимание на то, как можно оптимизировать код. Выдаёт, например, информацию о том, сколько раз код запускался, какие типы используются и как мы можем его оптимизировать?
Так что, если профайлер находит часть кода, которую можно оптимизировать, он передаёт этот код JIT-компилятору, чтобы он мог быть скомпилирован и выполнялся быстрее. Рассмотрим эти конвейеры с интерпретатором и компилятором более подробно.
Оптимизирующий компилятор
Задача оптимизирующего компилятора — определить, что делает программа, подлежащая оптимизации, и создать из неё оптимизированную программу, выполняющую всё то же самое, только быстрее.
Он не преобразует файлы на лету. Он делает свою работу заранее, создавая преобразование только что написанного кода и компилируя его в язык, понятный для компьютера.
Деоптимизация
Оптимизирующий компилятор на основе имеющихся у него данных профилирования делает определённые предположения, а затем выдаёт высокооптимизированный машинный код. Но вполне возможно, что в какой-то момент одно из предположений окажется неверным. И тогда оптимизирующий компилятор может деоптимизировать код.
JavaScript — это динамический язык программирования, а это подразумевает, что свойства могут легко добавляться или удаляться из объекта после его создания. Для написания более лучшего кода надо понимать, как JavaScript определяет объекты и как движок работает с объектами и свойствами.
Enumerable → определяет, перечисляется ли свойство в циклах for—in.
Value → само значение.
Writable → определяет, можно ли свойство изменить.
Configurable → определяет, можно ли удалить свойство.
Оптимизация доступа к свойству
В динамических языках, таких как JavaScript, для доступа к свойствам требуется ряд инструкций. Поэтому почти в каждом движке имеется оптимизация для ускорения этого доступа. Такая оптимизация в V8 реализуется скрытыми классами: V8 присоединяет скрытый класс к каждому отдельному объекту. Целью скрытых классов является оптимизация времени доступа к свойствам.
Скрытые классы работают аналогично фиксированным макетам (классам) объектов, используемым в таких языках, как Java (за исключением того, что они создаются во время выполнения). Магия здесь в том, что несколько объектов могут иметь один и тот же скрытый класс, поэтому необходимы только минимальные ресурсы, а код становится быстрее:
Выгода становится очевидной, когда объектов много. Ведь пока у них один и тот же скрытый класс, информацию приходится сохранять только один раз независимо от того, сколько всего имеется объектов!
Тема оптимизации очень обширна и может стать предметом обсуждения отдельной статьи.
Цель встроенного кэширования в том, чтобы ускорить привязку метода времени выполнения. Происходит это за счёт запоминания результатов поиска предыдущего метода непосредственно в месте вызова.Встроенное кэширование особенно полезно для динамически типизированных языков, где большинство, если не все привязки методов происходят во время выполнения и где виртуальные таблицы методов часто не могут быть использованы.
Основная причина существования скрытых классов — концепция встроенных кэшей. Движки JavaScript используют встроенные кэши для запоминания информации о том, где найти свойства в объектах. Поэтому нет необходимости повторять дорогостоящий поиск свойств при каждом доступе к ним. Зачем каждый раз искать свойства, когда со встроенными кэшами это значительно быстрее?
инициализировать объекты лучше всегда одним и тем же способом, чтобы скрытые классы у них не были разными;
с атрибутами свойств элементов массива надо быть осторожным, чтобы они могли аккуратно сохраняться, а работа с ними была эффективной.
Почти всё там изложенное я уже успел узнать сам из разных источников.
Рассказано, что движок JavaScript «V8» встроен в браузер «Google Chrome» и отвечает там за выполнение скриптов на языке JavaScript. Движок JavaScript «V8» не зависит от программы (изначально браузера), в которую он встроен. Поэтому стало возможным появление среды выполнения «Node.js», которая тоже использует этот движок.
Область применения среды выполнения «Node.js» огромна. В частности, благодаря движку JavaScript «V8», среду выполнения «Node.js» можно использовать для написания обычных приложений для настольного компьютера (по-английски «desktop applications» или «desktop apps») с помощью таких проектов, как фреймворк «Electron».
С помощью фреймворка «Electron» создан, к примеру, очень популярный сейчас редактор кода «Visual Studio Code».
Другие браузеры используют их собственные движки JavaScript. Например:
– браузер «Mozilla Firefox» использует движок JavaScript «SpiderMonkey»: https://spidermonkey.dev
– браузер «Safari» (разработанный компанией «Apple») использует движок JavaScript «JavaScriptCore» (также его называют «Nitro»): https://developer.apple.com/documentation/javascriptcore
– браузер «Microsoft Edge» первоначально использовал движок JavaScript «Chakra»: https://github.com/chakra-core/ChakraCore
Но сейчас браузер «Microsoft Edge» перестроен для работы на базе браузера «Chromium» и поэтому стал использовать движок JavaScript «V8».
Кроме перечисленных существуют и другие движки JavaScript.
Движок JavaScript «V8» написан на языке программирования «C++» и постоянно улучшается. Он портирован для работы в операционных системах «macOS», «Windows», «Linux» и некоторых других. Детали реализации этого движка можно найти на его официальном сайте:
https://v8.dev
Все движки JavaScript участвуют в гонке за производительностью (скоростью работы). Изначально язык программирования JavaScript считался интерпретируемым языком программирования, но сейчас это не так: современные движки компилируют код.
Ускорение работы движка дало программистам возможность увеличить размер программ, написанных на языке JavaScript, с нескольких десятков строк кода до полноценных приложений, включающих от тысяч до сотен тысяч строк кода.
Движок JavaScript: что внутри
Введение
Чтобы разобраться в том, как работает механизм обработки кода (иначе говоря, движок JavaScript), надо понять, что происходит при выполнении кода. Такие знания помогают программистам писать лучший, более быстрый и умный код.
Движки JavaScript — это не что иное, как программы, преобразующие код на JavaScript в код более низкого уровня, который компьютер сможет понять. Эти движки встроены в браузеры и веб-серверы (Node.js), что даёт возможность выполнять код и осуществлять компиляцию во время выполнения.
Разве JavaScript — это не интерпретируемый язык?
Краткий ответ: это зависит от реализации. Обычно JavaScript относят к интерпретируемым языкам, хотя вообще-то он компилируется. Современные компиляторы JavaScript фактически выполняют JIT-компиляцию, т.е. компиляцию «на лету», которая осуществляется во время работы программы.
Движок
Существует множество разных движков. У каждого из них внутри есть что-то вроде конвейера с интерпретатором и конвейера с оптимизатором и компилятором. Интерпретатор генерирует байт-код, а оптимизатор выдаёт оптимизированный машинный код. Далее в статье в качестве примера будет использоваться движок V8.
V8 — это высокопроизводительный движок от Google с открытым исходным кодом JavaScript и WebAssembly, написанный на языке C++.Он используется в Chrome, Node.js и других платформах и реализует ECMAScript и WebAssembly (см.v8.dev).
Что внутри движка
Всякий раз, когда JavaScript-код отправляется в движок V8, он проходит ряд этапов для отображения console.log:
Парсер
Движок выполняет то, что мы называем лексическим анализом. Это первое, что происходит с файлом JavaScript при попадании в движок. Код разбивается на части, называемые токенами, для выявления их назначения, после чего мы узнаём, что код пытается сделать.
Абстрактное синтаксическое дерево (AST)
На основе этих токенов создаётся то, что мы называем AST. Синтаксическое дерево — это древовидное представление синтаксической структуры кода JavaScript, и мы можем использовать этот инструмент для анализа преобразования кода AST.
Интерпретатор
Интерпретатор читает файлы JavaScript построчно и преобразовывает их на ходу (во время работы программы, не прерывая её выполнение). На основе сгенерированного кода AST интерпретатор начинает быстро создавать байт-код. Никаких оптимизаций здесь не выполняется, так что байт-код этот неоптимизированный.
Байт-код не является таким низкоуровневым, как машинный код, но всё же может быть интерпретирован движком JavaScript для выполнения кода.
Профайлер
Профайлер отвечает за проверку кода. Он вызывает специальное средство контроля, которое отслеживает код и наблюдает за ходом его выполнения, обращая наше внимание на то, как можно оптимизировать код. Выдаёт, например, информацию о том, сколько раз код запускался, какие типы используются и как мы можем его оптимизировать?
Так что, если профайлер находит часть кода, которую можно оптимизировать, он передаёт этот код JIT-компилятору, чтобы он мог быть скомпилирован и выполнялся быстрее. Рассмотрим эти конвейеры с интерпретатором и компилятором более подробно.
Оптимизирующий компилятор
Задача оптимизирующего компилятора — определить, что делает программа, подлежащая оптимизации, и создать из неё оптимизированную программу, выполняющую всё то же самое, только быстрее.
Он не преобразует файлы на лету. Он делает свою работу заранее, создавая преобразование только что написанного кода и компилируя его в язык, понятный для компьютера.
Деоптимизация
Оптимизирующий компилятор на основе имеющихся у него данных профилирования делает определённые предположения, а затем выдаёт высокооптимизированный машинный код. Но вполне возможно, что в какой-то момент одно из предположений окажется неверным. И тогда оптимизирующий компилятор может деоптимизировать код.
Объектная модель JavaScript
JavaScript — это динамический язык программирования, а это подразумевает, что свойства могут легко добавляться или удаляться из объекта после его создания. Для написания более лучшего кода надо понимать, как JavaScript определяет объекты и как движок работает с объектами и свойствами.
Enumerable → определяет, перечисляется ли свойство в циклах for—in.
Value → само значение.
Writable → определяет, можно ли свойство изменить.
Configurable → определяет, можно ли удалить свойство.
Оптимизация доступа к свойству
В динамических языках, таких как JavaScript, для доступа к свойствам требуется ряд инструкций. Поэтому почти в каждом движке имеется оптимизация для ускорения этого доступа. Такая оптимизация в V8 реализуется скрытыми классами: V8 присоединяет скрытый класс к каждому отдельному объекту. Целью скрытых классов является оптимизация времени доступа к свойствам.
Скрытые классы работают аналогично фиксированным макетам (классам) объектов, используемым в таких языках, как Java (за исключением того, что они создаются во время выполнения). Магия здесь в том, что несколько объектов могут иметь один и тот же скрытый класс, поэтому необходимы только минимальные ресурсы, а код становится быстрее:
Выгода становится очевидной, когда объектов много. Ведь пока у них один и тот же скрытый класс, информацию приходится сохранять только один раз независимо от того, сколько всего имеется объектов!
Тема оптимизации очень обширна и может стать предметом обсуждения отдельной статьи.
Встроенные кэши
Цель встроенного кэширования в том, чтобы ускорить привязку метода времени выполнения. Происходит это за счёт запоминания результатов поиска предыдущего метода непосредственно в месте вызова.Встроенное кэширование особенно полезно для динамически типизированных языков, где большинство, если не все привязки методов происходят во время выполнения и где виртуальные таблицы методов часто не могут быть использованы.
Основная причина существования скрытых классов — концепция встроенных кэшей. Движки JavaScript используют встроенные кэши для запоминания информации о том, где найти свойства в объектах. Поэтому нет необходимости повторять дорогостоящий поиск свойств при каждом доступе к ним. Зачем каждый раз искать свойства, когда со встроенными кэшами это значительно быстрее?
Выводы
инициализировать объекты лучше всегда одним и тем же способом, чтобы скрытые классы у них не были разными;
с атрибутами свойств элементов массива надо быть осторожным, чтобы они могли аккуратно сохраняться, а работа с ними была эффективной.
Читайте также:
Перевод статьи Leonardo Freitas: Inside the JavaScript Engine
NodeJS. Движок V8.
Вы здесь:
Главная — JavaScript — JavaScript Основы — NodeJS. Движок V8.
Всем привет! В этой статье мы поговорим о том, что такое движок V8 и какую роль он играет в платформе NodeJS.
Что такое движок JavaScript?
Чтобы понять, как работает платформа NodeJS, мы немножко рассмотрим работу движка, на котором она основана.
Для начала нам нужно понять, что вообще такое движок javascript? На самом деле, здесь нет ничего необычного. Все дело в том, что компьютеры сами по себе не понимают javascript, т.к. это язык высокого уровня. Чтобы компьютер смог понять, что от него требуется, и существует движок, который берет написанный на javascript код и конвертирует его в машинный, понятный компьютеру код.
Это выглядит примерно так: сначала у нас есть javascript код, потом платформа, написанная на C++, далее язык ассемблера, а уже потом машинный код. И каждый раз, когда вы компилируете свой код, он проходит от верхнего уровня к нижнему.
Что такое V8 Engine?
Итак, что же такое V8 Engine? V8 Engine – это высокопроизводительный движок от корпорации Google с открытым исходным кодом. Он написан на C++ и используется в Google Chrome. Движок может работать автономно или быть установлен в любое C++ приложение.
Таким образом, чтобы javascript код был понятен компьютеру, платформа NodeJS, написанная на C++, берет его и передает движку V8, который преобразовывает язык высокого уровня в низкоуровневый машинный код.
Заключение
Итак, на этом сегодня, пожалуй, все. Мы рассмотрели, что такое V8 Engine и зачем он нужен в платформе NodeJS, а в следующей статье поговорим про глобальный объект.
Спасибо за внимание!
Создано 15.06.2016 19:30:03
Михаил Русаков
Предыдущая статья Следующая статья
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov. Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте, то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Кнопка:
<a href=»https://myrusakov.ru» target=»_blank»><img src=»https://myrusakov.ru/images/button.gif» alt=»Как создать свой сайт» /></a>
Она выглядит вот так:
Текстовая ссылка: <a href=»https://myrusakov.ru» target=»_blank»>Как создать свой сайт</a>
Она выглядит вот так: Как создать свой сайт
BB-код ссылки для форумов (например, можете поставить её в подписи):
[URL=»https://myrusakov.ru»]Как создать свой сайт[/URL]
Качественное и эффективное двигатель v8 для автомобилей Inspiring Driving Experience
Получите доступ к качественному, мощному и надежному сервису. двигатель v8 на Alibaba.com за повышение производительности двигателей и значительное увеличение срока их службы. Эти емкостные и прочные. двигатель v8 подходят не только для транспортных средств, но и идеально подходят для всех типов тяжелой техники. Качество этих. двигатель v8 абсолютно превосходны, и они созданы с использованием новейших технологий для лучшей поддержки двигателей и их бесперебойной работы.
Замечательное и выдающееся. двигатель v8, представленные на сайте, предлагаются некоторыми из ведущих поставщиков и оптовых торговцев, которые на протяжении долгого времени преуспели в поставке запчастей высокого качества для машин. Эти крепкие. двигатель v8 антифрикционные, стабильно работающие и экологически чистые — самые большие преимущества этих продуктов. Вы можете выбрать из множества вариантов бензиновых и дизельных двигателей. двигатель v8 совместим со всеми видами моделей.
При покупке они эффективны и безупречны. двигатель v8 на Alibaba.com вы можете выбирать между различными вариантами продуктов в зависимости от их размеров, мощности, крутящего момента, разновидностей радиаторов и моделей в соответствии с вашими конкретными требованиями. Файл. двигатель v8 доступны здесь, а именно коромысла, толкатель распределительного вала, подшипник штока, радиатор и многое другое, что позволяет получить доступ ко всем типам деталей. Файл. двигатель v8 все сертифицированы ISO, SGS, CE, IAF для обеспечения оптимального качества.
Изучите различные. двигатель v8 представлен на Alibaba.com и экономит деньги при покупке продуктов. Все эти продукты доступны как OEM-заказы при оптовых закупках вместе с вариантами индивидуальной настройки упаковки и продуктов. Вас ждут большие скидки на эти товары.
Блог
· Блог V8
· V8
Библиотека Oilpan внутренняя память cppgc
V8 release v9.7
WebAssembly Dynamic Tiering готов к испытанию в Chrome 96 WebAssembly
Сверхбыстрый super доступ к собственности JavaScript
Быстрее JavaScript вызывает внутреннее устройство
Выпуск V8 v8.9
Дополнительный механизм RegExp без обратного отслеживания Внутренние элементы RegExp
V8, выпуск v8.7
Отслеживание резервов в V8 внутренности
V8 release v8.6 release
V8 release v8.5 release
V8 release v8.4
Высокопроизводительная сборка мусора для C ++ внутренняя память cppgc
Понимание спецификации ECMAScript, часть 4 ECMAScript Понимание ECMAScript
До 4 ГБ памяти в WebAssembly Инструментальные средства JavaScript WebAssembly
Выпуск V8 v8.3
Что в этом .wasm ? Представляем: wasm-decompile Инструментальные средства WebAssembly
Понимание спецификации ECMAScript, часть 3 ECMAScript Понимание ECMAScript
Сжатие указателя в V8
V8 release v8.1 25 февраля 2020 г. , выпуск
Понимание спецификации ECMAScript, часть 1 03 февраля 2020 г. ECMAScript Понимание ECMAScript
V8, выпуск v8.0 18 декабря 2019 г. Ноябрь 2019 г. Инструментальные средства WebAssembly
Выпуск 8 версии 7.9 20 ноября 2019 г. Выпуск
Улучшение регулярных выражений V8 04 октября 2019 г. внутреннее устройство RegExp
выпуск 8 версии v7.8 27 сентября 2019 г., выпуск
Легче V8 12 сентября 2019 г. презентаций внутренней памяти
История падения производительности V8 в React 28 августа 2019 г. выпуск
Emscripten и бэкэнд LLVM WebAssembly 01 июля 2019 г. Инструментальные средства WebAssembly
Стоимость JavaScript в 2019 г. 25 июня 2019 г. внутреннего анализа
V8 release v7.6 19 июня 2019 г., выпуск
Кэширование кода для разработчиков WebAssembly 17 июня 2019 г. Внутренние компоненты WebAssembly
, выпуск V8, версия 7.5 16 мая 2019 г. Выпуск
Более быстрые и многофункциональные API-интерфейсы интернационализации 25 апреля 2019 г. ECMAScript Intl
Год со Spectre: перспектива V8 23 апреля 2019 г. security
Невероятно быстрый анализ, часть 2: ленивый анализ 15 апреля 2019 г. внутренний анализ
Кэширование кода для разработчиков JavaScript 08 апреля 2019 г. внутреннее
Невероятно быстрый синтаксический анализ, часть 1: оптимизация сканера 25 марта 2019 г. внутренний анализ
V8 release v7.4 22 марта 2019 г., выпуск
V8 без JIT 13 марта 2019 г.
V8 release v7.2 18 декабря 2018 release
Ускорение элементов распространения 4 декабря 2018 Тесты ECMAScript
Более быстрые асинхронные функции и обещания 12 ноября 2018 Презентации тестов ECMAScript
V8 release v7.1 31 октября 2018 г. , выпуск
V8, выпуск v7.0 15 октября 2018 г., , выпуск
Рассортировка вещей в V8 28 сентября 2018 г.
Празднование 10-летия V8 11 сентября 2018 тестов
Liftoff: новый базовый компилятор для WebAssembly в V8 20 августа 2018 г. Внутренние компоненты WebAssembly
Встроенные компоненты 14 августа 2018 г.9 07 августа 2018 г., выпуск
, выпуск V8, версия 6.8 21 июня 2018 г. to V8 02 мая 2018 г. ECMAScript
Улучшенное кэширование кода 24 апреля 2018 г. внутреннее
V8 release v6.6 27 марта 2018 release
Фоновая компиляция 26 марта 2018 г. внутреннее
Трассировка от JS до DOM и обратно 01 марта 2018 г. внутренняя память
Ленивая десериализация 12 февраля 2018 г. внутренняя память
Выпуск V8 v6.5 01 февраля 2018 г. Выпуск
Оптимизация хеш-таблиц: скрытие хэш-кода 29 января 2018 г. внутренности
Chrome приветствует спидометр 2.0! 24 января 2018 г. тестов
Выпуск V8 v6.4 19 декабря 2017 г. Выпуск
Покрытие кода JavaScript 13 декабря 2017 г. Внутренние компоненты
Ориноко: сборка мусора молодого поколения 29 ноября 2017 г. внутренняя память
Сложность приручения архитектуры в V8 — CodeStubAssembler 16 ноября 2017 г. internals
Объявление Web Tooling Benchmark 06 ноября 2017 г. тестов Node.js
V8 release v6.3 25 октября 2017 г. Выпуск
Оптимизация прокси ES2015 в V8 05 октября 2017 г. Внутренние компоненты тестов ECMAScript
Стажировка по лени: ленивое отключение деоптимизированных функций 4 октября 2017 г. память Временное отключение анализа побега 22 сентября 2017 г. security
Виды элементов в V8 12 сентября 2017 г. внутренние презентации
V8 release v6.2 11 сентября 2017 г., выпуск
Свойства быстрого доступа в V8 30 августа 2017 г. выпуск
выпуск V8 v6.0 09 июня 2017 г. выпуск
Запуск Ignition и TurboFan 15 мая 2017 г. Внутренние компоненты
выпуск V8 v5.9 27 апреля 2017 г. выпуск
Прекращение использования тестов Octane 12 апреля 2017 г. 9000
V8, выпуск v5.8 20 марта 2017 г. release
Fast для — в в V8 01 марта 2017 г. внутренности
Высокопроизводительный ES2015 и выше 17 февраля 2017 г. ECMAScript
Помогите нам проверить будущее V8! 14 февраля 2017 г. внутренности
Один маленький шаг для Chrome, одна гигантская куча для V8 09 февраля 2017 г. память
V8 release v5.7 06 февраля 2017 release
Ускорение регулярных выражений V8 10 января 2017 г. внутренние компоненты RegExp
Как V8 измеряет реальную производительность 21 декабря 2016 г. тестов
V8 ❤️ Node.js 15 декабря 2016 г. Node.js
V8 release v5.6 02 декабря 2016 release
Предварительная версия браузера WebAssembly 31 октября 2016 WebAssembly
V8 release v5.5 24 октября 2016 release
Оптимизация V8 потребление памяти 07 октября 2016 г. тесты памяти
выпуск V8 v5.4 09 сентября 2016 г. выпуск
Запуск интерпретатора Ignition 23 августа 2016 г. внутренних компонентов
V8 на конференции BlinkOn 6 21 июля 2016 г. презентаций
V8, выпуск v5.3 18 июля 2016 г. , выпуск
V8, выпуск v5.2 4 июня 2016 г. Busters, часть вторая: Orinoco 12 апреля 2016 г. внутренняя память
Версия V8 v5.0 15 марта 2016 г. версия
Экспериментальная поддержка WebAssembly в V8 15 марта 2016 г. WebAssembly
RegExp lookbehind assertions
ECM 26 февраля 2016 г. RegExp
Дополнения V8 04 февраля 2016 г. внутренности
Выпуск V8 v4.9 26 января 2016 г. , выпуск
Есть Math.random () , а затем Math.random () 17 декабря 2015 г. Внутренние компоненты ECMAScript
V8, выпуск v4.8 25 ноября 2015 г. Jank Busters, часть первая 30 октября 2015 г. памяти
V8, выпуск v4.7 14 октября 2015 г. release
Пользовательские снимки состояния запуска 25 сентября 2015 г. внутреннее устройство
V8 release v4.6 28 августа 2015 выпуск
Получение сборка мусора бесплатно 07 августа 2015 г. внутренняя память
Кэширование кода 27 июля 2015 г. внутренняя часть
V8 release v4.5 17 июля 2015 г. , выпуск
Копаемся в TurboFan JIT 13 июля 2015 г. внутренности
Привет, мир! 10 июля 2015 г.
Выпуск V8 v9.5 · V8
Каждые четыре недели мы создаем новую ветвь V8 как часть процесса выпуска. Каждая версия является ответвлением от мастера Git V8 непосредственно перед этапом бета-тестирования Chrome. Сегодня мы рады анонсировать нашу новейшую ветку, V8 версии 9.5, которая находится в стадии бета-тестирования до ее выпуска в координации с Chrome 95 Stable через несколько недель.V8 v9.5 наполнен всевозможными полезностями, предназначенными для разработчиков. Этот пост представляет собой предварительный просмотр некоторых основных моментов в преддверии релиза.
JavaScript #
Intl.DisplayNames v2 #
В версии 8.1 мы запустили API Intl.DisplayNames API в Chrome 81 с поддерживаемыми типами «язык», «регион», «сценарий» и « валюта». В версии 9.5 мы добавили два новых поддерживаемых типа: «календарь» и «dateTimeField». Они возвращают отображаемые имена различных типов календаря и полей даты и времени соответственно:
const esCalendarNames = new Intl.DisplayNames (['es'], {type: 'calendar'}); const frDateTimeFieldNames = новый Intl.DisplayNames (['fr'], {type: 'dateTimeField'}); esCalendarNames.of ('roc'); frDateTimeFieldNames.of ('месяц');
Мы также улучшили поддержку типа «язык» с помощью новой опции languageDisplay, которая может быть либо «стандартной», либо «диалектной» (в качестве значения по умолчанию, если не указано):
Intl.DateTimeFormat API в v9.5 теперь поддерживает четыре новых значения для параметра timeZoneName :
«shortGeneric» для вывода названия часового пояса в коротком общем формате без определения местоположения, например «PT», «ET», без указания того, находится ли он в режиме летнего времени.
«longGeneric» для вывода названия часового пояса в коротком общем формате, не связанном с местоположением, например «Тихоокеанское время», «Горное время», без указания того, используется ли летнее время.
«shortOffset» для вывода названия часового пояса в кратком локализованном формате GMT, таком как «GMT-8».
«longOffset» для вывода названия часового пояса в длинном локализованном формате GMT, например, «GMT-0800».
WebAssembly #
Обработка исключений #
V8 теперь поддерживает предложение по обработке исключений WebAssembly (Wasm EH), поэтому модули скомпилированы с помощью совместимой цепочки инструментов (e.г. Emscripten) может выполняться в V8. Это предложение призвано снизить накладные расходы по сравнению с предыдущими обходными путями с использованием JavaScript.
Например, мы скомпилировали оптимизатор Binaryen в WebAssembly со старой и новой реализациями обработки исключений.
Когда обработка исключений включена, увеличение размера кода снижается с примерно 43% для старой обработки исключений на основе JavaScript до всего 9% для новой функции Wasm EH.
Когда мы запустили wasm-opt.wasm -O3 на нескольких больших тестовых файлах, версия Wasm EH не показала потери производительности по сравнению с базовой линией без исключений, в то время как версия EH на основе JavaScript заняла примерно на 30% больше времени.
Однако Binaryen редко использует проверку исключений. Ожидается, что в рабочих нагрузках с большим количеством исключений разница в производительности будет еще больше.
V8 API #
Главный файл заголовка v8.h разделен на несколько частей, которые могут быть включены отдельно. Например, v8-isolate.h теперь содержит v8 :: Isolate class . Многие файлы заголовков, которые объявляют методы, передающие v8 :: Local, теперь могут импортировать v8-forward.h , чтобы получить определение v8 :: Local и все типы объектов кучи v8.
Используйте git log branch-heads / 9.4..branch-Head / 9.5 include / v8 \ *. H , чтобы получить список изменений API.
Разработчики с активной проверкой V8 могут использовать git checkout -b 9.5 -t branch-heads / 9.5 , чтобы поэкспериментировать с новыми функциями в V8 v9.5. Вы также можете подписаться на канал бета-версии Chrome и вскоре сами опробовать новые функции.
Механизм JavaScript V8
V8 — это имя механизма JavaScript, на котором работает Google Chrome.Это то, что берет наш JavaScript и выполняет его при просмотре в Chrome.
V8 предоставляет среду выполнения, в которой выполняется JavaScript. DOM и другие API веб-платформы предоставляются браузером.
Замечательно то, что движок JavaScript не зависит от браузера, в котором он размещен. Эта ключевая функция способствовала развитию Node.js. V8 был выбран в качестве движка, на котором работал Node.js еще в 2009 году, и когда популярность Node.js резко возросла, V8 стал движком, который теперь поддерживает невероятное количество серверного кода, написанного на JavaScript.
Экосистема Node.js огромна и благодаря V8, который также поддерживает настольные приложения с такими проектами, как Electron.
Другие механизмы JS
В других браузерах есть собственный механизм JavaScript:
и многие другие существуют также.
Все эти движки реализуют стандарт ECMA ES-262, также называемый ECMAScript, стандартом, используемым JavaScript.
В поисках производительности
V8 написан на C ++ и постоянно совершенствуется. Он портативен и работает на Mac, Windows, Linux и некоторых других системах.
В этом введении V8 мы проигнорируем детали реализации V8: их можно найти на более авторитетных сайтах (например, на официальном сайте V8), и они со временем меняются, часто радикально.
V8 постоянно развивается, как и другие движки JavaScript, чтобы ускорить Интернет и экосистему Node.js.
В Интернете идет гонка за производительностью, которая продолжается годами, и мы (как пользователи и разработчики) много выигрываем от этой конкуренции, потому что год за годом мы получаем более быстрые и оптимизированные машины.
Компиляция
JavaScript обычно считается интерпретируемым языком, но современные движки JavaScript больше не просто интерпретируют JavaScript, они его компилируют.
Это происходит с 2009 года, когда в Firefox 3.5 был добавлен компилятор JavaScript SpiderMonkey, и все последовали этой идее.
JavaScript внутренне компилируется V8 с своевременной (JIT) компиляцией для ускорения выполнения.
Это может показаться нелогичным, но с момента появления Google Maps в 2004 году JavaScript превратился из языка, который обычно выполнял несколько десятков строк кода для завершения приложений с тысячами и сотнями тысяч строк, выполняемых в браузер.
Наши приложения теперь могут работать в течение нескольких часов в браузере, а не представляют собой всего лишь несколько правил проверки форм или простые скрипты.
В этом новом мире компиляция JavaScript имеет смысл, потому что, хотя может потребоваться немного больше, чтобы подготовить JavaScript , после того, как он будет готов, он будет намного более производительным, чем чисто интерпретируемый код.
Amazon.com: Комплекты моделей двигателей HMANE V8 для взрослых, 500 + шт. 1: 3 Металлическая модель механического двигателя DIY Сборка Модель двигателя Игрушки Физические игрушки Подарки: Игрушки и игры
В настоящее время недоступен. Мы не знаем, когда и появится ли этот товар в наличии.
Количество деталей
500
Возрастной диапазон (Описание)
Молодежь, взрослый
Марка
HMANE
Материал
Алюминий, нержавеющая сталь
9070 L 9070 Размеры 9070
6.77 x 7,8 x 7,09 дюймов
Убедитесь, что он подходит, введя номер своей модели.
Изысканные украшения моделей: комплекты моделей двигателей V8, металлические детали с высокой текстурой, принесут вам все знания об удивительном мире машиностроения в захватывающей среде. Вы создадите свой собственный двигатель V8.
Комплектные аксессуары: Двигатель состоит из более чем 500 деталей и поставляется с бумажной инструкцией на английском языке.Вы лично испытаете весь процесс сборки от мелких деталей до готовых изделий и создадите шокирующую модель двигателя V8.
Имитационная модель: Двигатель V8 может приводиться в движение двигателем, что более реалистично и интересно. Вы можете наблюдать за процессом работы двигателя на протяжении всего процесса. Это не только изысканная модель украшения, но и имитационная динамическая модель, которая может имитировать работу реального двигателя.
Отличные паровые игрушки: взрослые могут собираться вместе с детьми, чтобы улучшить взаимодействие родителей и детей; дать детям знания в области науки и производства, расширить их знания и воображение; развивать умственное и логическое мышление, а также развивать новаторские и творческие способности.Продукция упакована в высококачественные металлические подарочные коробки, которые являются лучшим подарком для друзей и родственников.
Размеры продукта: 6,77×7,80×7,09 дюймов / 17,2×19,8×18 см
Как работает двигатель V-8?
Двигатели V-8 — один из наиболее распространенных типов двигателей во всей автомобильной промышленности, особенно когда целью является выработка большой мощности с плавной подачей.
Итак, как работает такой двигатель? Джейсон Фенске из Engineering Explained здесь, чтобы пролить свет на то, как работает двигатель V-8. В частности, он использует популярный 6,2-литровый V-8 от General Motors LS3, установленный в Corvette шестого поколения и Camaro пятого поколения.
Прежде всего, V-8 работает как любой другой четырехтактный двигатель с бензиновым двигателем. Цилиндр втягивает воздух и топливо, сжимает воздух и топливо, свеча зажигания воспламеняет смесь, создавая мощность и заставляя поршень опускаться, и, наконец, поршень выталкивает выхлопные газы, когда он движется обратно вверх.Цикл происходит в восьми разных цилиндрах в разное время, и цикл распространяется по цилиндрам для плавной подачи мощности. В LS3 V-8 порядок включения — 1-8-7-2-6-5-4-3, в котором цилиндр срабатывает на каждые 90 градусов вращения коленчатого вала.
Далее мы переходим к распределителю клапанов. Всасываемый воздух поступает из верхней части двигателя в боковые стороны головки блока цилиндров, а выхлопные газы проходят через выпускные клапаны на стороне головки блока цилиндров. Сама головка блока цилиндров включает единственный впускной клапан и единственный выпускной клапан.На видео мы видим больший клапан на впуске и меньший клапан на выпуске. Поскольку мы смотрим на двигатель Chevrolet V-8, в нем присутствуют толкатели. Толкатели активируют коромысла, открывающие клапаны. Альтернативой является конструкция верхнего кулачка с цепным приводом, которую в GM вы теперь найдете в новейшем двигателе V-8 Cadillac.
Что касается толкателей, они работают через выступы распределительного вала, которые активируют толкатели, открывая соответствующий клапан при его повороте. Теперь распределительный вал, который управляет клапанами, соединен с коленчатым валом.За каждые два оборота коленчатого вала распредвал поворачивается один раз, и Джейсон снимает масляный поддон, чтобы показать, как работает коленчатый вал. К кривошипу добавляются грузы для уравновешивания двигателя, а шатуны соединяют кривошип с поршнями.
Итак, когда поршень достигает верхней части цилиндра, противовес располагается прямо напротив него, чтобы уравновесить силу. А еще при 90 градусах он нейтрализует силу другого поршня. Когда поршень возвращается в нижнюю часть, вес противодействует всей направленной вниз силе направленной вверх.Хотя это звучит сложно, на самом деле весь процесс представляет собой простой способ создать плавный двигатель без особых вибраций.
5 лучших японских двигателей V8 за все время
Япония поздно вступила в игру с двигателями V8 в Северной Америке, но когда восьмицилиндровые двигатели начали прибывать из-за Тихого океана, они оказали значительное влияние на автомобильную промышленность в целом. Используя передовые разработки и передовые методы сборки и сборки, двигатели V8 от Toyota и Nissan сначала помогли представить люксовые бренды Lexus и Infiniti, а затем внедрить их в сегменты пикапов и внедорожников.
Какие из этих восьмицилиндровых мельниц были самыми впечатляющими? Вот наш выбор из 5 лучших японских двигателей V8 всех времен.
Тойота 1UZ-FE
Когда Toyota решила выйти на рынок роскоши со своим брендом Lexus в конце 1980-х, она знала, что, чтобы воспринимать ее всерьез, она должна превзойти немецкий и американский статус-кво на как можно большем количестве арен. Двумя наиболее важными целями были общее качество сборки и надежность трансмиссии, причем последнее потребовало разработки первого двигателя марки V8 с двумя верхними распределительными валами, 1UZ-FE.
Американцы впервые столкнулись с этим мотором под капотом Lexus LS 400 1989 года, где он выдавал 250 л.с. и 260 фунт-фут крутящего момента при рабочем объеме 4,0 л. Многое было сделано из плавного характера двигателя, которым Lexus щеголял в телевизионных репортажах, демонстрируя пирамиду хрустальных бокалов для шампанского, балансирующих на капоте LS 400, когда он работал на динометре на полном газу со скоростью 145 миль в час.
Это было больше, чем просто шумиха. Мало того, что 1UZ-FE был способен генерировать легкое и безвибрационное ускорение в полноразмерном седане LS, но его квадратное отверстие, ход поршня и основные подшипники с 6 болтами сделали его почти пуленепробиваемым с точки зрения надежности. .К концу 90-х годов конструкция с 32 клапанами была модернизирована за счет изменения фаз газораспределения, что увеличило мощность до 300 л.с. и 310 фунт-фут крутящего момента в его самой высокой спецификации. V8 оставался в производстве до 2004 года (установлен на коммерческих автомобилях японского рынка после того, как он покинул Северную Америку в 2000 году), и послужил основой для еще нескольких поколений восьмицилиндровых двигателей для Lexus.
Nissan VK45DE
Бренду Nissan Infiniti потребовалось намного больше времени, чтобы создать свой первый двигатель V8 — фактически, более десяти лет — но он также оказался основой будущих восьмицилиндровых двигателей компании.VK45DE также был нацелен на обеспечение бесшумной и легкой мощности, и первоначально он был использован в седане Infiniti Q45 2002 года (за ним вскоре последовали кроссовер-внедорожник FX45 и полноразмерный седан M45).
VK45DE был агрегатом объемом 4,5 л, который отличался DOHC, 32-клапанной конструкцией, с пятиопорным коленчатым валом, катушкой зажигания, коваными стержнями и регулируемыми фазами газораспределения. Мощность от алюминиевого блока составляла изрядные 340 пони и 333 фунт-фут крутящего момента, и вскоре гоночное подразделение компании NISMO разработало версию двигателя с плоской кривошипом для установки на Nissan GT-R.
Совершенно верно — Nissan был настолько уверен в возможностях VK45DE, что заменил почтенный твин-турбо V6 VR38DETT от GT-R и заменил его версией V8 мощностью 490 л.с. для соревнований в Super GT, начиная с 2008 года ( протестировав его по программе 350Z Super GT годом ранее). Фактически, двигатель продолжал служить силовой установкой для автомобилей Le Mans Prototype в классе LMP2 с 2011 по 2016 год, где он имел мощность 450 л.с. и 430 фунт-фут крутящего момента.
Лексус 2UR-GSE
Хотя технически он также производился Toyota, 2UR-GSE V8 никогда не предлагался ни в чем другом, кроме Lexus. Этот 5,0-литровый двигатель V8 был разработан специально для того, чтобы сыграть важную роль в бренде, когда он начал выпуск своих спортивных моделей серии F в конце 2000-х годов, начиная с Lexus IS F.
2008 года.
После успешного сотрудничества с Yamaha при разработке двигателя V10 для суперкара LFA, Lexus снова обратился к своему соотечественнику с просьбой произвести головки с 32 клапанами для 2UR-GSE.V8 с высокой степенью сжатия (11,8: 1) также имеет непосредственный впрыск топлива и первоначально выдавал 416 л.с. и 371 фунт-фут крутящего момента, когда предлагался в IS F. Почти десятилетие спустя тот же двигатель был установлен в обоих купе RC F. и седан среднего размера GS F, где удар сжатия добавил 51 дополнительную мощность наряду с меньшим увеличением крутящего момента. Гибридная версия V8 даже использовалась в полноразмерном четырехдверном Lexus LS 600H мощностью 394 л.с.
Lexus 2UR-GSE — самый мощный двигатель, который когда-либо предлагался Lexus, и недавнее обновление в настоящее время находится между передними крыльями Lexus LC 500 купе и кабриолет, где он выдает 471 лошадиную силу и 398 фунт-фут крутящего момента.
Nissan VK56DE
К тому времени, когда развернулась программа пикапов Nissan Titan, автопроизводитель знал, что ему нужен двигатель большего объема для создания крутящего момента, необходимого для удовлетворения американских потребностей в буксировке и транспортировке. 5,6-литровый двигатель VK56DE V8 вырос из корней программы VK45DE, когда инженеры компании добавили 9,3 мм хода и использовали диаметр отверстия 98 мм, сохранив 32-клапанную конструкцию DOHC на рынке, где американские грузовики были только с толкателем.
Уровень сжатия оставался низким — 9.8: 1, чтобы облегчить использование обычного топлива, и V8 генерировал респектабельные 305 лошадиных сил и 379 фунт-фут крутящего момента, когда он впервые появился в 2004 году с Titan и внедорожником Armada (а также с его близнецом Infiniti QX56, где топливо премиум-класса увеличило мощность до 320 лошадиных сил и 393 фунт-фут крутящего момента). В конце концов, он также нашел свое место в Pathfinder, а также в семействе полноразмерных фургонов Nissan.
VK56DE продвигался вперед почти 10 лет, прежде чем он был обновлен системой прямого впрыска топлива, которая увеличила мощность до 420 л.с. для моделей Infiniti (внедорожник QX80 и седан M56), а для Nissan — от 375 до 400 л.с. Приложения.Как и в случае с меньшим двигателем V8, который ему предшествовал, NISMO собрал готовые к гонкам версии двигателя для использования в соревнованиях GT1 и австралийских суперкаров V8, и он выдал невероятные 650 л.с.
Тойота 3UR-FE
Toyota также нуждался в более совершенном двигателе для грузовиков, когда она модернизировала Tundra для модели 2007 года, и она сделала резкий скачок от скромного 2UZ-FE, который мотивировал предыдущее поколение к 3UR-FE. Новый двигатель объемом 5,7 л позаимствовал некоторые технологии от Lexus 2UR-GSE, но при этом имел приоритет перед крутящим моментом, долговечностью и доступностью по сравнению с высокими оборотами.
В результате 3UR-FE не имел прямого впрыска топлива, но поставлялся с 32-клапанными головками DOHC и выдавал 381 л.с. и 401 фунт-фут крутящего момента на момент своего появления, что было сильным показателем для любой двигатель V8 того времени. Tundra поделилась своим мотором с внедорожником Sequoia, а также с роскошным внедорожником Lexus LX 570 и его близнецом Toyota Land Cruiser.
Двигатель оказался настолько успешным, что производился без каких-либо реальных изменений или обновлений в течение почти 15 лет, и только недавняя Toyota Tundra 2022 года представила замену.Фактически, это был настолько популярный выбор, что на некоторых рынках Toyota удалила опцию V6 из списка заказов Tundra, фактически сделав стандартное оборудование iForce V8.
Додж считает, что
дней двигателя V8 сочтены | Car News
Нет ничего удивительного в том, что в 2021 году можно сказать, что в ближайшем будущем бензиновые двигатели начнут исчезать с рынка. Неудивительно также, что первыми выйдут двигатели V8.Но когда вы слышите это от генерального директора автопроизводителя, который все еще сильно полагается на эти большие пушки, вы садитесь и прислушиваетесь.
Вот что произошло на прошлой неделе, когда большой босс Dodge Тим Кунискис сказал CNBC, что дни двигателей V8 с наддувом сочтены. Наши первые мысли мчатся о двигателе Hellcat, но он включает в себя все сильные стороны 8-цилиндровых агрегатов.
Не то чтобы мощные автомобили никуда не делись — они будут полагаться на свою мощность на электрические силовые агрегаты, которые более чем подходят для этой задачи.Но ощущение никогда не будет прежним.
Стоит отметить, что исчезновение двигателей Hellcat и им подобных не произойдет из-за падения спроса. Модели, которые в настоящее время их используют, Challenger и Charger, по-прежнему очень хорошо продаются. Кунискис отметил, что за последние пять лет его компания продала более 50 000 автомобилей, оснащенных этим двигателем.
Вместо этого он объясняет возможную кончину двигателей V8 затратами на модернизацию, в частности необходимостью их модификации для соответствия новым и будущим нормам по выбросам.Нам нужно только подумать о Dodge Durango SRT Hellcat 2021 года. Он входит в линейку Dodge в этом году, но не будет в следующем году — и единственная причина в том, что в 2022 году он больше не будет соответствовать правилам, которые будут к нему применимы.
Откройте для себя Shopicar! Все новые марки и модели и все текущие акции.
Двигатель Dodge Charger SRT Hellcat
В своем интервью CNBC Кунискис также сказал, что Dodge продолжит фокусироваться на продукции «американской мускулатуры», находящейся в собственности новой материнской компании Stellantis.
«Весь мир перейдет на электрификацию», — сказал он. «Весь мир добьется этого, и когда это произойдет, цена на эту технологию снизится».
Мы уже не в первый раз слышим, как слова «электрификация» и «Додж» произносятся вместе. В 2019 году Майк Мэнли, тогда глава FCA (Fiat Chrysler Automobiles), а ныне глава североамериканского подразделения Stellantis, предположил, что двигатели следующих Challenger и Charger будут частично электрифицированы.Генеральный директор Stellantis Карлос Таварес также сказал, что все новые модели, выпускаемые компанией, будут оснащены той или иной технологией электрической трансмиссии.
Кажется, что двигателю внутреннего сгорания действительно суждено исчезнуть, волнует это вас или нет.