Приветствую, уважаемые читатели Хабра. Эта статья про фронтенд, JavaScript и FPS. И сегодня мне хотелось бы поделиться своими мыслями о «слепом» коде, который фактически никак не учитывает производительности среды исполнения. Ну и, конечно, напишем очередной велосипедокостыль — куда без них.

image

Предисловие


Была недавно статья на Geektimes — «Mozilla выпустила статистику «железа» на клиентских ПК». И прекрасный комментарий от schetilin выражающий основную проблему —
Вот бы эту статистику посмотрели наши сайтописатели. А то сейчас создают сайты, для которых i7 32Gb маловато будет.

И в догонку была еще статья «Правда о традиционных JavaScript-бенчмарках». Суть которой в том, что попугаи хороши для сравнения попугаев, но не для реального применения.

Бла бла бла...


Представим ситуацию — сидит супер разработчик Вася и пишет свой крутой код, запускает его на своем не менее крутом «маке». И… сайт прекрасно работает, анимация красивая, скролл быстрый и плавный.

Но вот пользователь Петя сел за свой ноутбук и решил зайти на сайт интернет магазина для которого, кстати, пишет код Вася. И… понеслось — анимируемые галереи, баннеры, всплывающие окна и еще где-то в недрах запускается парсинг каких-то данных с сервера. Все это периодически сопровождается значком ожидания. Ноутбук Пети в шоковом состоянии, а сам Петя начинает заочно проклинать Васю. Давайте разбираться кто «крайний».

«Петя — тормоз непродвинутый пользователь, пусть обновляет железо» — скажите вы. Но пользователь не обязан подстраиваться по каждый сайт, это дело сайта предоставить качественный интерфейс.

«Вася — рукожоп нехороший разработчик!» — скажите вы. И зря, Вася изо всех сил старается оптимизировать свой код, но он не может его протестировать на всех платформах под все конфигурации железа. И тут напрашивается вопрос к разработчикам браузеров, почему бы ни предоставить web-разработчику хоть какую-нибудь информацию о производительности системы на которой исполняется JS код. Ну правда, что например дает знание какой браузер использует пользователь? Это дает разработчику знание какой синтаксис и API он может использовать. И как это поможет, например, в выборе таймаута при разбиении ресурсоемкой задачи на части. Для каждой версии браузера/движка, для каждой операционной системы и конфигурации железа оптимальный таймаут будет разный.

Представим если бы у web-разработчика, был хотя бы примерный индекс производительности, который бы учитывал тип ОС и конфигурацию железа. Тогда бы при индексе 2 из 10, можно было б вообще отключить все анимации и наоборот при 10 использовать по полной. Ну и неплохо было бы иметь статистические таблицы со временем исполнения типичных задач (например, вставка 100 строк в таблицу) под каждый индекс. Но, на сколько я знаю, ничего подобного нет.

Счетчики


Что ж, прямых данных о производительности системы на которой исполняется наш код, у нас нет. Но есть FPS (Frames Per Second) — отличный косвенный показатель. Высокий FPS — это то что нужно пользователю для комфортной навигации, и это то, что может измерить разработчик. А если мы можем измерить, то можем и использовать эту метрику в каких-то условиях. Прекрасно, идем дальше…

Cправка:

FPS — это, количество кадров в секунду на экране монитора. Для браузеров это количество раз в секунду, когда браузер обновляет интерфейс.

Вообще процесс отрисовки в браузере не так прост. Браузер старается не перерисовать всю страницу, разбивает страницу на слои и еще кучу всего, черт знает чего. Лезть в дебри этого процесса, сейчас нет никакого смыла, да и алгоритм отрисовки может меняться от версии к версии. В принципе достаточно запомнить, одно правило — если часто обращаешься к DOM элементу, то нужно вытащить его из основной структуры т.е. позиционировать его фиксировано или абсолютно.

Итак, нам нужно измерить FPS. Известно, что браузеры стараются обновлять интерфейс порядка 60 раз в секунду ( т.е. идеальный FPS равен 60). И есть метод requestAnimationFrame(), который позволяет запланировать исполнение нашего кода перед следующей перерисовкой интерфейса. Этот метод настоятельно рекомендуется использовать для всех анимаций, вместо setTimeout/setInterval которые могут вызывать принудительны перерисовки, что увеличивает нагрузку на систему.

Пишем счетчик FPS:

let frameCount = function _fc(timeStart){
        
        let now = performance.now();
        let duration = now - timeStart;
        
        if(duration < 1000){
            
            _fc.counter++;
            
        } else {
                      
            _fc.fps = _fc.counter;
            _fc.counter = 0;
            timeStart = now; 
            console.log(_fc.fps);

        }
        requestAnimationFrame(() => frameCount(timeStart)); 
    }

frameCount.counter = 0;
frameCount.fps = 0;

frameCount(performance.now())

Счетчик отлично считает, в этом можно убедиться в консоли. Но он оперирует секундами, а код исполняется гораздо быстрее — единицы и доли миллисекунд. Попробуем максимально сократить период подсчета, например, до 100 ms. Конечно это неизбежно уменьшит точность, зато прирост скорости будет аж в 10 раз.

Быстрый счетчик FPS:

let frameCount = function _fc(timeStart){
        
        let now = performance.now();
        let duration = now - timeStart;
        
        if(duration < 100){
            
            _fc.counter++;
            
        } else {
                      
            _fc.fps = _fc.counter * 10;
            _fc.counter = 0;
            timeStart = now; 
            console.log(_fc.fps);

        }
        requestAnimationFrame(() => frameCount(timeStart)); 
    }

frameCount.counter = 0;
frameCount.fps = 0;

frameCount(performance.now())

И тут меня осенило — почему бы ни использовать оба счетчика. Получится некая система из счетчиков — одного быстрого и одного точного. Посмотрим…

let frameCount = function _fc(fastTimeStart, preciseTimeStart){
        
        let now = performance.now();
        
        let fastDuration = now - (fastTimeStart || _fc.startTime);
        let preciseDuration = now - (preciseTimeStart || _fc.startTime);
        
        if(fastDuration < 100){
            
            _fc.fastCounter++;
            
        } else {
            
            _fc.fastFPS = _fc.fastCounter * 10;
            _fc.fastCounter = 0;
            fastTimeStart = now;
            console.log(_fc.fastFPS);
        }
        
        if(preciseDuration < 1000){
            
            _fc.preciseCounter++;
            
        } else {
                      
            _fc.preciseFPS = _fc.preciseCounter;
            _fc.preciseCounter = 0;
            preciseTimeStart = now; 
            console.log(_fc.preciseFPS);

        }
        requestAnimationFrame(() => frameCount(fastTimeStart, preciseTimeStart)); 
    }

frameCount.fastCounter = 0;
frameCount.fastFPS = 0;
frameCount.preciseCounter = 0;
frameCount.preciseFPS = 0;
frameCount.startTime = performance.now();

frameCount() 

Теперь мы можем хоть как-то оценить текущее состояние среды исполнения. Но, все равно, меня не покидало чувство, того что еще не все сделано. И я опять решил поэксперементировать…

image

На рисунке видно, что мы тратим один такт (фрейм) на обнуление счетчика, а реальный подсчет начинается со следующего такта. Сократим время подсчета, при этом не будем обнулять счетчик. Это также даст небольшой выигрыш в скорости подсчета FPS. Новое значение периода подсчета определяется как (100/6)*5 = 83 для быстрого счетчика и как (1000/60)*59 = 983 для точного.

Таймаут


Метрику, на которую мы будем ориентироваться, добыли. Теперь нужен механизм управления временем исполнения кода. А раз так нам нужны такие параметры как timeout и delta- величина изменения таймаута. Но, сразу возникает вопрос, как определить оптимальную величину этих параметров? Ответ прост — сходу, никак. Нужны данные, нужна статистика. Хотя сам механизм управления таймаутом вполне можно определить. Он будет предельно прост:

timeoutCorrection(){
                    
	if(fastFPS <= 2 && timeout < 1000){
		
		timeout += delta;
	
	} else if(fastFPS > 2  && timeout > 16) {
	
		timeout -= delta;
	} 
	
}

Из кода видно, что ориентир идет на величину в 20 FPS ( fastFPS *10 ), а точнее чуть большую — "<= ". Значения 16 и 1000 это лимиты. Минимальный таймаут 16, соотвествует 60 FPS. А максимальный выбран величиной в 1000 соответствующий 1 FPS.

Предполагается, что функция корректировки таймаута будет вызываться каждый раз при установке нового значения быстрого счетчика FPS.

Исполнитель


FPS измеряем, таймаутом управляем. Теперь нужен исполнитель — функция, которая на основе этих данных, будет управлять исполнением кода, а точнее просто тормозить вызовы, когда необходимо. Пишем…

let requestAdaptiveAnimation = function _raa(cb, priority, timeout, ...args){
            
      if( !_raa.cbsStore.has(cb) || timeout){

        _raa.cbsStore.add(cb);

        _raa.queue = _raa.queue.then(()=>{
            return new Promise((res)=>{
                setTimeout(()=>{
                    requestAnimationFrame(()=>{
                        cb(...args);
                        res();
                    });
                }, timeout || 0);
            });
        });
        
        return;            
    } 
    
    if(frameCount.fastFPS >= 4 || priority){
        
        requestAnimationFrame(()=>cb(...args));

        return;
    }

    if( frameCount.preciseFPS < 15){
        
        _raa.queue = _raa.queue.then(()=>{
            return new Promise((res)=>{
                requestAnimationFrame(()=>{
                    cb(...args);
                    res();
                });
            });
        });

        return;
    }
    
    setTimeout(()=>{

        requestAnimationFrame(()=>cb(...args));

    }, _raa.timeout);

}

requestAdaptiveAnimation.cbsStore = new Set();
requestAdaptiveAnimation.queue = Promise.resolve();

Эта функция намеренно названа схоже с requestAnimationFramе, так как именно этот метод лежит в её основе. Т.е. любые обновления интерфейса мы делаем через requestAnimationFramе. Но так как с помощью этого метода нельзя задать таймаут, делаем обертки с помощью setTimeout.

На основе цепочки промисов организована асинхронная очередь, которая во первых позволяет задать таймаут, а во вторых предотвращает «лавинное» исполнение кода.

Aлгоритм requestAdaptiveAnimation в следующем:

  • Если переданный коллбэк используется впервые или задан таймаут, то исполнение колбэка ставиться в общую очередь.
  • Если это анимация и один и тот же коллбэк вызывается несколько раз, тогда при высоком значении быстрого счетчика FPS производиться прямой вызов requestAnimationFramе.
  • Если значение точного счетчика FPS упало ниже критического порога в 15, то колбэк ставится в очередь. Визуально это выглядит как временная заморозка анимации. Число 15 выбрано субъективно, на мой взгяд при этом значение скролл страницы еще можно назвать приемлемым.
  • По умолчанию, вызываем requestAnimationFramе, через откорректированный таймаут.

Тесты


Настало время собрать все вместе и затестить. И кроме того осталась еще нерешенная задача с таймаутом и дельтой. Для тестирования я написал небольшой стенд , и стал смотреть различные соотношения таймаута и дельты.

Изначально я решил закоррелировать таймаут и дельту с количеством анимируемых объетов. И соображения у меня были такие — чем больше элементов, тем больше стартовое значение таймаута, а дельта соответсвенно меньше, иначе будут сильные колебания таймаута и анимации.

Таким образом расчет сводился к:

timeout = numberObjects;
delta = 1/numberObjects;

Но это работало плохо, и я решил ввеcти поправочный коэффициент — ratio. Расчет свелся к:

timeout = numberObjects/ratio;
delta = ratio/numberObjects;

Я стал менять этот коэффициент и смотреть графики.

100 объектов:



500 объектов:



1000 объектов:



2000 объектов:



Этот график интересен тем, что на нем видно включение «спасительного» механизма очереди при FPS ниже 15 кадров.

Сравнительная табличка:



По табличке отбираем лучшее варианты и продолжаем тестировать.

Тут надо немного пояснить. Хотя мы указываем анимировать 1000 элементов, на самом деле из-за очереди первоначальных вызовов, одновременно у меня анимировались не более 300. Это из-за того, что при начале анимации, например, 300-го элемента, анимация первого уже заканчивалась.

Вот как это выглядит (кусок примерно 300 элементов):



1000 объектов, короткие анимации ( сравнение лучших из таблицы):



Теперь увеличим, длину анимации (я просто сделал максимальное масштабирование страницы) и снова затестим.

Как теперь это выглядит (все элементы — 1000):



Теперь видно, что анимируются все элементы одновременно.

1000 объектов, длинные анимации ( сравнение лучших из таблицы):



По графику видно, что «спасительный» механизм очереди не включается только при timeout ( elements/ratio ) и delta ( ratio /elements) равных 1 — розовая линия на графике.

Итак, с таймаутом и дельтой определились. Теперь самое время сравнить с нативными методами — setInterval, setTimeout, requestAnimationFrame:



Из графика видно, что при малом количестве элементов имеем минимальный оверхед. При включении механизма адаптации, длительность исполнения кода конечно увеличивается, зато FPS сохраняется на уровне 30. Нативные же методы просаживают FPS до 7-8.

Казалось, бы вот и всё. Но нет, все тесты я запускал на ноутбуке, а изначальная цель всей этой писанины — работа на разных системах, с разными конфигурациями. Поэтому проверяем все тоже самое, но на десктопе.

Сравнение с нативными методами на коротких анимациях:

image

Сравнение с нативными методами на длинных анимациях:

image
Видно, что и на ноутбуке (CPU: A8, RAM: 8Gb) и на десктопе (CPU: i5, RAM: 8Gb) работа механизм аналогична — FPS сохраняется на уровне 30. Разница лишь в том, на сколько происходит растягивание исполнения кода во времени.

Итоги


Хорошо:

  • Механизм работает, FPS сохряняется
  • Сохранение уровня FPS независимо от браузера (Firefox, Chrome)
  • Сохранение уровня FPS независимо от производительности системы

Плохо:

  • Независимость между собой разных анимаций. Из-за этого анимации запущенные позже при определенных условиях, могут завершаться раньше

«Ну вот теперь, то всё» — подумал я. Но то странное чувство… и я решил опять…

P.S.


Всем, хорошего настроения. Комментарии и критика приветствуются. Единственная просьба, если ставите 'минус', то черкните пару строк, за что именно.
Поделиться с друзьями
-->

Комментарии (38)


  1. kafeman
    12.01.2017 05:25
    +3

    Зачем это, если во всех нормальных браузерах такие графики уже есть «из коробки»?

    Firefox 50, пример скролла этой страницы - средний FPS 12.73, макс. 60, мин. 3.48


    1. i360u
      12.01.2017 06:49
      +1

      Затем, что мы не видим этих графиков из JS?


      1. kafeman
        12.01.2017 07:06

        А зачем на них «смотреть» из JS? Собирать какие-нибудь отчеты и отправлять на сервер? Так ваша считалка и будет составлять основную нагрузку. А для разработки штатных средств более чем достаточно.


        1. i360u
          12.01.2017 10:32

          Вы пункт "Бла бла бла" не читали? Для реализации чего-то типа graceful degradation в JS-рантайме?
          К примеру, я сейчас делаю редактор списков который в реальном времени добавляет/убирает элементы, которые могут содержать пользовательские данные со сложной структурой. При превышении определенного порога по количеству элементов — редактор переключается в упрощенный режим, чтобы браузер продолжал его рендерить без тормозов. Мне вполне бы пригодился инструмент для автоматического определения этого порога.
          Прошу отметить, что считалка не моя, я говорю о проблеме безотносительно данного конкретного решения, в котором тоже вижу некоторые недостатки. Вы знаете как сделать лучше?


          1. IPri
            12.01.2017 11:00

            … в котором тоже вижу некоторые недостатки

            Видите — пишите что не так, оч интересно глянуть как вы считаете.


            1. i360u
              12.01.2017 11:12

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


              1. IPri
                12.01.2017 11:35

                … которые будут смешиваться в очереди с тем,...

                Для этого у меня везде и используется requestAnimationFrame, который не запускает функцию здесь и сейчас, а группирует несколько вызовов в одну пачку и выполняет, выбирая оптимальный момент для запуска.


                1. i360u
                  12.01.2017 12:23

                  Ок, а что происходит с очередью вызовов не связанных с рендером DOM? В нее точно так-же встраиваются колбэки из requestAnimationFrame, как и из setTimeout/setInterval ведь? И если никакой анимации нет, а есть, к примеру, парсинг JSON — вызовы бенчмарка замедляют весь остальной код?


                  1. kafeman
                    12.01.2017 12:33

                    Как я понял из дискуссии ниже, он занимается разработкой каких-то сложных WebGL-приложений (возможно, игры), так что там рендер идет беспрерывно (даже если игрок стоит на месте, то движутся какие-нибудь облака, NPC и т.д.), а код бенчмарка и код отрисовки будет запущен в рамках одной итерации цикла отрисовки.

                    А вот для простых страничек — да, пускай там будет производительность хоть 100500 кадров в секунду, вопрос: откуда там вообще берутся эти кадры, если страница статична, ее можно было просто один раз записать в видеопамять, а потом бы уже видеокарта ее аппаратно блендила с остальными окнами.


                    1. i360u
                      12.01.2017 13:01

                      Не, "он" пилит онлайн-редактор и просто хочет оградить пользователей от "выстрела в ногу", при котором их браузер не потянет ими же созданный документ =)


                      1. IPri
                        12.01.2017 13:33

                        Я пилю фронтенд :)


                  1. IPri
                    12.01.2017 13:33

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


                    1. i360u
                      12.01.2017 14:05

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


                      какой смысл в это время что-то анимировать

                      А анимировать не обязательно, нагрузку и перерисовку может создать сам пользователь, масштабируя окно или, к примеру, редактируя contenteditable.


                      1. IPri
                        12.01.2017 14:08

                        Спасибо, за предложение. Веб-воркер — хорошая идея.


          1. kafeman
            12.01.2017 11:15

            Ну, графики вам не нужны точно (а если нужны, то нажмите F12). Кроме того, вам придется держать некий буфер и постоянно считать для него среднее арифметическое (если брать только текущий FPS, то он может «прыгать» туда-сюда, из-за чего ваше приложение будет постоянно мерцать, переключаясь со слабой детализации на полную и обратно).


            1. IPri
              12.01.2017 12:00

              Вместо буфера у меня используется «точный счетчик», как раз чтобы не реагировать на возможные провалы 'быстрого счетчика'. Так что мерцание будет в самом критическом случае, когда либо совсем мертвый интефейс, либо хоть какой, но живой. Лично я за живой.


        1. IPri
          12.01.2017 10:57

          Так ваша считалка и будет составлять основную нагрузку.

          Какой именно участок кода, по вашему мнению будет давать нагрузку? Если жмакните по ссылке на 'стенд', то увидите, что при запущенном счетчике и вашем бездействие, FPS будет 60. Так что о какой нагрузке речь, я не понимаю.
          А зачем на них «смотреть» из JS?

          Вот, в потом то и печаль, что разработчики даже не понимают зачем это нужно. Используя штатные средства, вы можете оценить производительность на конкретной машине с конкретной конфигурацией и конкретным js-движком. А что делать с остальными миллионами пользователей с их разнообразием железа, ОС и д.т.? Есть два варианта либо вы зайдете к каждому и запустите на его машине средства разработки в браузере, либо всем пользователям нужно иметь такую же конфигурацию аппаратно-программных средств как у вас.


          1. kafeman
            12.01.2017 11:32
            +1

            Если жмакните по ссылке на 'стенд', то увидите, что при запущенном счетчике и вашем бездействие, FPS будет 60
            Какая мне разница, какой у вас FPS? Открываю пустую страницу, Firefox потребляет 13% времени ЦП (у меня сейчас открыто много вкладок). Открываю ваш «стенд», потребление сразу растет до 20%, а если поводить над окном мышкой, то даже 30%.

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

            Профайлинг, бенчмаркинг, etc. — это все хорошо, но только для отладочной версии. Попробовали разные алгоритмы, нашли «узкие места» — поздравляю, теперь полностью вырезаем все свои тесты и отдаем пользователю нормальную и быструю программу. Возьмите, для примера, тот же Firefox и запустите его в отладочном режиме. Производительность упадет значительно, потому что это помогает разработчикам понять, что и где еще можно ускорить. Но вот пользователям генерировать эти отчеты нет ну никакого смысла (и посылать эти отчеты разработчикам тоже).


            1. i360u
              12.01.2017 11:43

              Поверьте мне, я прекрасно понимаю

              Я не верю. Вы с WebGL дело имели? А с Web VR? А мой пример выше чем Вас не устроил? У веб-разработки, знаете ли, есть такая специфика, как ОЧЕНЬ большой разброс в производительности и прочих возможностях сред исполнения. Если Вы с подобными задачами не сталкивались, это не значит, что их нет.


              1. kafeman
                12.01.2017 11:56

                Нет, с WebGL я дела не имел, но писал достаточно сложные шейдеры на GLSL. Да, замерял время рендера и потребляемые ресурсы, но только в отладочной версии. В релизе это все вырезалось на уровне препроцессора C.

                В общем, приложение ваше, пользователи тоже ваши, хотите принудительно запускать у каждого по бенчмарку — пожалуйста, ваше право. Да хоть майньте Биткоины, главное же чтобы FPS не сильно падал.


                1. i360u
                  12.01.2017 12:09

                  Конечно, куда нам, веб-макакам до С-господина… Мы ведь можем тоже отдельный раздел с настройками уровня графики запилить, верно? Хотя стоп, а откуда берутся "рекомендованные настройки" в играх?


                  1. kafeman
                    12.01.2017 12:23

                    куда нам, веб-макакам до С-господина…
                    Прошу прощения, если это показалось вам слишком грубым, но я только имел ввиду, что за пределами веба зоопарки бывают гораздо шире. Кроме того, я думаю, вы не станете спорить с тем, что веб-приложения — это нечто относительно новое по сравнению с историей разработки «традиционного» software? И «C-господа» уже успели дофига раз «отстрелить себе ногу», «наступить на грабли» и споткнуться «о подводные камни», чтобы вырабатать некие правила, которым потом можно будет научить «веб-макак».

                    «Рекомендованные настройки» берутся исходя из статистики + таки да, возможно, существуют специальные сборки с бенчмарками, которые раздаются части пользователей за какие-нибудь плюшки. Но вы можете себе представить, например, GTA V, отправляющую разработчикам графики FPS на протяжении всей игры? Я — нет. Модель и производитель видеокарты, версия операционной системы, установленные драйвера, размер памяти — это еще куда ни шло, но FPS точно не отправляется.


                    1. i360u
                      12.01.2017 12:33
                      +1

                      Но никто не говорит о отправлении, мы можем просто присваивать индекс производительности на старте приложения и включать/выключать какие-то функции в зависимости от него? Бенчмарк на этом этапе умирает и больше никому не мешает, все остальные манипуляции в коде — просто экстраполяция стартового теста на основе работы уже с профайлерами на тестовом стенде. В том то и дело, что в JS нет доступа к свойствам системы и определить возможности клиента даже приблизительно трудно. Максимум — по типу браузера. И, как я уже сказал, вне веб — адаптация к производительности клиента — вполне нормальная практика, иначе все играли бы только в Супер Марио...


                      1. kafeman
                        12.01.2017 12:43

                        на старте приложения
                        Вот это, кстати, уже нормальный кейс. Я бы сделал так: на самый первый запуск, когда еще у пользователя не закешированы картинки, скрипты, стили и т.д., показывается экран «Пожалуйста, подождите...» и параллельно выполняется бенчмарк (получается, что он должен грузиться у вас первым, до всех остальных ресурсов). Результат сохраняется в какой-нибудь localStorage, и при следующей загрузке уже ничего не считается. В таком случае да, согласен, можно попробовать.


                      1. IPri
                        12.01.2017 13:37

                        Это интересный вариант. Но это же надо, чем то занять пользователя, пока бенчмарк издевается над его браузером. :)


            1. IPri
              12.01.2017 11:45

              Повторил ваш опыт, с множеством вкладок и открытым стендом. Увеличения нагрузки CPU не наблюдаю.

              … полностью вырезаем все свои тесты и отдаем пользователю нормальную и быструю программу


              Как вы определите, что ваша программа быстрая во всех случаях? Для конкретной машины, пускай даже 5 машин, на которых вы сможете протестить программу — да. А в остальных случая вы уверены?

              Но вот пользователям генерировать эти отчеты нет ну никакого смысла...

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


              1. kafeman
                12.01.2017 12:07

                Как вы определите, что ваша программа быстрая во всех случаях? Для конкретной машины, пускай даже 5 машин, на которых вы сможете протестить программу — да. А в остальных случая вы уверены?
                Нет, я в этом не уверен. Но у меня есть статистика по используемым машинам, и я могу запустить виртуалки со всеми самыми распространенными конфигурациями. В автоматическом режиме код будет оттестирован, будут собраны отчеты, я их изучу и сделаю выводы.

                Думаю, что вам тоже нужно идти в этом направлении:

                1. Собрать подробную статистику. Например, для веба можете использовать modernizr, он достаточно хорошо определяет наличие тех или иных фич. По результатам поднимите тестовые окружения по самым распространенным конфигурациям.

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


                1. IPri
                  12.01.2017 13:45
                  +1

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


                  О чем и речь, это каждому разработчику/группе разработчиков нужен такой стенд. И для чего, а для того чтобы просто узнать как исполняет код браузер. А не проще было бы, если бы разработчики браузеров, предоставляли подобную информацию о своих детищах. И не надо было сотен тысяч виртуалок.

                  Для случаев, когда пользователь жалуется на слабую производительность...

                  Они никому не жалуются, они просто закрывают сайт и ищут альтернативу.


  1. russelgal
    12.01.2017 11:01
    +1

    а как же троттлинг в хроме для теста производительности для эмуляции слабых машины/девайсов?


    1. IPri
      12.01.2017 11:19

      image
      Вы про это? Да это позволяет, примерно оценить работу кода в разных условиях. И если заморочиться и высчитать все возможные случаи относительно вашем машины, то конечно можно получить представление о том как ваш код исполняется на разных машинах. Но вам, тогда необходимо будет выбирать компромиссный вариант, который будет устраивать всех и сразу. А хочется динамически определять возможности клиента и прямо в рантайме оптимизировать исполнение.


  1. ThisMan
    12.01.2017 11:01
    +1

    Это дает разработчику знание какой синтаксис и API он может использовать.

    Для каждой версии браузера/движка, для каждой операционной системы и конфигурации железа оптимальный таймаут будет разный.

    Это теперь к кроссбраузерности добавится кросспроизводительность? Ну, спасибо!


  1. 0x9d8e
    12.01.2017 15:46

    Как раз недавно очень печалился отсутствием возможности приблизительно узнать производительность клиента и при необходимости отключить тормозные и бесполезные «красивости», либо заменить их на менее прожорливые.


  1. maniacscientist
    12.01.2017 17:07

    Выжать 60 fps на любом хламе, снижая качество — не такая сложная задача. Бывает хуже. У гипотетического Пети — метросексуальный сони, тонкая машинка, и вот я имею на ней 60 fps, но Петя недоволен — греется у него, видите ли.


    1. IPri
      12.01.2017 17:31

      Я вовсе, не ставил перед собой задачи в 60 FPS. Иначе я вовсе не использовал бы анимацию и минимум JS кода. Задача состояла в притормаживании слишком «прожорливого» кода, чтобы у пользователя оставался запас на скролл и тыкания мышкой.


  1. vintage
    14.01.2017 11:31

    Зачем такие сложности с двумя счётчиками? Достаточно иметь массив из 60 чисел, указатель на последний замер и на каждый кадр, смещать указатель, записывать прошедшее в последнего замера время в массив и когда надо — просто разделить 60000 на сумму всех чисел, получив фпс.


    1. IPri
      14.01.2017 22:46

      Судя по описанию, у вас то, как раз, позаковыристее получается — массивы, указатели :) Ни могли бы показать реализацию в коде.
      И что такое 60000? Вы предлагаете среднее за минуту? Я писал, что получать метрику даже 1 раз в секунду слишком медленно.


      1. vintage
        15.01.2017 00:42
        +1

        1. IPri
          15.01.2017 05:48

          Спасибо, отличный вариант! Буфер и указатель — супер!