Pull to refresh

О тестировании скорости или как не надо писать тесты

Reading time 4 min
Views 2.8K

Недавно увидел пост Тест производительности работы браузера с HTML5 Canvas. В результатах IE9 начал показывать сумашедшие циферки — 350+ fps.

Это, конечно, хорошо, но почему-то браузеры, которые в других тестах javascript'а и canvas'a показывали не меньшую производительность, в этом тесте показывали в разы (а иногда и в десятки раз) меньший fps (при условии запуска на Windows-платформе, но об этом позже).

Под катом покажу, почему тест показывает скорость совсем не HTML5-Canvas, а в самом конце будет скрин с 470 fps для FF4 без никакого фотошопа, для начала разберем что именно не так в этом тесте.


Ну, для начала, давайте посмотрим что такое 200 fps. Это 200 прорисовок в секунду. Т.е. событие прорисовки должно происходить примерно каждые 5 мс. Если посмотреть в код теста, то там действительно есть строка:
	setInterval(moveIt,1);

Действительно, «moveIt» должен выполнятся каждую 1мс судя по спецификации (WhatWG, W3C). Но нет такого понятия, как минимальное время, через которое хендлер должен быть вызван.

И тогда вступает в силу особенности реализации очереди событий в браузерах. Есть вот такой интересный тест, который должен показать минимальную точность setTimeout. У меня в Chrome было 4мс, а в FF4 четко на 10мс держалось. Возможно это из-за гранулированности работы с таймером для Windows систем (без использования high-performance counter-ов или мультимедия расширений) (в конце табличка с результатами).

Так, как цель этого топика не обсуждение особенностей реализации setTimeout, а обсудить то, как же можно действительно измерить производительность той или иной операции. Как мы убедились, setTimeout не подходит (может быть только «пока не подходит»?) — он не дает возможность полностью загрузить браузер какой либо задачей.

Совсем без таймаута тоже нельзя (т.е. просто в цикле проводить операцию), так как пользователь не увидит ничего — надо дать браузеру время отобразить результат операции.

Возможное решение этой проблемы опубликовал Davin Baron: использовать window.postMessage (WhatWG, W3C, MSDN). Код, реализующий аналог setTimeout но, с минимальной (предпочтительно нулевой) задержкой (я модифицировал код Девида, добавив аналог setInterval-а (поправьте меня, если там нужен fn.apply):
// Only add setZeroTimeout to the window object, and hide everything
    // else in a closure.
    (function() {
        var timeouts = [];
        var messageName = "zero-timeout-message";
 
        // Like setTimeout, but only takes a function argument.  There's
        // no time argument (always zero) and no arguments (you have to
        // use a closure).
        function setZeroTimeout(fn) {
            timeouts.push(fn);
            window.postMessage(messageName, "*");
        }
	function intervalHelper(fn) {
	    var h = function() {
		fn();
		setZeroTimeout(h);
	    };
	    return h;
	}
	function setZeroInterval(fn) {
	    setZeroTimeout(intervalHelper(fn));
	}
 
        function handleMessage(event) {
            if (event.source == window && event.data == messageName) {
                event.stopPropagation();
                if (timeouts.length> 0) {
                    var fn = timeouts.shift();
                    fn();
                }
            }
        }
 
        window.addEventListener("message", handleMessage, true);
 
        // Add the one thing we want added to the window object.
        window.setZeroTimeout = setZeroTimeout;
	window.setZeroInterval = setZeroInterval;
    })();

Разница просто огромная (demo):
100 iterations of setZeroTimeout took 19 milliseconds.
100 iterations of setTimeout(0) took 393 milliseconds.


С этим минимальным изменением имеем скриншот в шапке в случае Firefox4 и включенном Direct2D/DirectWrite (посмотреть можно в about:support)

Вот типа «тест» HTML5-Canvas с примененным изменением.

Пару слов о Google Chrome: под не-Windows платформами он часто показывает 200+ fps в этом тесте на даже не очень сильных машинах (при условии включения аппаратного ускорения вывода). На Windows он упирается в лимит VSync — 60 fps. Есть Issue об этом, за который можно проголосовать поставив звездочку.

Ну и не забывайте смотреть about:flags — там есть GPU Accelerated Compositing/GPU Accelerated Canvas 2D.

Вывод


При написании теста/бенчмарка, если какой-то браузер показал необъяснимо большую разницу, не начинайте ошибочно думать о том, что этот браузер мега-супер оптимизирован. Вполне возможно просто тест тестирует не то, что кажется. К примеру, тест, который по идее должен показывать скорость HTML5-Canvas на самом деле показывал особенности реализации setTimeout и включении/выключении VSync.

PPS. Чтоб показать совсем кривость этого теста, откройте на FF4 c моим патчем тест и табнитесь в другой таб (почти сразу), потом через секунд 15-20 вернитесь — у меня было около 400 fps и медленно падало вниз. Пруф

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

Собрав комментарии я составил такую табличу результатов теста:
Opera 11 (Win) 2
Chrome 10-11 (Win) 4
Firefox (Win) 10
Safari, OS X 10.6.6 10
iPhone 10
IE 8 15.6

По мере поступления комментариев буду пополнять.

По этому поводу стоит отметить, что по рекомендации HTML5 от WhatWG/W3C, setTimeout/setInterval должен работать так:
The setInterval() method must run the following steps:
5. If timeout is less than 10, then increase timeout to 10

The setTimeout() method must run the following steps:
5. If the currently running task is a task that was created by the setTimeout() method, and timeout is less than 4, then increase timeout to 4.

Т.е. меньше чем 4 мс для setTimeout и 10 для setInterval не должно быть. Что показывает явную ошибку в любом броузере, где «оригинальный» тест дает больше 100 fps (так как в нем используется именно setInterval). 250 fps — максимум при использовании setTimeout.

UPDATE:
Сделал копию теста, в котором увеличил количество полигонов (в сфере до 72 сегментов и букв в 30 раз).
Сейчас у меня результаты (Windows7 SP1 x64, nVidia gtx 260, Core Quad 2.4ghz):
Chrome 12.0.703.0 canary build 33 fps
IE 9.0.8112.16421 26 fps
FireFox 4.0 58 fps

Никаких «заточек» и т.д. не делал (кроме setZeroTimeout, который тоже увеличивает скорость оригинального «теста» в IE9). Той мега-огромной разницы как в оригинальном тесте я не вижу.

В результате я скажу одно: криво написанный тест может "привратьприукрасить" результат в десятки, а то и сотни раз.
Tags:
Hubs:
+109
Comments 67
Comments Comments 67

Articles