Недавно увидел пост Тест производительности работы браузера с 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). Той мега-огромной разницы как в оригинальном тесте я не вижу.
В результате я скажу одно: криво написанный тест может "