Как Node.js (V8) оптимизирует JavaScript код? [ru] / Евгений Обрезков

  Рет қаралды 4,807

fwdays

fwdays

2 жыл бұрын

Видео с онлайн-конференции Fwdays: JS is back!, которая прошла с 23 ноября по 3 декабря 2021 года.
Описание доклада:
Ни для кого не секрет, что один из компонентов Node.js - это движок, выполняющий JavaScript код - V8. Но как происходит так, что скриптовый язык программирования выполняется настолько быстро, что в некоторых ситуациях он сравнивается с компилируемыми языками программирования? В этом докладе мы разберем, что такое Node.js, V8, состоящее из V8 и что такое JIT компиляция.
Страница доклада:
fwdays.com/event/js-autumn-fw...
Больше докладов и видео по теме конференции:
fwdays.com/event/js-autumn-fw...
Fwdays более 10 лет занимается организацией масштабных конференций для разработчиков таких направлений: JavaScript, .Net, Python, Data Science, PHP, QA, Highload, Architecture, DevOps, Databases.
Больше информации про актуальные события:
fwdays.com/events
Подписывайтесь, чтобы первыми узнавать про старт продаж билетов по самой выгодной цене:
Facebook: / fwdays
Twitter: / jsfwdays
Telegram: t.me/jsfwdays

Пікірлер: 32
@vladyslavkarpenko9372
@vladyslavkarpenko9372 Жыл бұрын
Дякую Євгене🤝 Дуже корисна інформація, відповідає на ряд практичних питань👌
@arthursaenz6305
@arthursaenz6305 Жыл бұрын
Максимально круто! Дякую! Один із накращих докладів за останій час!
@doomor
@doomor 2 жыл бұрын
Щікарний доклад! дуже дякую, дуже цікаво було
@VV-kv5cc
@VV-kv5cc Жыл бұрын
Дуже просто, детально і зрозуміло, дякую
@user-sg4kw8uh3m
@user-sg4kw8uh3m Жыл бұрын
Дякую! Супер доклад!
@mixmax1982
@mixmax1982 Жыл бұрын
Дякую за доповідь 👍
@lesson-web
@lesson-web Жыл бұрын
Ой Евгений ,зря вы полезли в эту тему, лучше бы сайты на WIX конструировали)))))) Вечерняя сказка для маленьких джавасприптеров записана для вас!!! Заходите к нам на огонёк
@Shafty17
@Shafty17 Жыл бұрын
20:00 Одразу згадав You don't know JS :) Чудова доповідь, дякую!
@msiavolt4301
@msiavolt4301 Жыл бұрын
Потужно, дякую!
@theoty-js-react
@theoty-js-react Жыл бұрын
УВАЖАЕМЫЙ ЕВГЕНИЙ! ВАШЕ ВИДЕО ЧЕПУХА, НА КАНАЛЕ ВЫШЕЛ ВЫПУСК КОТОРЫЙ ПОДТВЕРЖДАЕТ ЧТО ВЫ ДЕМОНТРИРУЕТЕ СВОЮ НЕКОМПЕТЕНТНОСТЬ. КАНАЛ - AS FOR JS..
@demimurych1
@demimurych1 Жыл бұрын
*Abstract* Материал содержит большое количество фундаментальных ошибок, в результате чего, у слушателя, формируется совершенно неверное представление о том чем является V8, какова его архитектура и то, каким образом сейчас обстоят дела в плоскости, оптимизации кода, интерпретируемых языков в целом, и JS в частности. Разбор ключевых ошибок материала, я опубликую отдельными комментариями к этому сообщению. Я пропущу огромное количество неточностей допущенных в материале, поскольку на описание их у меня нет ни времени ни сил. Хотя это, конечно тоже заслуживает внимания. *Anti Disclaimer* Я не ставлю перед собой цель обидеть или дискредитировать кого-либо, потому любые намеки на проявление токсичности с моей стороны, следует воспринимать как мою эстетическую недоразвитость. *Я беру на себя ответственность утверждать что все сказанное мной* отвечает как существующей на сегодняшний момент спецификации, так и состоянию того или иного программного продукта. Я настаиваю на том, чтобы в случае обнаружения ошибок, о них был заявлено в максимально уничижительной для меня форме, ибо сказано - да прошествует назвавший себя клизмою, в место ее прямого назначения.
@demimurych1
@demimurych1 Жыл бұрын
*Комментарий 1* *Почему все детали материала о преимуществах и недостатках JIT не отвечают действительности* или история развития архитектуры V8 00:14:38 Автор: _Если бы мы делали одновременно и компиляцию и интерпретацию, то по итогу время затраченное на эти процессы не стоило бы [затраченных усилий]. Поэтому JIT компиляция применяется к JavaScript-у только когда это действительно нужно_ Чтобы понять почему, сказанное выше не отвечает действительности, нужно вспомнить историю развития V8. Историю развития V8 можно разделить на четыре этапа: 2008 - 2010 года или дикое время 2010 - 2016 года или Crankshaft - который перевернул мир 2016 - 2021 года, Ignition / TurboFan, или мы лучше знаем как вам будет лучше 2021 - 2022 года, Sparkplug, или да здравствует Crankshaft (простите мы обоср@лись) *2008 - 2010 года или дикое время* Это время когда появился Google Chrome. Когда JavaScript вдруг стал выполняться с производительностью, которая резко изменила отношение к языку. В то время меня мало интересовал JavaScript, потому я ничего не могу сказать относительно внутреннего устройства V8. *2010 - 2016. Crankshaft - который перевернул мир* В 2010 году официально заявляется новая инфраструктура для компиляции JS кода в V8, название которой Crankshaft. Это было начало новой эпохи, когда слова: JS код может выполняться со скоростью нативного - не были пустым звуком. Crankshaft занимался тем, что код, который должен был бы сейчас выполняется, сразу же интерпретировался и компилировался в его машинное представление. Более того, код насыщался большим количеством кешей, и блоками сбора статистики, на основании которой, этот же код, мог быть пересобран в оптимизированную форму. Ни один RunTime того времени не то что конкурировать, даже рядом стоять не мог с V8. Ближайшие конкуренты выполняли код в два три раза медленнее. Даже современный V8 выполняет код в среднем на 10% - 30% медленнее чем V8 с Crankshaft (О причинах этого ниже) *Думаю теперь понятно, почему сказанное в видео, про JIT и производительность* *для случая совмещения интерпретации с компиляцией - не отвечает действительности* Хотя Бы потому, что в течении 6 лет расцвета Google Chrome, JavaScript код именно так и выполнялся - одновременно с интерпретацией и его компиляцией в низкоуровневый код, для текущей архитектуры. И это было то время, когда были поставленный рекорды производительности, к которым сейчас только подбираются. Кстати сказать, одним из автором этого чуда был тот самый Вячеслав Егоров, о котором в материале говорят позднее, якобы как о человеке, коотрый разобрал по полочкам системы типа JS Perf. А на самом деле, пытавшегося объяснить как все работает, и, что если есть желания делать хипстерские тесты, то делать это нужно строго понимая что ты делаешь. *2016 - 2021 года, Ignition / TurboFan* Команда Crankshaft, чуть ли не в полном составе уходит на разработку Дарт. На смену им приходят другие люди. Кем то принимается решение, что теперь важно не то, чтобы работало быстро, но то, чтобы работало везде где это возможно. В результате в 2016 году, Crankshaft заменяется на Ignition + TurboFan, целью которых, в первую очередь, является введение промежуточного байт кода в который интерпретируется JS код. Напоминаю, что в случае Crankshaft, код собирался в его машинное представление, уже после чего мог подвергнуться дополнительной оптимизации. Теперь, код собирается в байт-код - промежуточное представление, которое по факту и становиться основным для выполнения JS кода. Выгода этого в том, что портирование V8 на другие платформы становиться плевой задачей. Недостаток этого в том, что из-за введение промежуточного байт кода, мы неизбежно теряем от 10 до 30% исходной производительности, и это принципиально компенсировать ничем нельзя. Я думаю теперь понятно почему любые утверждение о том, что современный V8 это как минимум то, что обеспечивает нас производительностью не хуже старых версий, также является вещью не отвечающей действительности. *2021 - 2022 года, Sparkplug* В 2021 году, все сказанное выше, а главное акценты на ошибках в выборе решений, дошло и до разработчиков V8. Что стало причиной появление еще одной прослойки в V8. Которая названа Sparkplug. Современный V8, с 2021 года, стал по возможности собирать байт код - в машинный, еще до момента, когда можно дать оптимизированный код. То есть по факту, делается откат к Crankshaft, но с попыткой сохранить транс-компиляцию в байткод. *Игого* До 2016 года, v8 работал в режиме, когда интерпретатор и компилятор работали одновременно, при этом производительность его работы была рекордной. Рекордной настолько, что в соизмеримых современных условиях, вопрос демонстрации схожей производительности является открытым. До 2021 года, V8 развивался с упором на переносимость, чем и объясняется просадка в его производительности. Потому утверждать, что V8 этого времени, являет собой совокупностью прогрессивных технологий оптимизации, мягко говоря некорректно. Так же совершенно неправильно утверждать, что TurboFan генерирует не - оптимизированный машинный код. *TurboFan генерирует ТОЛЬКО оптимизированный машинный код* Так же важно отметить, что сами разработчики текущего v8, пришли к выводу, что принятая схема Ignition / ByteCode => TurboFan / Optimize x86 code не является оптимальной с точки зрения производительности. И для решения сложившейся ситуации, они добавили новую прослойку называемую Sparkplug, задача которой, получать из байткода, машинный код текущей платформы, без необходимости его оптимизации. То есть максимальное приближение к поведению Crankshaft: Ignition / ByteCode => Sparkplug / x86 code => TurboFan / Optimize x86 code
@demimurych1
@demimurych1 Жыл бұрын
*Комментарий 2* 00:15:52 *Для примера, как работает V8* В альтернативной реальности автора материала: 1. Если функция вызывается условный первый раз - просто интерпретируем. 2. Если она вызывается условный второй раз - мы ее интерпретируем, но при этом и компилируем. 3. Если она вызывается условный третий раз - то мы уже используем ее скомпилированную версию. В реальности 2010 - 2016 года: 1. Если функция вызывается условный первый раз - происходит ее интерпретация, и компиляция в машинный код, который и выполняется. 2. Если функция вызывается условный второй раз - функция обозначается как горячая, что означает, что возможно это функция которую следует оптимизировать. 3. Если функция вызывается условный третий раз - происходит оптимизация ее машинного кода к виду, обеспечивающему максимальную производительность В реальности 2016 - 2021 года: 1. Если функция вызывается условный первый раз - происходит интерпретация кода и его сборка в промежуточный байт код, который и выполняется. 2. Если функция вызывается условный второй раз - функция обозначается как горячая, что означает, что возможно это функция которую следует оптимизировать. При этом выполнение продолжается так же как и раньше - путем интерпретации только уже байт кода. 3. Если функция вызывается условный третий раз - происходит транс компиляция ее байт кода в машинный код, с одновременной оптимизацией этого кода. В реальности 2021 - 2022 года: 1. Если функция вызывается условный первый раз - происходит интерпретация кода и его компиляция в байт код, который и выполняется виртуальной машиной. 2. Если функция вызывается условный второй раз - происходит транс компиляция байт кода в машинный код, без оптимизаций. Функция обозначается как горячая, что означает, что возможно это функция которую следует оптимизировать. При этом выполнение кода теперь происходит уже в его скомпилированном в код текущей архитектуры. 3. Если функция вызывается условный третий раз - происходит преобразование ее машинного кода к оптимизированному виду.
@demimurych1
@demimurych1 Жыл бұрын
*Комментарий 3* *Современная реальность оптимизации JS кода* или почему автор материала фантазирует на эту тему 00:24:01 Очевидно, что автор материала, в период с 2010 по наш 2022 год, не имел даже минимального опыта в решении реальных практических задач оптимизации JS кода. В противном случае он бы знал, что весь его рассказ о Peephole optimization, Local Optimization, Loop optimization, Global optimization и прочих других страшных словах из википедии и показанных на слайде, имеет практически нулевое отношение к реальной работе оптимизирующего компилятора, в задачи которого, в первую очередь входит решение задач, которые вообще не связаны с оптимизацией машинного кода, на уровне тех страшных слов. Но имеет первоочередное отношение к решению задач нивелирования всех недостатков интерпретируемых языков в целом и JS в частности. Дело в том, что интерпретируемые языки, в силу того как они созданы, обладают целым рядом очень удобных для программиста возможностями, по динамическому формированию того или иного кода. Цена этих возможностей заключается в том, что этот же удобный код не может быть однозначным образом не только скомпилирован, но и интерпретирован. И напрямую зависит от конкретной ситуации в которой этот код выполняется. Например интерпретатор не может знать, что будет связано с конкретным идентификатором в конкретный момент его использования. В следсвтии чего, он не может даже предположить откуда нужно будет брать необходимые данные. Как следствие - компиляция такого когда в машинный, сопровождается тоннами сопуствущего кода для выяснения обстоятельств, чем сейчас, в данный конкретный момент, является тот или иной идентификатор. Например: function (doThing) { return doThing(); } Тут идентификатор doThing явно должен ссылаться на что-то что должно оказаться функцией. Но интерпретатор не может знать какую именно функцию будут вызывать - всегда ли одну и туже, да и вообще знает ли программист что если он, что передав туда что-то кроме функции, то может случиться эпичная фиг-ня. В результате, это оказывается такой глобальной проблемой, что по этому поводу собираются отдельные конференции, где куча бородатых нудных человеков ищут новые оптимальные решения таких задач, где на текущее время, проверенным решением, является решение с использованием механизмов Inline Cache ей, и Hidden Class ов. Чтобы Вы поняли насколько это важная и первоочередная задача, я озвучу следующие цифры: решение вопросов идентификации, кеширования и контроля за тем, чем может быть идентификатор в языке с динамической типизацией, занимает почти 90% работы всего времени оптимизирующего компилятора. И это первое, что узнает любой программист, который дает себе труд, прочитать код, генерируемый оптимизирующим компилятором. И происходит это потому, что не заметить 90% листинга, с подсказками на всех возможных языках о том, почему есть проблема и как ее пытается решить компилятор, может только тот, кто этот листинг использует вместо туалетной бумаги, когда настоящая закончилась. Ведь этот листинг допупа огроменнный. Как рулон той самой бумаги. И вот когда, программист понял сермяжную правду Hidden Class ов с Inline Cache ей на перевес, он познает другую магию - функция в интерпретируемом языке, совсем не обязательно является реальной функцией в машинном коде. Компилятор знает как часто любят программисты заявлять функции, которые не надо реально вызывать, но достаточно просто вставить их код в место вызова. Типичный пример: [].map( (el) => el); Очевидно, что map и его обработчик каждой итерации, не должен быть преобразован в реальный код вызова функции. Это бессмысленная трата ресурсов. И вот теперь компилятор упорно решает следующую проблему: какой код может быть вставлен инлайн вместо вызова, а какой нет. Со всем тем же набором проблем Inline Cache-эй и Hidden Class-ов И вот когда программист понимает, как писать код, который компилятор легко распознает, он сталкивается с следующей засадой - каким образом описывать структуры данных, чтобы оптимизирующий компилятор, мог выбрать максимально быстрое их представление. Ведь когда мы пишем someObj.someProperty для нас это просто название объекта и его свойства, а для компилятора целая проблема, особенно вспомнив что идентификатор someObj в один момент может указывать на один объект, а в другой момент уже на функцию. И все это только начало долгого, интересного пути понимания языка JS и его эффективного использования, в конце которого, спустя 3 - 4 года, может быть и появиться пять минут чтобы посмотреть как компилятор, попытаться сделать свертку констант, или удалить Dead Code. *Игого* Чтобы нагляднее пояснить ситуацию, я сообщу такие цифры: проблемы с инлайн кешированием, предсказанием того чем является идентификатор, инлайном кода - меняют производительность в порядки. Я не ошибся - в порядки. И это та самая ситуация, когда вы можете жутко напортачить со сложностью алгоритма, и вытащить производительность зная архитектуру языка, в сравнении с человеком, который выберет идеальный алгоритм, но про-тилилинькает все полимеры, то есть понятия не будет иметь как писать код образом, который бы привел к эффективному машинному коду, то есть стал причиной постоянных оптимизаций и деоптимизаций.
@demimurych1
@demimurych1 Жыл бұрын
*Комментарий 4: правда о создании myFastFunction* или почему в материале говорят хню-хню о том, что это сложно и невозможно 00:41:14 Автор: _Я готов сделать свою функцию myFastFunction так что напишу бенчмарк для нее, она быстро заработает и все будет классно… хотел бы я это сказать… давайте сделаем синтетический бенчмарк который все это померяет [...] но придется мне сказать, что лучше так не делать_ И сказать автору видео это пришлось по той причине, что он понятия не имеет, как реально происходит процесс оптимизации критически важного кода, а самое важное то, что названные им в материале причины типа Dead code elimination или Inlining, которые исказят ваши тесты - в реальности, по шагам, показываются логом оптимизатора, с указанием причин того или иного решения с его стороны. То есть, современный V8 позволяет получать весь лог, от Data Transition, до Inline Caching вплоть до каждой итерации (если мы говорим о цикле) с описанием всей цепочки принятия решений компилятором. Иными словами перед глазами оптимизатора лежит ВСЯ необходимая информация, для понимания того, что произошло, и почему код повел себя именно таким образом. Мало того, компилятор настолько добрый, что он сигнализирует особо большими буквами на самые типичные случившиеся проблемы. И даже подсказывает варианты решения проблемы. Все это, в очередной раз говорит о том, что автор материала, ни разу в жизни не оптимизировал код. В противном случае, он бы знал обо всех этих вещах, а не фантазировал на тему того, почему этого нельзя делать. В его оправдание можно сказать разве что то, что его слова абсолютно справедливы для ситуации, когда вы тестируете свой код каким нибудь онлайн сервисом, который вам не показывает лог работы V8. Но тут хочется сразу спросить - какой же вы мамкин оптимизатор, если не умеет на своей локальной рабочей машине делать тоже самое, да еще и контролируя весь процесс? 00:43:19 *Есть люди которые любят рассказывать [...] шлите таких людей* Это восхитительная позиция людей, которые очень сильно хотят оправдывать свою безграмотность в той области, где они подозревают свою компетенцию. Юмор ситуации в том, что знание базовых принципов работы современного JS, оказывающих радикальное влияние на производительность, не только не мешают читабельности кода, но напротив, могут ее улучшать. 00:43:57 *Оптимизирующие компиляторы достаточно умные* Вот ровно так же говорили люди в 2016 году, когда убивали Crankshaft в угоду Ignition / TurboFan. Им понадобилось 5 лет работы над этой связкой, чтобы признать - человек намного умнее и хитрее компилятора созданным другим человеком, хотябы потому, что у создателя компилятора времени очень мало на глупости, а вот людей, которые хотят глупости делать и имеющих на это время - сотни. Результатом чего, неизбежна ситуация, когда компилятор не только не справиться с задачей, но и сделает все хуже. Хуже настолько, что придется признавать ошибочность фразы заявленой в 16 году про очень умный компилятор, и думать как делать откат до архитектуры Crankshaft. Усвойте раз и навсегда, никогда не будет человека, который бы сделал что-то, что всегда будет умнее всех людей на свете. По этой причине, позиция - компилятор умнее вас, ущербна. Ущербна потому, что вдумчиво работать с компилятором будет не сотня хомячков которым пофигу вообще на эту работу, а люди, которые разберут компилятор до байта. И сделают они это с одной единственной целью - понять как лучше писать для него код. И вот тут вопрос, раз эта ситуация повторяется уже не первый раз, может быть гораздо умнее не соревноваться с такими людьми, но обозначить прозрачные правила игры? 00:44:05 *Старайтесь писать такой JS который был бы понятен другим людям* Это фраза звучит как - пишите код так, чтобы он был понятен каждому альтернативно одаренному. Эй! А можно я напомню для чего пишется код? Код пишется для того, чтобы решать поставленную задачу, в пределах обозначенных ресурсов. И чтение этого кода, является вопросом квалификации читающего, но не обязанность автора кода мучать себя задачами сделать этот код понятным человеку с совком вместо мозга. Программирование - это инженерная специальность, требующая квалификации. Это не общество случайных людей, умеющих давить кнопки со словом - ЫЫЫЫЫ. Программист - это тот, кто умеет эффективно переводить язык решения на язык машины. И первая задача программиста, это сделать перевод понятным машине. Понятным настолько, насколько это только возможно для того, чтобы машина могла использовать максимум из доступных ей ресурсов.
@demimurych1
@demimurych1 Жыл бұрын
*Комментарий 5: или фантазии на тему наглядной демонстрации* За пределами моего понимания остается то, что заставило автора видео, не отдавая себе отчета в том, что за информацию ему показывает V8, упорно делать вид что он все понимает. Судите сами: 00:48:51 *Этот байткод лежит по адресу* По этому адресу не лежит байткод. Любой человек, который пытался читать лог V8, и тем более работать с ним в интерактивном режиме знает, что это адрес SharedFunctionInfo, которая содержит структуру внутри которой V8 хранит все что касается функции. От типа функции, до потенциальных векторов оптимизации. Иными словами, заявленный по этому адресу байт код, является всего лишь одним из трех десятков свойств обьекта. 00:50:13 *Теперь машинный код и опция print-code* Автором сказано: _У нас времени не так много остается поэтому я предлагаю print-code пропустить он не такой интересный, он просто пишет машинные инструкции, но они не будут оптимизированы, то есть это будет общая компиляция TurboFan_ Что является полной чушью по следующим причинам: С 2016 года по декабрь 2021 опция --print-code являлась Alias ом к опции --print-opt-code. Было это по причине того, что никакого не оптимизированного машинного кода в связке Ignition => TurboFan НЕ БЫЛО. Машинный код появлялся всегда сразу в оптимизированном виде и исключений тому небыло. С конца 2021 года, появился новый герой Sparkplug, в задачи которого вошло решение проблем, с которыми, за 5 лет, так и не справилась связка Ignition => TurboFan, а именно генерация не оптимизированного машинного кода так, как это было до 2016 года. Именно с этого момента, в логе работы V8 действительно можно увидеть куски неоптимизированного машинного кода. И тут можно было бы подумать, что автор видео, просто подзабыл как он было в реальности, только вот беда - при генерации кода, в логе всегда четко написано --- Optimized code --- optimization_id = 0 source_position = 10324 *kind = TURBOFAN* stack_slots = 15 compiler = turbofan или 0x7f57280855c1: [Code] - map: 0x0fad00002655 - code_data_container: 0x0fad002570b1 *kind = BASELINE* compiler = baseline address = 0x7f57280855c1 И, что уж совсем добивает версию автора видео, делает он это сейчас далеко не всегда, и только для определенных архитекутр. Иными словами, сейчас, все еще, нужно сильно постараться, чтобы увидеть результаты его работы. 00:51:41 *Получение оптимизированного кода, или как автор сам забыл все о чем говорил раньше* Ранее автор материала, рассказывал о том, что оптимизации происходят не просто так, но когда им приходит время. В чем он прав. Именно по этой причине, в natives-syntax существует две команды, которые создают условия максимально приближенные к реальной работе: %PrepareFunctionForOptimization(myFunc) myFunc(); %OptimizeFunctionOnNextCall(myFunc); myFunc(); А не единичный вызов %OptimizeFunctionOnNextCall как это показывает автор видео. Далее, поскольку опыта чтения ассемблера у автора нет, это видно по тому как он выделяет блоки кода, в V8 подумали и об этом, добавив опции, которые, выдаваемый ассемблерный код комментируют, где на английском языке разьясняют логику процесса. За это отвечают опции: --code-comments --detailed-line-info --print-code-verbose а если добавить опции: --trace-file-names --no-concurrent_recompilation то V8 добавит к комментариям еще и ссылки на оригинальный файл с JavaScript кодом, и конкретными строками для которых ассемблерный код создан. Поверьте человеку (то есть мне), который читает машинный код уже больше 30 лет. Даже я со всем своим опытом, не отказываю себе в том, чтобы мне подсказали какие конкретно куски JS кода связаны с тем или иным машинным кодом. Это удобно хотябы потому, что при верной организации рабочего места, можно кликом по этим ссылкам сразу попадать в исходный JS файл. Что как бы страшно удобно, когда ты не делаешь вид, что оптимизируешь функцию a+b, а работаешь с кодом на сотни килобайт, где подобных ссылок будет сотни.
@elnights11
@elnights11 Жыл бұрын
Выключил на рассказе про си++ биндинги, которые "реализовывают API" в ноде и браузере. Афтор вообще не понимает, о чём говорит.
Deep Dive Into NestJS [ru] / Никита Галкин
1:08:02
[柴犬ASMR]曼玉Manyu&小白Bai 毛发护理Spa asmr
01:00
是曼玉不是鳗鱼
Рет қаралды 46 МЛН
ХОТЯ БЫ КИНОДА 2 - официальный фильм
1:35:34
ХОТЯ БЫ В КИНО
Рет қаралды 2,3 МЛН
1🥺🎉 #thankyou
00:29
はじめしゃちょー(hajime)
Рет қаралды 71 МЛН
Андрей Мелихов - V8 под капотом
59:57
Сети для самых маленьких
1:11:54
Fox Devs
Рет қаралды 9 М.
Java с нуля. Что такое код
21:48
Java Kabala
Рет қаралды 6 М.
🚀 Node.js  🧑‍💻 Введение в технологию ✨
1:31:54
Xiaomi Note 13 Pro по безумной цене в России
0:43
Простые Технологии
Рет қаралды 1,9 МЛН
What percentage of charge is on your phone now? #entertainment
0:14
A Comprehensive Guide to Using Zoyya Tools for Photo Editing
0:50
XL-Power Best For Audio Call 📞 Mobile 📱
0:42
Tech Official
Рет қаралды 772 М.
Apple watch hidden camera
0:34
_vector_
Рет қаралды 50 МЛН