Ещё раз о показателях игровой производительности — средние величины, процентили, 1% и 0.1% низкие FPS
реклама
В предыдущий раз мы познакомились с понятием времени кадра, а также с часто используемыми показателями игровой производительности, а именно, со средним, 1% и 0.1% низкими FPS, которые определённым образом характеризуют весь массив значений времени кадра на какой-либо игровой сцене. Необходимость использования указанных величин логических вытекает из двух простых, но важных утверждений:
- Для достижения высокого игрового комфорта важным является не только достаточно высокий средний FPS, но и высокая стабильность значений времени кадра.
- Минимальный FPS плохо подходит для целей оценки стабильности значений времени кадра, так как является единственным значением из набора данных и, как следствие, может представлять собой выброс (англ. outlier).
реклама
Упрощённо, выброс (или промах) — это единичный результат измерений, сильно отличающийся от всех остальных результатов из всего массива данных. Выброс может быть обусловлен ошибкой измерений (устранимой или нет), а может просто отражать высокую вариативность значений измеряемой величины. И вся суть проблемы выбросов как раз и заключается в том, что мы, зачастую, не можем знать, какой из двух упомянутых вариантов имеет место быть. Так, например, в нашем случае аномально низкое значение величины минимального мгновенного FPS может как отражать реально низкую производительность конкретной компьютерной системы на самом сложном кадре игровой сцены, так и быть обусловленным сторонними факторами — как минимум, не стоит забывать, что мы пользуемся многозадачной операционной системой, в которой одновременной с игровым приложением запущено ещё огромное множество программ и сервисов, способных в определённый момент задействовать разделяемые с игрой аппаратные ресурсы компьютера.
Процентили
Конечно, можно взять и просто исключить выбросы из массива данных, руководствуясь каким-либо критерием, коих, вообще говоря, существует не так уж и мало. Однако, такой подход очевидно плох в случае, когда выбросы лишь отражают высокую вариативность измеряемой величины, а так как знать наверняка, так это или нет в нашем случае невозможно, подход с исключением выбросов — это однозначно не наш выбор. Альтернативный вариант действий предполагает использование только статистических величин и методов, способных работать в условиях наличия выбросов. В статистике такие величины и методы называют выбросоустойчивыми, или робастными (от англ. robust, устойчивый). Минимальное значение, очевидно, не является робастной характеристикой набора данных, так как является единичным значением из этого набора, которое может оказаться выбросом. А вот процентили, или перцентили, напротив, робастны.
реклама
Напомню, что n-ый процентиль, обозначаемый обычно Pn, можно определить как значение, ниже которого находится n процентов значений из всего набора данных. Так, например, 99-й процентиль — значение, ниже которого находятся 99% значений из набора. В нашем конкретном случае, если 99-й процентиль времени кадра равен, скажем, 33 мс, то это означает, что у 99% кадров значение времени кадра меньше либо равно 33 мс и лишь у 1% кадров значение времени кадра больше 33 мс. Интерпретировать эти значения можно следующим образом — можно ожидать, что у 99 из 100 кадров значение времени кадра будет меньше либо равно 33 мс и только у 1 кадра оно превысит 33 мс. Аналогично и с частотой кадров, только необходимо учитывать, что в случае частоты кадров нас в первую очередь интересуют малые, а не большие процентили, например, 1-ый процентиль FPS, значение которого таково, что лишь для 1% кадров мгновенный FPS оказывается ниже или равен 1-ому процентилю, а для 99% — выше. Иными словами, можно ожидать, что только для 1 из 100 кадров мгновенный FPS будет меньше или равен 1-ому процентилю FPS, а для остальных 99, соотвественно, больше, и, таким образом, 1-й процентиль FPS является робастной заменой минимального FPS.
Учитывая тот факт, что мгновенный FPS есть величина обратная времени кадра
FPSi = 1000/ti
можно показать, что процентили этих величин связаны соотношением
реклама
Pn(FPS) = 1000/P100-n(t)
то есть, имея на руках массив значений времени кадра, для вычисления 1-ого процентиля FPS необязательно вычислять значения мгновенного FPS для каждого кадра и затем считать 1-й процентиль полученных чисел, а можно вычислить 99-й процентиль времени кадра и взять обратное значение. Напомню только, что с учётом того факта, что время кадра обычно исчисляется в миллисекундах, а частота кадров в единицах в секунду, в обеих формулах появляется множитель 1000. Кстати говоря, во многих программах 1-й процентиль FPS ошибочно обозначается как 99-й, что технически, конечно, неверно, но суть должна быть ясна.
И от ошибок чужих переходим к ошибкам собственным — в прошлой заметке значения 1% low FPS и 0.1% low FPS были неправильно отождествлены с соответствующим процентилями FPS. Это неверно, так как указанные величины представляют собой среднее арифметическое значений мгновенного FPS, попавших в соответствующий процентиль. То есть, например, что-бы посчитать 1% low FPS необходимо вначале вычислить 1-й процентиль FPS, затем выбрать из всего массива значений мгновенного FPS только те, которые в этот процентиль попадают и посчитать среднее арифметическое. Мне, признаться, такая схема обработки данных не нравится, так как весь смысл использования процентилей состоит в том, чтобы нивелировать влияние выбросов, а на величины 1% low FPS и 0.1% low FPS выбросы оказывают существенно большее влияние, чем на процентили. Так что продолжим вычислять и использовать именно процентили, просто теперь будем их правильно называть на диаграммах.
реклама
Осталось только определиться с тем, какие процентили FPS считать. И здесь, как уже говорилось в прошлый раз, всё определяется негласными соглашениями в какой-либо области, и конкретно в игровых бенчмарках де-факто стандартом стали 99-й и 99.9-й процентили времени кадра и соотвествующие им 1-й и 0.1-й процентили FPS, которые мы ранее и использовали в результатах, пускай и неправильно их именуя (см. выше). Однако, в ходе анализа получаемых данных я пришёл к выводу, что 0.1-й процентиль следует исключить из результатов по следующей причине. Дело в том, что среднее время используемых игровых бенчмарков составляет порядка 1 минуты (60 секунд), так что при среднем FPS порядка нескольких десятков (скажем, 60 FPS) на выходе получается всего несколько тысяч значений времени кадра (60 c * 60 кадров/с = 3600 кадров), а 0.1% от нескольких тысяч — это всего-навсего несколько кадров (чаще всего 1-2), которые могут являться выбросами, от влияния которых мы, вообще говоря, и хотим избавиться. Иными словами, нескольких тысяч кадров попросту недостаточно, для того чтобы величина 0.1-ого процентиля была робастной. С 1-ым процентилем такой проблемы нет, так как 1% от нескольких тысяч кадров составляет уже несколько десятков кадров — величину, достаточную для робастности 1-ого процентиля. Можно было бы, конечно, обрабатывать не каждый прогон отдельно, а все прогоны вместе взятые, но, во-первых, такая обработка данных некорректна, а во-вторых, даже в этом случае понадобилось бы порядка 10 прогонов для каждого бенчмарка, что чрезмерно затратно по времени.
И последнее о процентилях, точнее, об одном особом процентиле, а именно 50-м процентиле, известном так же как медиана. Медиана, будучи 50-м процентилем, представляет собой такое число, что половина элементов из некоторого набора больше него, а другая половина меньше либо равна. Медиана часто используется в качестве так называемой меры центральной тенденции, то есть одного единственного числа, служащего (для краткости) в целях описания всего множества значений. По-простому, меры центральной тенденции часто называют "средними", и, собственно, различные виды средних величин, например, среднее арифметическое так же могут быть использованы в качестве меры центральной тенденции. Ранее, мы так и поступали, считая средний FPS как среднее арифметическое значений мгновенного FPS. Однако, у среднего арифметического есть один важный недостаток, которого (отчасти) лишена медиана — среднее арифметическое не является робастной величиной, а оценки медианы (обычно) робастны.
Классическим примером ситуации, в которой неробастность среднего арифметического проявляет себе во всей красе является подсчёт среднего дохода. Будучи посчитанным как среднее арифметическое, средний доход зачастую будет выше, чем доходы большинства людей, так как высокий доход нескольких людей с большим отклонением от среднего делает сильный перекос среднего арифметического. Например, если 19 работников предприятия получают по 5000 ₽ в месяц, а генеральный директор — 1 млн ₽, то средняя зарплат, посчитанная как среднее арифметическое, составит чуть больше 50000 ₽. Прямо как в том анекдоте: чиновники едят мясо, простые люди — капусту, а в среднем мы все едим голубцы.
Медиана же, в отличие от среднего арифметического, справляется с наличием такого сильного перекоса — средний доход по медиане в указанном примере составляет 5000 ₽. Конечно, при большем количестве усредняемых значений влияние выбросов на среднее арифметическое будет снижаться, но тем не менее, скажем, в штате, где официально декларирует свой доход какой-нибудь Джефф Безос, влияние его дохода на среднее арифметическое значение всё ещё может быть существенным. В нашем случаем, с несколькими тысячами значений времени кадра, мне пока ни разу не довелось наблюдать существенного расхождения между средним арифметическим и медианой, однако, далее будем использовать именно медиану. Во-первых, потому, что робастность этой характеристики в целом выше, чем у среднего арифметического, а во-вторых, для единообразия — математический смысл медианы аналогичен таковому у 1-ого процентиля (так как медиана, собственно, тоже суть есть процентиль, только 50-й).
Время кадра
Обсудив, каким образом мы будем обрабатывать данные, переходим к описанию процесса их получения. И начнём, пожалуй, с того, что измерение чего-бы то ни было, во-первых, предполагает получение "на выходе" некоторого числового значения (или нескольких значений) совместно с используемой единицей измерения, а во-вторых, характеризуется определённой точностью. При этом точность измерений объединяет такие понятия как правильность измерений и их прецизионность, первое из которых описывает близость результата измерений к истинному значению измеряемой величины, а второе — близость результатов нескольких измерений друг к другу. При этом точность измерений (в обоих смыслах) зависит в первую очередь от метода измерений, так что с обсуждения метода измерений времени кадра мы и начнём.
Методы измерения времени кадра
Итак, мы измеряем время кадра, о котором уже писалось подробнее ранее. Измеряем используя программный метод, а именно, с помощью утилиты PresentMon. Что же можно сказать о правильности и прецизионности таких измерений? Начнём с правильности. Вообще говоря, тема правильности измерений времени кадра программным методом слишком обширна для подробного обсуждения, к тому же хорошо раскрыта в многочисленных обзорах программного-аппаратного комплекса NVIDIA FCAT, например в статьях на overclockers.ru (1, 2), а также на ixbt.com и hardwareluxx.ru (1, 2, 3). Интересующиеся могут ознакомиться с материалами по приведённым ссылкам, здесь же мы позволим себе лишь краткое резюме. С одной стороны, можно утверждать, что точно (в смысле правильно) измерить время кадра, т.е. разницу во времени между двумя реально выведенными на экран друг за другом кадрами, возможно только с помощью программно-аппаратных решений. С другой стороны, программно-аппаратные решения:
- Требуют наличия дополнительной компьютерной системы, оснащенной высокопроизводительной картой видеозахвата, а также высокопроизводительной и ёмкой подсистемой хранения данных для записи несжатого материала (обычно RAID-массив из SSD-накопителей).
- Значительно увеличивают время проведения тестов и время их анализа.
- И, наконец, в большинстве случаев выдают результаты незначительно отличающиеся от полученных программным способом измерений.
По поводу последнего утверждения, необходимо пояснить, что несмотря на то, что программная методика не является 100% точной в смысле истинности получаемых абсолютных значений времени кадра, она хорошо показывает себя для целей выяснения относительной производительности компьютерных систем, которая обычно и интересна. На этом, в принципе, можно было закрыть тему обсуждения программного метода измерения времени кадра, если бы не ещё один важный нюанс.
Время кадра: rendered vs. displayed
Рендеринг кадра — процесс технически сложный, включающий множество этапов, оформленных в конвейер. Вот, например, упрощенная блок-схема графического конвейера, взятая из руководства пользователя NVIDIA FrameView — одной из утилит мониторинга, основанной на PresentMon.
Упрощённо работу графического конвейера можно описать следующим образом:
- Вначале за некоторое время T_game игровой "движок" совершает все необходимые расчёты, результатом которых является список инструкций некоторого графического API (например, Direct3D), необходимых для отрисовки кадра.
- На отметке времени T_present игровой "движок" передает список инструкций в среду выполнения графического API, где высокоуровневые инструкции и шейдеры транслируются в низкоуровневые команды, понятные графическому драйверу.
- Затем драйвер транслирует полученный список команд в список машинных инструкций, понятный уже конкретному установленному в системе графическому процессору.
- Далее графический процессор исполняет полученный набор инструкций, формируя кадр в видеопамяти. На отметке времени T_render кадр уже полностью отрисован и готов к отсылке на дисплей.
- На заключительной стадии графический процессор отправляет готовый кадр на монитор. T_display — момент времени, когда кадр отображен на дисплее (полностью или частично в зависимости от настроек вертикальной синхронизации).
Теперь зададимся вопросом: по каким из указанных отметок времени следует считать время кадра как разницу во времени между двумя кадрами, проходящими через графический конвейер? С теоретической точки зрения максимально корректно было бы считать время всякого i-ого кадра равным T_display[i] - T_display[i-1], то есть по отметкам в самом конце графического конвейера, а не в самом его начале (T_present[i] - T_present[i-1]) или где-то посередине. Почему? Как минимум, потому, что на дисплее может быть отображено меньше кадров, чем поступило в графический конвейер или даже прошло его несколько ступеней. Как минимум, такой способ измерений не учитывает наличие так называемых "отброшенных", или "выпавших", кадров (dropped frames) и "неполноценных", или "карликовых", кадров (runt frames). "Отброшенные" кадры — это кадры, попавшие в графический конвейер, но до экрана так и не добравшиеся, так как во время их продвижения по конвейеру "что-то пошло не так". Например, кадр может быть отброшен, если его отрисовка заняла слишком много времени, за которое игровая ситуация уже успела измениться, так что надобности в выводе этого кадра на экран уже нет. "Неполноценные" кадры — это кадры, которые таки добираются до дисплея, но выводятся на него лишь на мгновение, так что отображаются в очень небольшой части экрана и не воспринимаются человеком.
Собственно, самым главным недостатком большей части чисто программных методов определения времени кадра (например, FRAPS) как раз и является та их особенность, что они измеряют лишь разницу во времени между отсылкой двух следующих друг за другом кадров на конвейер, то есть разницу между отметками времени в самом начале графического конвейера, в то время как программно-аппаратные комплексы измеряют время кадра в самом конце графического конвейера. На данный момент, впрочем, существуют и чисто программные решения, способные измерять время кадра по отметкам времени в конце графического конвейера. Так, например, используемый нами PresentMon умеет измерять не только T_present, но и T_display. Однако, повторим, что точно (в смысле правильно) измерять разницу во времени между реальным выводом на экран двух следующих друг за другом кадров возможно только с помощью программно-аппаратных решений. Всё дело в том, что чисто программные методы могут быть "обмануты" драйвером видеокарты, который легко может скрыть факт наличия "отброшенного" или "неполноценного" кадра, отрапортовав, что весь процесс рендеринга прошёл в штатном режиме и кадр был полностью выведен на экран. Единственный способ быть однозначно уверенным, что кадр был полностью выведен на экран — это тщательный анализ видеопотока, полученного путём захвата кадров с одного из выходных интерфейсов видеокарты. Но как уже было сказано выше, на практике для целей сравнительного анализа игровой производительности в большинстве случаев достаточно и точности чисто программных средств.
Итак, казалось бы, в теории отметки времени T_display лучше подходят для целей измерения времени кадра, чем отметки времени T_present, однако, в абсолютном большинстве случаев используют именно последние, так как в использовании первых имеется, как минимум, одно большое "но". И имя этому "но" —DirectX 12, или, точнее, отсутствие в нём классического эксклюзивного полноэкранного режима (exclusive fullscreen mode) и необходимость определённых манипуляций для запуска DirectX 12 приложений в ближайшем аналоге этого режима. Суть, вкратце, состоит в том, что в DirectX 12 есть возможность запускать полноэкранные приложения лишь в режиме окна без рамки (borderless windowed mode), при этом игра, запущенная в таком режиме не получает видеобуфер в своё безраздельное пользование, а как и любое другое оконное приложение записывает данные в свой собственный буфер, а затем диспетчер окон рабочего стола (англ. Desktop Window Manager, DWM) компонует буфер каждой программы в окончательное изображение, которое и выводится на экран.
С одной стороны такой подход позволяет легко использовать различные визуальные эффекты, которые объединяют элементы нескольких приложений, например прозрачность и оверлеи, а так же упрощает переключение между полноэкранным и остальными приложениями и использование многомониторных конфигураций. С другой — в работе DWM есть одна важная особенность, которую необходимо учитывать при проведении замеров времени кадра по выходу из графического конвейера: для предотвращения мерцаний и разрывов картинки DWM использует двойную буферизацию, меняя текущий первичный буфер (из которого осуществляется вывод информации на экран) на текущий вторичный буфер (в котором содержаться результаты обработки следующего кадра) только в момент завершения вертикальной развёртки монитора. Как результат значения времени кадра по выходу из графического конвейере, то есть посчитанные по отметке времени T_display, могут быть лишь числами, кратными величине, обратной частоте развёртки монитора. Так, например, для монитора с частотой развёртки 60 Гц, возможны лишь значения времени кадра, кратные 1/60=0.016(6) с, или 16.6(6) мс. Фактически кадровая частоты в таком случае оказывается синхронизирована с частотой вертикальной развёртки монитора аналогично ситуации с использованием вертикальной синхронизации (V-Sync), вот только в отличие от V-Sync выключить двойную буферизацию DWM невозможно.
На самом деле у разработчиков есть возможность обойти DWM при использовании DirectX 12, чтобы избавиться от вышеупомянутой синхронизации частоты кадров с частотой вертикальной развёртки монитора (необходимо использовать модель представления True Immediate Independent Flip), однако, многие DIrectX 12 игры эту возможность не используют. Как результат, на практике получаем такую картину:
На рисунке сверху отображены значения времени кадра, посчитанные по отметкам времени в начале (оранжевый график) и в конце графического конвейера (зелёный) для нескольких первых секунд встроенного бенчмарка Metro Exodus, запущенного в режиме DirectX 12. Так как данные получены на мониторе с частотой развёртки 60 Гц, а указанный выше механизм "обхода" DWM игра не использует, то время кадра по выходу из графического конвейера строго кратно 16.6(6) мс. Мало того, что на значения времени кадра в конце графического конвейера влияет такой абсолютно внешний фактор, как частота развёртки используемого монитора, так даже при использовании одного и того же монитора, накладываемое на значения времени кадра ограничение может сильно скрадывать разницу в производительности двух систем. В теории, конечно, можно использовать время кадра по выходу из графического конвейера, а влияние указанного ограничения нивелировать, используя монитор с высокой частотой развёртки, скажем, 240 Гц, но на практике большинство просто использует время кадра по входу в графического конвейера, так как в абсолютном большинстве случаев, когда указанного ограничения нет, разница между значениями, посчитанными по разным отметками незначительна.
Время кадра: прецизионность измерений
С точностью измерений, можно считать, разобрались, теперь скажем несколько слов об их прецизионности, которая, напомню, есть близость результатов нескольких измерений друг к другу. Конечно, оценивать близость результатов измерений значений времени каждого единичного кадра технически сложно да и бессмысленно, так как, строго говоря, получить идентичную последовательность кадров в двух тестовых прогонах практически невозможно. Так что прецизионность имеет смысл оценивать лишь по статистическим характеристикам всего набора значений времени кадра, то есть, например, по выбранным для анализа процентилям. В идеальной ситуации, то есть на хорошо повторяемой последовательности кадров (обычно реализуемой посредством встроенного бенчмарка) стандартное отклонение по 3-5 прогонам составляет максимум чуть больше 1 FPS для показателей медианы и 1-ого процентиля FPS. Таким образом даже в таком идеальном с точки зрения прецизионности измерений случае значения указанных величин имеет смысл приводить только в целочисленном виде, а о десятых (и уж тем более сотых) долях FPS не может идти и речи.
При тестировании же на случайных игровых сценах (ручная "пробежка" участка определённого игрового уровня) ситуация значительно хуже — статистические погрешности значительно выше, особенно на не самых производительных системах. Достаточно банально повернуть голову в сторону в одной конкретной сложной сцене на одном проходе и не повернут на другом, чтобы получить существенно различающиеся значения показателей игровой производительности. Страдают, конечно, в первую очередь малые процентили, но бывают и ситуации, когда от прогона к прогону сильно "гуляет" и значение медианного FPS, так что ему нельзя верить даже с точностью до целых. Фактически здесь приходится выбирать — либо максимальный охват актуальных игровых проектов при невысокой прецизионности результатов, либо высокая прецизионность результатов, но полученная лишь для небольшого числа проектов со встроенным бенчмарком. Можно сказать, что мы здесь выбираем что в первую очередь тестируем — игры или "железо"? Мне интересно тестирование именно "железа", так что хотелось бы иметь хорошо воспроизводимые результаты, в которых не приходилось бы сомневаться даже при сравнительно небольшом числе тестовых прогонов. Так что мой выбор пал на игры со встроенным бенчмарком. Кроме того, прогоны встроенного бенчмарка практически всегда возможно автоматизировать, что значительно снижает трудозатраты на тестирование.
Конечно, в теории всегда можно попытаться автоматизировать передвижение персонажа по игровому миру, так чтобы получить хорошо воспроизводимую последовательность кадров даже в играх без встроенного бенчмарка. Но это в теории, а на практике, реализовать это технически сложно, так что даже если не помешает античит или крупное обновление, которое сломает необходимые файлы сохранений или удалить используемую для тестов локацию, трудозатраты слишком велики. Каждый, как мне кажется, должен заниматься своим делом — написать встроенный бенчмарк разработчикам игры в разы проще, чем сторонним тестировщикам производительности. При том, что такой бенчмарк для внутреннего использования безусловно пишется почти в любом случае, а понять мотивы, побуждающие не делать его доступным для простых смертных крайне трудно.
В любом случае, пока, резюмируя:
- Тестируем на небольшом числе проектов со встроенными бенчмарками.
- Измеряем время кадра программно, по отметкам времени в начале графического конвейера.
- В качестве робастной характеристики среднего и минимально FPS используем медиану и 1-й процентиль, соответственно.
На этом пока всё.
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Комментарии Правила