Перейти к содержимому
RU
Играть

Форум

Почему HTML5 сломан и как с этим жить


Niced

Рекомендованные сообщения

16 минут назад, Serene сказал:

Так вот от чего эти дикие лаги, на которые все жалуются!

А ведь правы были токсичные! Понаделают скинов, красочек да эффектов и даже не предзагружают их заранее.

 

Справедливости ради замечу, что, возможно, это загрузка не одного игрока и просто так совпало, но мне от этого не легче. Тем более за записанную минуту похожая ситуация возникала аж три раза.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

  • Ответы 145
  • Создано
  • Последний ответ

Топовые авторы в этой теме

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

 

Бугурт-моде он. Какое недальновидное решение бандлить движок в установщик, сейчас там столько улучшений именно по части игр делается (благодарите Stadia), а игроки ТО будут продолжать мучиться на старой версии, по-моему даже без возможности автоматического обновления. Нельзя было лаунчер сделать как, черт возьми, делали школосерваки Майнкрафта еще 10 лет назад? Бугурт-моде офф.

  • Нравится 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

3 минуты назад, Niced сказал:

Какое недальновидное решение бандлить движок в установщик

Да, нужен ланчер с автообновлятором для ланчера! УИИИ!
Пора уже пилить бутлегерскую версию на электроне с встроенными расширениями %)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

16 минут назад, Serene сказал:

Да, нужен ланчер с автообновлятором для ланчера! УИИИ!

Да, лаунчер - громко сказано. Достаточно легкой прослойки, которая бы выкачивала свежую версию с сервера.

 

17 минут назад, Serene сказал:

Пора уже пилить бутлегерскую версию на электроне с встроенными расширениями %)

Вот то, что бутлегерскую, все портит. Тут и не бутлегерское расширение делать по большей части не для кого. :(

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 минуту назад, Niced сказал:

Да, лаунчер - громко сказано. Достаточно легкой прослойки, которая бы выкачивала свежую версию с сервера.

Лаунчеры обычно другого и не делают %(

Нужна ещё прослойка между компом и креслом, которая будет этот ланчер пилить, а потом клиент обновлять. точнее, ланчер клиента. ;D

2 минуты назад, Niced сказал:

Вот то, что бутлегерскую, все портит. Тут и не бутлегерское расширение делать по большей части не для кого. :(

У клиента больше шансов, хоть и больше шансов быть выпиленным вместе с тобой :rolleyes:

Ставить расширения тоже гораздо проще через хромовский маркет. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

16 минут назад, Serene сказал:

Лаунчеры обычно другого и не делают %(

Ну, в случае с Майнкрафтом навороченный и красивый лаунчер был признаком крутого сервера. Школьники не ценят минимализм.

 

16 минут назад, Serene сказал:

Нужна ещё прослойка между компом и креслом, которая будет этот ланчер пилить, а потом клиент обновлять. точнее, ланчер клиента. ;D

Как всегда на то, что реально облегчит жизнь игрокам, нет ресурсов. Впрочем, ничего нового.

 

16 минут назад, Serene сказал:

У клиента больше шансов, хоть и больше шансов быть выпиленным вместе с тобой :rolleyes:

Я бы даже не сказал "шансов". Клиент выпилят вместе со мной. :heh:

 

Изменено пользователем Niced
  • Нравится 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

В 26.01.2021 в 21:41, Niced сказал:

Бугурт-моде он. Какое недальновидное решение бандлить движок в установщик, сейчас там столько улучшений именно по части игр делается (благодарите Stadia), а игроки ТО будут продолжать мучиться на старой версии, по-моему даже без возможности автоматического обновления. Нельзя было лаунчер сделать как, черт возьми, делали школосерваки Майнкрафта еще 10 лет назад? Бугурт-моде офф.

cкоро будет cтрим c раклом где он cнова будет бегать по комнате и вопить " треклтый движок " ? ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Давно не виделись! Время офигительных историй.

 

Какое-то время назад я заметил одну вещь, которая, скажем так, меня расстраивала (на самом деле у меня дико горело). Симптомы проблемы такие: происходит лаг на несколько секунд (из-за интернета или задержки на сервере), но мы люди привычные, и все бы ничего, если бы после лага производительность не падала до конца битвы, порой превращая игру в лютый лагодром. В прямом смысле: начинаются просадки FPS, картинка дергается и играть становится некомфортно. Помогает только перезагрузка. И вот, в один из таких случаев, я вооружился профайлером и записал происходящее:

 

Скрытый текст

3MuirDh.png

 

Да, так иногда начинает работать игра после разового лага, это тяжелый случай. Сверху - время между кадрами, которое, как мы видим, сильно отличается от комфортных 16.6 мс. Чрезвычайно много времени занимает как подготовка кадров, так и обработка данных из сети. Вот как выглядит обработка одного сетевого события:

 

Скрытый текст

qNptV8o.png

 

С сервера приходят данные, затем цепочка вызовов приводит к функции createDust, которая, как можно понять из названия, отвечает за эффект пыли. В конце все упирается в некую функцию с обфусцированным именем mi, и ее выполнение занимает большую часть времени не только в этом событии, но и вообще во всей записи длительностью 24 секунды:

 

Скрытый текст

EP622x1.png

 

13 секунд суммарно занимает выполнение одной лишь функции. Что же она делает?

 

Последняя функция в цепочке, назначение которой можно легко вывести из названия - createDustParticle, и вот ее код:

 

Скрытый текст

h3DRsGt.png

 

Нас интересуют два вызова метода get в начале (мы видели их на втором скриншоте), которые приводят к злосчастной функции mi. Срезая углы скажу, что это извлечение объекта из пула, где хранятся экземпляры часто используемых игрой объектов - таких как эффекты выстрелов, пыли, гильз и т. п. Принцип работы с пулом: взял объект, поработал с ним, вернул обратно в пул. Таким образом можно ограничиться уже созданными экземплярами, что снижает нагрузку на сборщик мусора (ведь новых объектов не создается и их не надо удалять, когда они становятся не нужны), ну и сэкономить на их создании, если оно дорого. Клиент ТО активно задействует пулы. Взглянем на код метода get и метода, возвращающего объект в пул:

 

Скрытый текст

jkp6m5y.png

 

По-русски: "если пул пустой, создаем новый объект и возвращаем его; если там что-то есть - достаем первый объект, удаляем его из пула и возвращаем". На самом деле не первый, а любой объект. Потому что тип хранилища - HashSet, и оно не сохраняет порядок элементов, у него нет начала и конца. Чтобы получить первый или последний - не важно - элемент из сета, функция first_7wnvza$ (а точнее та самая функция mi, вызываемая далее) создает список всех элементов в коллекции, и берет нужный из этого списка. Снова: чтобы получить ОДИН элемент из коллекции, библиотечная (т. е. ее написали не наши разработчики) функция каждый раз заносит ВСЕ ее содержимое (точнее, хэши всех элементов, но это детали) в список. Насколько это медленно? Зависит от размера коллекции. В большинстве обычных сценариев это норм, но НЕ В КОДЕ, КОТОРЫЙ ВЫЗЫВАЕТСЯ 100500 РАЗ. Камон, у вас сложность O(n) там, где легко можно было обойтись O(1), используя для хранения объектов не hash set, а массив/список. Да, не совсем понятно как быть в случае, если объект по ошибке возвращается несколько раз, но можно дополнительно держать hash set для проверки - и это все равно было бы быстрее. И да, вспоминая цель существования пулов: чтобы достать один объект, реализация "за кулисами" создает минимум два в процессе. Гениально.

 

Можно предположить, что разработчики слепо понадеялись на реализацию, предоставляемую стандартной библиотекой. Во-первых, в таком критичном к производительности коде, как пулы, это по умолчанию плохой подход. Во-вторых, неужели клиент не отлаживается в реальных боевых условиях, в самом замесе? Даже при легкой игре какие-то непонятные функции отнимают многовато времени для реалтайм-приложения:

 

Скрытый текст

ctGbqw2.png

 

Никого это не смущает?

 

Ладно, а что же с адским лагодромом? Обратим взоры на метод, возвращающий объект обратно в пул. Можно легко заметить, что создание объектов в методе get никак не ограничено (если пул пуст), как и в методе put_11rb$ не ограничено их возращение. Пул у нас безразмерный: надо игре 100500 объектов - она запрашивает их из пула, пул их при необходимости создает, после использования они возвращаются обратно и размер пула увеличивается. Дело в том, что трудно угадать подходящий размер пула заранее. Объекты могут закончиться, а без них игра не работает. Тем не менее, неограниченное помещение объектов обратно в пул - потенциальная утечка памяти, что вкупе с крайне неоптимальной реализацией самого пула и приводит к описанной проблеме.

 

Итак, происходит вот что, как я предполагаю. В "жаркий" момент битвы, когда клиенту необходимо быстро обрабатывать/отрисовывать множество выстрелов, эффектов овердрайвов, пыли из-под гусениц и прочего, с сервера сыплется куча данных. Если в этот момент случается ощутимый лаг (что в нашей игре не редкость), необработанные данные накапливаются. Когда же наконец производится их обработка, из-за большого количества одновременно происходящих событий на карте (и связанных с ними эффектов) объектов в пуле не хватает и он серьезно увеличивается в размерах. По мере его роста игре заметно плохеет, вплоть до неиграбельности.

 

Я пробовал имитировать лаги и проверять количество объектов в пулах, и точно могу сказать, что объяснение выше имеет смысл. Когда счет объектов в пулах идет на тысячи, начинаются заметные просадки. Т. е. чтобы играть стало резко некомфортно, нужен серьезный замес и достаточно продолжительный лаг, однако в любом случае игра постепенно замедляется во время битвы. Также я пробовал заменить hash set на массив - игра прекрасно работает (порой несколько миллисекунд выигрыша за фрейм). Более того, я пробовал отключать пулы совсем - игра прекрасно работает, и сборщик мусора вроде не проявляет себя сильнее обычного. Еще я отключал пыль и гильзы в настройках (советую сделать, пока эту фигню не пофиксили), что сильно снижает нагрузку на пулы, но это же не наш метод!

 

Вот, надеюсь, кому-нибудь было интересно это небольшое расследование. Хочу добавить, что это все можно было описать куда компактнее, потому что то, о чем я говорю - далеко не rocket science, это базовые вещи. Цель была показать, что сторонний пользователь, вооружившись доступными инструментами способен находить слонов в комнате, по какой-то причине игнорируемых разработчиками.

  • Нравится 7
  • Хаха 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 часа назад, Niced сказал:

Камон, у вас сложность O(n)

Я тут загуглил, возможно даже O(n log n) сложность: https://stackoverflow.com/a/64912755 Что интересно:

 

Named properties on most objects that occur in practice (up to about a thousand properties) are (usually) stored in creation order in a special kind of internal array, so they can be retrieved in O(n) and don't need to be sorted.

 

Скорее всего, этим и объясняется существенное падение производительности после достижения пулом размера около тысячи объектов.

  • Хаха 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

В 24.09.2021 в 16:36, Niced сказал:

С сервера приходят данные, затем цепочка вызовов приводит к функции createDust, которая, как можно понять из названия, отвечает за эффект пыли. 

Я знал, что отделу пыли и кустов нельзя доверять.

Цитата

Итак, происходит вот что, как я предполагаю. В "жаркий" момент битвы, когда клиенту необходимо быстро обрабатывать/отрисовывать множество выстрелов, эффектов овердрайвов, пыли из-под гусениц и прочего, с сервера сыплется куча данных. Если в этот момент случается ощутимый лаг (что в нашей игре не редкость), необработанные данные накапливаются. Когда же наконец производится их обработка, из-за большого количества одновременно происходящих событий на карте (и связанных с ними эффектов) объектов в пуле не хватает и он серьезно увеличивается в размерах. По мере его роста игре заметно плохеет, вплоть до неиграбельности.

На царство. Ты прогуляйся по магазинам и присмотри себе козырный новый стул. Не за горами тот день когда тебе нужно будет занять освободившуюся должность, ну а без стула, сам понимаешь, никак.

  • Спасибо 1
  • Хаха 2

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

On 9/24/2021 at 11:36 AM, Niced said:

<...>

Вот именно поэтому я всегда играю на минимальной графике :D

 

On 9/24/2021 at 11:36 AM, Niced said:

Снова: чтобы получить ОДИН элемент из коллекции, библиотечная (т. е. ее написали не наши разработчики) функция каждый раз заносит ВСЕ ее содержимое (точнее, хэши всех элементов, но это детали) в список

Я чот совсем не понял логики происходящего и зачем такая функция вообще нужна применительно к хэшсетам. И зачем для пула нужен хэшсет.

 

То есть у нас есть хэшсет с объектами, которые типа разные (ну тк разные хэши), но при этом, когда нам нужен объект, нас судя по всему удовлетворяет любой из них. Это странный момент номер раз.

Потом, когда вызывается функция first(cache), непонятно, по какому принципу она определяет, что там first, а что нет. Окей, она занесла все хэши в список, дальше что, она возвращает первый элемент из списка? То есть тот, который занесла первым? Вообще можно посмотреть на реализацию конкретно этой функции?

 

И что это за библиотека то такая?

  • Озадачен 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 час назад, Fizzika сказал:

Вот именно поэтому я всегда играю на минимальной графике :D

 

Я чот совсем не понял логики происходящего и зачем такая функция вообще нужна применительно к хэшсетам. И зачем для пула нужен хэшсет.

 

То есть у нас есть хэшсет с объектами, которые типа разные (ну тк разные хэши), но при этом, когда нам нужен объект, нас судя по всему удовлетворяет любой из них. Это странный момент номер раз.

Потом, когда вызывается функция first(cache), непонятно, по какому принципу она определяет, что там first, а что нет. Окей, она занесла все хэши в список, дальше что, она возвращает первый элемент из списка? То есть тот, который занесла первым? Вообще можно посмотреть на реализацию конкретно этой функции?

 

И что это за библиотека то такая?

надеюсь, теперь то понял ,что рачин уровня ПТУ поддерживал?))

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 час назад, Fizzika сказал:

Я чот совсем не понял логики происходящего и зачем такая функция вообще нужна применительно к хэшсетам.

Тут странных моментов вагон и маленькая тележка, но сам понимаешь, я постарался объяснить суть, чтобы многим было понятно, и тут без искажений смысла сложно обойтись.

 

1 час назад, Fizzika сказал:

И зачем для пула нужен хэшсет.

Идея, насколько я понимаю, в том, чтобы исключить попадание дублей одного объекта в пул, если он случайно возвращается несколько раз. Ошибкой было использование HashSet из стандартной библиотеки Котлина, который вычисляет хэш из состояния объекта (вызовом метода hashCode), и соответственно разные объекты могут иметь один хэш, что тоже забавный факт. Стоило использовать джаваскриптовский Set.

 

1 час назад, Fizzika сказал:

Потом, когда вызывается функция first(cache), непонятно, по какому принципу она определяет, что там first, а что нет.

HashSet - это HashMap внутри, функция first создает итератор по сету (чтобы просто получить первый элемент), внутри создается итератор по мапе, а мапа реализована как JS-объект, где имя свойства - хэш, а значение - элемент/элементы. Когда создается итератор по мапе, вызывается Object.keys() для получения массива всех хэшей. А вот из-за того, как работает Object.keys(), случается песец по производительности, когда пул становится достаточно большой.

 

1 час назад, Fizzika сказал:

И что это за библиотека то такая?

Стандартная библиотека Котлина.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 часа назад, Niced сказал:

Ошибкой было использование HashSet из стандартной библиотеки Котлина, который вычисляет хэш из состояния объекта (вызовом метода hashCode), и соответственно разные объекты могут иметь один хэш, что тоже забавный факт. Стоило использовать джаваскриптовский Set.

Да, это так. Я снова залез в код и не нашел метода hashCode по крайней мере у одного типа объектов, которые хранятся в пуле. Это значит, что хэш у них 0 и все они находятся в одном большом списке с парами ключ/значение. *facepalm*

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

3 часа назад, Niced сказал:

Да, это так. Я снова залез в код и не нашел метода hashCode по крайней мере у одного типа объектов, которые хранятся в пуле. Это значит, что хэш у них 0 и все они находятся в одном большом списке с парами ключ/значение. *facepalm*

А, нет, все нормально. Если метода нет, то Котлин сам генерирует рандомный хэш для каждого объекта. Найти это не так просто в обфусцированном коде.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

18 hours ago, Niced said:

внутри создается итератор по мапе, а мапа реализована как JS-объект, где имя свойства - хэш, а значение - элемент/элементы

Ага, я уже глянул на котлиновские сорцы)

 

Я так понял, в нормальную джсовскую Map, у которой есть нормальный человеческий итератор, это все не транслируется из-за необходимости в обратной совместимости со старьем?

 

Интересные конечно подводные камни)

 

12 hours ago, Niced said:

Если метода нет, то Котлин сам генерирует рандомный хэш для каждого объекта

Ну скорей всего по аналогии с джавой берет адрес объекта в оперативе, если это не data-класс

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

24 минуты назад, Fizzika сказал:

Я так понял, в нормальную джсовскую Map, у которой есть нормальный человеческий итератор, это все не транслируется из-за необходимости в обратной совместимости со старьем?

Скорее всего. Котлин официально поддерживает только ES5, поэтому приходится использовать объекты, хотя вроде как операции со свойствами дороже, потому что объекты не рассчитаны на частое добавление/удаление ключей, как Map. Но по сравнению со всем остальным в этой реализации пула это мелочи.

 

24 минуты назад, Fizzika сказал:

Ну скорей всего по аналогии с джавой берет адрес объекта в оперативе, если это не data-класс

Какой адрес объекта в оперативе на JS? Да и вообще на платформе с GC, который может перемещать объекты в памяти?:heh: Что рантайм Котлина, что даже сам V8 просто рандомно генерирует хэш и сохраняет его где-то.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

10 minutes ago, Niced said:

Какой адрес объекта в оперативе на JS?

 

Да и вообще на платформе с GC, который может перемещать объекты в памяти?

Ну ничто не мешает любой платформе с GC сохранять первый выделенный адрес, а v8 уж точно имеет доступ к реальным адресам объектов в оперативе, так что при желании мог бы использовать и их)

 

А ваще я тут наткнулся на прикольную статью как это делает openjdk

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

3 минуты назад, Fizzika сказал:

Ну ничто не мешает любой платформе с GC сохранять первый выделенный адрес, а v8 уж точно имеет доступ к реальным адресам объектов в оперативе, так что при желании мог бы использовать и их)

Что на самом деле то же самое, что взять рандомное число. Но хуже по распределению.

 

Раз уж делимся статьями: https://shipilev.net/jvm/anatomy-quarks/26-identity-hash-code/

 

6 минут назад, Fizzika сказал:

А ваще я тут наткнулся на прикольную статью как это делает openjdk

Ну вот, даже там нет никакого адреса.

 

Да, ты спрашивал, зачем нужна функция first применительно к хэшсету. Это т. н. extension function для всех iterable. Ко всему прочему есть LinkedHashSet, который сохраняет порядок элементов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 minute ago, Niced said:

Что на самом деле то же самое, что взять рандомное число. Но хуже по распределению

Да речь в целом была про то, что это технически реализуемо, а не о том, зачем это применять.

 

1 minute ago, Niced said:

Ну вот, даже там нет никакого адреса.

Ну да, только через твики включается)

А доклады Шипилева конечно убойные, я после рассказа про JMM ещё не отошел

 

2 minutes ago, Niced said:

Это т. н. extension function для всех iterable

Да я уже понял это, я ж написал, что посмотрел в сорцы)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

3 минуты назад, Fizzika сказал:

Да речь в целом была про то, что это технически реализуемо, а не о том, зачем это применять.

Капитанство на капитанство. :smile: (Не обижайся, я не со зла.)

 

* Со стороны JS-приложения это не реализуемо, т. к. там нет аналога identityHashCode, насколько мне известно. Так что Котлин может взять только рандомное число, если нет hashCode.

 

Ну да ладно. А ты в курсе, что на этих ваших Линуксах ТО грузится почти в два раза медленнее из-за того, что шейдеры компилируются последовательно, а не параллельно? По какой-то причине с OpenGL-бэкендом компиляция шейдеров очень медленная, из-за этого создание контекста занимает 8-10 секунд вместо 2-3 на Винде. Разработчики ТО могли бы компилировать шейдеры параллельно (и сократить это время в несколько раз), но они этого не сделали (удивительно!). Разумеется, я все проверял.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Пожалуйста, войдите для комментирования

Вы сможете оставить комментарий после входа



Войти сейчас
  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу


×
×
  • Создать...