Comments 34
Оптимизации на уровне алгоритмов и структур данных (~30%)
Это в идеальном мире, когда хотя бы более-менее разумно сделано. В реальном - может быть и 3000% и более.
Меня всегда удивляло как разработчики умудряются размещать большой объем вычислений на относительно слабом железе, к каким трюкам и решениям прибегают, чтобы приложение работало бысто, это относится не только к игровым движкам
Nintendo Switch - Постоянная внутренняя флеш-память: 32 ГБ (64 ГБ в Nintendo Switch OLED)
Оперативная память - 4 ГБ (LPDDR4)
Это слабое железо?! Вот запихни это всё на БК-0010 с 64КБ памяти из них 32 КБ видеопамять!!! :D Вот что меня удивляло: ВОТ как ТУДА игры запихивали?! Причём не только "Клад", но и 3D игры, когда ты внутри авто и задача не впилиться в дерево! ;)
Там проблема больше в проце, который 1.1Ггц в турбо, но по факту 800-900Мгц в обычном режиме, и все время норовит частоты снизить. Как бы его не нагружали
Ошибаетесь) видеопамяти в БК было 16 кб, где каждый байт кодировал 4 пикселя) последние 16 кб там было ПЗУ с прошивкой и регистрами ввода-вввода)
Недавно попалось видео, где чувак собрал пентиум 3 + жефорс 4400. Запускал старые игры и показывал фпс.
Так вот там как раз наоборот - жуткая угловатая графика, которая к тому же не выдает 30 фпс (гта 3 например).
https://m.youtube.com/watch?v=bMZh0W3xFGY
Как мы в это вообще играли - не представляю. И как вообще такая убогая графика могла так тормозить. И это еще было пред топовое железо.
Так что, как итог, я бы не сказал, что программисты впихивают невпихуемое в слабое железо. Сейчас железо далеко не слабое.
столлы и частые смены контекста выполнения
И как же вы столлы определяли?
Их на самом деле несколько видов, чисто gpu-stall, cpu-gpu, cpu-cpu, memory, let-stall и io-stall. Это ситуация в потоке выполнения когда сpu/gpu ожидает завершения некоторого действия, будь то вычисления или ресурс. Ну как определяли - открывали pix и смотрели глазами, через какое-то время фиксов таких вещей нарабатывается опыт и уже по картинке снапшота выцепляешь проблемные колстеки, дальше разбираешься что мешает.
А что будет, если применить здесь нейронные сети?
Оффтоп конечно, через какое-то время я стал определять код на ревью от своих джунов и мидлов, который они скомуниздили у чатика. Поначалу отнекивались многие, тогда просил рассказать что код делает, увы люди не всегда даже понимают что им там написали ;( т.е. моя НС натренировалась видеть паттерны другой нс, что забавно конечно
зачем вообще на клиенте проверять данные, если они идут с сервака. и зачем в данных, вообще есть строки, которые нужны для экранирования.
как будто проблема вообще в существовании функции.
Зачем вам проверять что ее нужно экранировать? Чтобы потом снова пройтись и сделать это самое экранирование?
Выходит два прохода вместо одного и это изменение вроде не такое масштабное для архитектуры.
Кстати, хотелось бы спросить т.к. не знаю.
Вы сказали что бранч мешает компилятору сделать векторизацию. А потом ручками написали simd. Что значит *векторизация* тогда? Может имеется ввиду то как процессор выполняет инструкции, а не как компилятор их генерит. Надо глянуть асм.
Кстати, интересно как атрибут [[likely]]
повлиял бы на производительность. Если ли вообще смысл его применять. Я слышал что он просто игнорируется на многих архитектурах
Надо всегда думать за рамками текущей задачи, смотреть как она повлияет на общую картину. Если сделать исправления по месту то это усложнит логику работы и замедлит общее время выполнения плюс операции с конфигами это почти всегда копирование памяти, а это дорого, очень дорого для мобилок. Для случая, который описан в баге мы подобрали оптимальный размер блока проверки 256 байт для мобилок и 1к для пк, проверка возвращала место первого вхождения символа для изменения, далее таска уходила в воркер, который занимался уже изменениями во всем конфиге, наложением патча на конфиг уровня и его конвертом в формат движка. К моменту когда заканчивали проверки файлов и загрузку базовых текстур для левела, уже были подготовлены конфиги в формате движка и рендер начинал их грузить. Если все это делать сразу тл получаем лишние секунды работы, когда их можно избежать
Я не автор поста, но делал примерно похожие оптимизации для расчета вэйвформ аудио трэков. Компилятор умеет векторизовывать код автоматически, но делает это весьма консервативно - не всегда, не самые последние инструкции и не всегда самые оптимальные, но тем не менее делает. У меня в коде стоит выбор как обрабатывать данные я использую энум вида Vectorization { Auto, Manual } как раз подчеркнуть тот факт что если вы в коде интрисики руками не пишите то код все равно может содержать симд инструкции.
Вот эта разбивка обработки на 2 части это вообще классика любой симд оптимизации, забавно что иногда умные люди забывают про хвост и там потом УБ вылезает. Вторая классическая оптимизация это ручная развертка цикла, но ее в примере нету. Компиляторы стали хороши в этом плане и ручная развертка работает быстрее чем сделанная компилятором реже чем 10 лет назад. Вероятно ее попробовали сделать она ничего не дала и ее отменили т.к. код получается ещё более трудно читаемым и с ещё одним хвостом, а выхлопа нету.
Совсем похожий код я делал симд поиск строк / оптимизацию в таглибе что бы тэги в мпзшках искать очень очень быстро. Главная сложность ссе довольно сильно отличается от неона и один и тот же алгоритм надо писать 2 раза.
Если понимать, как работает векторизация, то можно "наивный" код довольно легко дотюнить до векторизуемого состояния. Получается короче интринсиков и совместимо с разными системами команд.
https://gcc.godbolt.org/z/jbrqGKxnx
(хвост проигнорирован для простоты)
Наверное, ваш "поиск строк" использует также довольно быструю операцию сравнения. Если так, то может ли он ещё и длину совпадения в байтах от начала сравниваемых областей выдавать? Мне нужно сделать быстрое сравнение областей памяти с выдачей длины в байтах совпадения от их начала. В этом деле нужна помощь.
которые присылали изменения и обновления в бинарном виде (экономия трафика) и для корректной обработки их необходимо было восстановить в текстовый вид, затем нормализовать до json'a, который уже надо было конвертнуть во внутренний формат, чтобы парсер уровня мог их прочитать.
Выглядит как проблема слишком большого количества уровней абстракции.
Насколько эффективнее было бы парсить уровень сразу из бинарных данных, присланных с сервера?
Интереснее вариант, когда изначально всё достаточно оптимально, но разрыв в производительности оригинального и целевого таргетов весьма существенный. Скажем, порт первого Doom под SNES (там ещё у автора не было оригинальных исходников и нормальной документации на чип в картридже, который давал хоть какую то производительность).
Что ж... забавно выяснить, что в научных вычислениях люди пришли примерно к тем же выводам относительно перфоманса и его улучшения что и в геймдеве, ну потому что никому не хочется неделю ждать окончания расчета.
Программные игровые реплеи пожалуй лучшее средство отладки багов, что я видел за время разработки, это как time travel debugging, но не требует поддержки со стороны компилятора и намного дешевле в разработке.
Так почему их не используют? Привязан движок к частоте кадров или нет, но ведь всегда есть детерминированная часть движка? Или его таки нет из-за потерь чисел с плавающей точкой?
Их используют, например реплеи в играх серии AoE2, warthunder, eve online, civ Проблема с ними в том, что они привязаны к определённой игре. Это немасштабируемый инструмент и довольно хрупкий
Точно, вы же про онлайновые игры, там это нужно. Я имел в виду, почему не используют в синглплэере.
Они будут работать любом случае, просто для онлайна их легче сделать- инфраструктура почти вся готова. Для сингла - из известного почти все игры парадоксов на сlauswitz engine
Т.е. вы не знаете причину, почему их не делают в обычных играх. Даже просто для отладки их почему-то не используют. То же самое с создателями эмуляторов разных консолей. Казалось бы, получить точное поведение как на реальной консоли и легко отлавливать баги - это важно, но нет, реплэи добавляются уже позже, если вообще добавят.
Причина простая, это люди и время которое можно потратить на разработку игры, фича для сингла дальше команды разработки не уйдет. Дешевле будет отдать баг QA и получить некотое репро, чем тратить время программиста на создание и поддержку, если бы были какие-то универсальные либы или инструменты, то использовали бы чаще.
Но это же буквально просто сохранение нажатых кнопок в вектор, нет? А потом просто чтение не напрямую из устройства ввода, а из этого вектора.
В старых консольных играх (Денди, Сега, аркады) такое было часто, так как у них запускалось демо игры, если на титульнике ничего не нажимать.
То что вы видели это называется бенчмарк и даже он не очень прост, а реплей - состояние системы ввода (кнопки), состояние анимаций, какая запущена, её внутренний тайминг, состояние скелета, партиклов, системы навигации, AI и его внутрренненего стейта (что видит, что слышит, кто враг, кто союзникк), а их может быть несколько, состояние физики и звуков. И так для каждого юнита и объекта на сцене, обычно это сотни мегабайт в памяти на каждый фремй, так что задача не очень тривиальная. Реплей приниципиально отличается тем, что мы можем в каждый момент остановить симуляцию и посмотреть состояние любого объекта на сцене с максимально возможной детализацией, иначе это будет не лучше чем видео с багом.
Значит, мы говорит немного о разном.
В вашем случае записывается всё, получается большой массив данных, но зато можно легко посмотреть нужное.
В моём случае получается минимум данных, нет нагрузки на производительность и программистов, но зато к нужному месту не дойти быстро - если баг случился на втором часу игры, то придётся два часа проигрывать игру до этого места, чтобы уже там начать логгировать как в вашем случае, чтобы изучать детальнее. Положение могли бы спасти более мощное железо для ускоренного проигрывания и снапшоты, но это уже усложнение для разработчиков.
Как засунуть слона в чемодан