As For JS
3.37K subscribers
144 photos
16 videos
4 files
453 links
As For JavaScript...
Обсуждения — @AsForJsTalks
Download Telegram
Позднее может выложим видео или будем выкладывать в YouTube.

Думаю там будет удобнее.

Просто пока у нас пробные попытки, но это уже, наверное, 5 пробная попытка)
As For JS
As For JS – Новая попытка.
Аудио, это отдельная часть. —
Начало общения.
Потом включили запись видеотрансляции.
👍5
Аудиозапись видеотрансляции.
Может кому будет так удобнее.
As For JavaScript Coalesce Expression - Nullish coalescing operator - ??

Abstract
О том, почему coalesce expression является идиотизмом притянутым в спецификацию идиотами не понимающими архитектуру языка, а так же о том как разработчики V8 реализовали эту парашу именно как парашу.


Что это такое ваше за Coalesce Expression - я забыл - напомни мне - я упырь
не волноваться, я тоже упырь и совершенно не понимаю как придумывают эти названия.
Потому вот документация и примеры:
Официальная спецификация ECMA
MDN
пример:
var theThing;
(theThing ?? "Yo");
// Если theThing связан с undefined или null результат вычисления выражения будет "Yo
"


В чем проблема проверять и на null и на undefined одной конструкцией
undefined - это primitive value которое устанавливается для идентификаторов созданных при помощи variable statement.
null
- это primitive value которое связывают с идентификатором который в прошлом или в будущем был/будет связан с обьектом, но в его текущем состоянии обьект не определен. То есть null - это обьект с нулевой структурой. Не путать с указателями из других языков.

Эта разница принципиальна для runtime. На основе этой информации RunTime может предпринимать совершенно разные шаги каким образом можно работать с нашим кодом. Очевидно, что идентификатор связанный с undefined - это идентификатор поведение которого в будущем предсказать невозможно. Идентификатор связанный с null - в будущем будет обьектом или ранее использовался как обьект.

По этой причине, Coalesce expression бесполезно так как включает в себя два пограничных случая, которые не пересекаются. Иными словами, если в Вашей программе Вы вынуждены проверять идентификатор на то может ли он быть связан в одной и той же ситуации с null или undefined - то у Вас проблемы с вашим кодом, его архитектурой. Так как если Вы знаете что Ваш идентификатор будет связан с обьектом в будущем - он обязан быть про инициализирован как null, а не undefined.

Конечно если в Ваши задачи входит писать эффективный код. То есть код, который претендует на максимальную производительность.

Посмотрите на официальные API - если API должно вернуть обьект, но при этом что-то произошло, что не позволяет это сделать, то API всегда вернет null. Чем прямо сообщает RunTime - да я должен был вернуть обьект, но что-то пошло не так.


Байт код от V8 для Coalesce expression
Именно ради этого раздела я и затеял все это словоблудие.

JavaScript код:
var theThing;
(theThing ?? "Yo");

byteCode V8:
    Ldar r0
/* Регистр r0 это наш идентификатор theThing.
Ldar - это загрузка содержимого регистра в регистр аккумулятора.
Регистр аккумулятора - это особенный уникальный регистр,
который существует в единственном числе в V8. Через него делаются практически все операции.
*/

    JumpIfUndefinedOrNull "label1"
/* Это непосредственно операция ??,
на языке байткода V8 звучит как - если в регистре аккумулятора находится что-то
что является undefined или null то сделать переход к метке label1
*/

    Jump "label2"    
// безусловный переход к метке label2

label1:              
// метка label1 к которой происходит переход

    LdaConstant [0]  
// загрузить в аккумулятор подготовленную константу. В нашем случае это будет ссылка на "Yo"

label2:              
// метка label2 к которой происходит переход
 
// Other code

Этот же код без комментариев:
    Ldar r0 
JumpIfUndefinedOrNull "label1"
Jump "label2"
label1:
LdaConstant [0]
label2:


Ничего не покоробило в логике этого кода?
🔥3
Для того, чтобы наглядно показать о чем я говорю, я приведу еще один код, на этот раз для JavaScript кода, где вместо ?? будем использовать &&:
var theThing;
(theThing && "Yo");

а теперь тот же код в byteCode V8:
Ldar r0
JumpIfToBooleanFalse "label1"
// Это непосредственно операция &&
LdaConstant [0]
label1:


Теперь видна разница между реализацией ?? и &&?
Совершенно верно - в реализации для ?? (Coalesce) используется два перехода ( JumpIfUndefinedOrNull и Jump "label2") , в отличии от && где использован только один переход.

Что естественно отражается на производительности: && или || в среднем на 7% выполняется быстрее для кода, который не был оптимизирован TurboFan. В оптимизированном случае разницы нет.

Напомню, что в текущих условиях, получить оптимизацию от turboFan вам нужно повторить выполнения одного и того же кода не менее 1200 раз, а в некоторых случаях гораздо больше. То есть в современных реализиях, большая часть нашего кода не оптимизируется вообще. В следствии чего те самые 7% уже не кажутся такими несущественными.


А как правильно
Правильно и наиболее эффективно, в случае V8 использовать максимально специфичные конструкции для ситуаций когда нам нужно что-то делать с логикой в зависимости от null или undefined

Например, для ситуации когда мы хотим проверить произошла ли инициализация идентификатора или он все еще undefined:
var theThing;
theThing === undefined && "Yo";

Да, синтаксис содержит слишком много букаф. Но посмотрите какой вкусный код генерирует для этого V8:
Ldar r0
TestUndefined
JumpIfFalse "label1"

LdaConstant [0]
label1:

V8 в своем bytcode имеет особую инструкцию проверки именно на undefined - TestUndefined и соответственно мы видим только один переход.

Аналогичная история с null
var theThing;
theThing === null && "Yo";

V8 код:
Ldar r0
TestNull
JumpIfFalse "label1"

LdaConstant [0]
label1:


Или так:
vat theThing;
theThing ||= 1;
// но следует четко отдавать себе отчет в том, с чем именно связано это условие


Вместо ИГОГО:
Всегда следует помнить о том, сколько стоят подобные оптимизации. Всегда следует отдавать отчет себе в том, что слова: вот || на 7% быстрее чем ?? говорит именно о 7% выигрыше в затратах на реализацию именно этих конструкций, которые на фоне прочих проблем программы могут оказаться ничтожными. То есть настолько бесполезными, что не будет существенной разницы - хорошую вы выбрали конструкцию или плохую, но фоне прочих проблем кода, выигрыш не будет ощутим.
🔥5💯2👍1
Обьявление:
В комментариях к этому сообщению, можно задать вопрос - а что будет быстрее это или это?
Например: Что быстрее var let или const?

Или
А как влияет на производительность создание идентификаторов? Если я создаю сотни промежуточных идентификаторов - это плохо или ничего страшного?

Или
любой другой вопрос относящийся к JavaScript или смежным темам.

Я прочитаю вопрос и если смогу на него ответить - то обязательно дам развернутый ответ.

Мне это нужно для того, чтобы нагружать голову, поскольку я сейчас очень много лежу и у меня появилось много времени для того, чтобы что-то обдумать. А без темы, я скатываюсь в сторону текущих проблем о которых пока думать бессмысленно.
👍2👀1
Оголошення.

Друзі, якщо я можу щось для Вас зробити саме Українською, у площині вивчення JavaScript, або у суміжних темах - напишіть мені будь ласка у коментарі до цього повідомлення - що саме.

Я обов'язково насамперед зроблю саме це.
Слава Україні.
7🤡2
Гдето через два часа, будет стрим на ютубе на тему проблем gpt чата и генерации js кода.

Кроме єтого, если я уложусь по времени - то отдельно запишу ответ на вопрос, про єффективность использования ключей разного типа для доступа к обьектам.

а если не успею - то просто сюда кину текстом.
9
Друзья, приглашаем вас на стрим, который пройдёт здесь в 21:30 по Киеву (21:30 по Москве).

Тема:
"О целесообразности использования числовых (number) ключей при работе с JavaScript объектами. "

#онлайн_встреча
@AsForJavaScript
6👍3
Live stream scheduled for
Live stream started
Трансляция началась…

#онлайн_встреча
@AsForJavaScript
🔥2🐳1
Live stream finished (3 hours)
Media is too big
VIEW IN TELEGRAM
Запись трансляции от 25.04.2023

#онлайн_встреча
@AsForJavaScript
👍9🐳1
Вопрос о алокации JS массивов: #array #perf #v8 #opti
Насколько я помню в си мы аллоцирум палять для массива и каждый раз когда мы хотим увеличить число элементов в массиве, мы должны сделать вновь аллоцировать память. Имеет ли смысл делать тоже самое на js? var arr = [1, 2, 3];, я хочу добавить ещё два элемента в массив, arr.length= 5; arr[3] = 4; arr[4] = 5; и будет ли это влиять на производительность? И что делать если всётаки нудно увеличить длину массива.

Я расширю ответ на все множество ситуаций для подобной темы.

Ответ [Часть 1/4]:
В JavaScript, в случае вопросов о том, что работает быстрее всегда нужно помнить две вещи:
1. Как это закреплено в спецификации
2. Как это оптимизирует конкретный #RunTime

* Если явно не указано другого, то рекомендации идут для RunTime: #V8.

Справка:
V8 - это RunTime от Google. Который встраивается сейчас в Google Chrome и NodeJs. Так же является одним из самых популярных RunTime для встраивания в разного рода штуки для Умного Дома - типа чайников, холодильников и т.д.

Я не знаю на момент написания ответа, иного RunTime, который бы по совокупности факторов, мог бы конкурировать с V8.
Под совокупностью факторов подразумевается: Базовая производительность, производительность в случае оптимизации горячего кода. Простота интеграции RunTime в другие HOST системы.



Как это закреплено в спецификации
Именно для Exotic Object Array, то есть того обьекта который мы создаем при помощи конструктора #Array или литеральной формы [] никакого особого поведения, для оптимизации работы, с точки зрения спецификации - не предусмотрено.
Потому каждый RunTime крутится как хочется.


В случае V8, для exotic object Array используется следующая стратегия:
Внутри RunTime существует 20 вариантов представления подобного обьекта.
Производительность которых сильно отличается друг от друга.

V8 устроен таким образом, что при создании Exotic Object Array, он получает наиболее производительную форму, которая в последствии деградирует до менее производительной, опираясь на те данные которые используются в Array.

Самое быстрое представление:
var theArr = [1, 3, 2];
или
var theArr = new Array (1, 3, 2);

Такое представление называется PACKED_SMI_ELEMENTS.
[1] PACKED: в обьекте (нет и не было) дырок

[2] SMI: элементы состоят из целых чисел со знаком в пределе 2^(32-1). То есть в пределе между -1073741824 до 1073741823. 2^31 степени: 0-2147483647 однако V8 по умолчанию оперирует SMI со знаком, в следсndии чего еще один бит уходит на знак.

[3] ELEMENTS: Значения связаны с числовыми ключами. (0, 1, 2, .... )



Любое превышение заявленого предела для SMI, приводит к преобразованию Exotic Object Array к другой форме, которая называется PACKED_DOUBLE_ELEMENTS.
Например использование значений: 1073741824, NaN, -0, 1.1 etc...
То есть форме, которая хранит в себе 64-bit IEEE floating point number
4👍1