**** Разъяснение **: Я не ищу самый быстрый код или оптимизацию. Я хотел бы понять, почему некоторый код, который, кажется, не оптимизирован или не оптимален, фактически работает в целом последовательно быстрее.
Краткая версия
Почему этот код:
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
Более эффективный, чем этот?
var index = Math.floor(ref_index) * 4;
Длинная версия
На этой неделе автор Impact js опубликовал статью о некоторой проблеме рендеринга:
http://www.phoboslab.org/log/2012/09/drawing-pixels-is-hard
В статье был источник функции масштабирования изображения путем доступа к пикселям в холсте. Я хотел предложить некоторые традиционные способы оптимизации такого кода, чтобы масштабирование было короче во время загрузки. Но после тестирования его результат был в большинстве случаев хуже, чем исходная функция.
Предполагая, что это был движок JavaScript, который делал некоторую интеллектуальную оптимизацию, я попытался понять немного больше, что происходило, поэтому я сделал кучу тестов. Но мои результаты довольно сбивают с толку, и мне нужна была какая-то помощь, чтобы понять, что происходит.
У меня есть тестовая страница здесь:
http://www.mx981.com/stuff/resize_bench/test.html
jsPerf: http://jsperf.com/local-variable-due-to-the-scope-lookup
Чтобы запустить тест, щелкните изображение, и результаты появятся на консоли.
Существуют три разные версии:
Исходный код:
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
var indexScaled = (y * widthScaled + x) * 4;
scaledPixels.data[ indexScaled ] = origPixels.data[ index ];
scaledPixels.data[ indexScaled+1 ] = origPixels.data[ index+1 ];
scaledPixels.data[ indexScaled+2 ] = origPixels.data[ index+2 ];
scaledPixels.data[ indexScaled+3 ] = origPixels.data[ index+3 ];
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
Одна из моих попыток оптимизировать его:
var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = Math.floor(ref_index) * 4;
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];
ref_index+= ref_step;
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
Тот же оптимизированный код, но с пересчетом индексной переменной каждый раз (Hybrid)
var ref_index = 0;
var ref_indexScaled = 0
var ref_step = 1 / scale;
for( var y = 0; y < heightScaled; y++ ) {
for( var x = 0; x < widthScaled; x++ ) {
var index = (Math.floor(y / scale) * img.width + Math.floor(x / scale)) * 4;
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+1 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+2 ];
scaledPixels.data[ ref_indexScaled++ ] = origPixels.data[ index+3 ];
ref_index+= ref_step;
}
}
jsPerf: http://jsperf.com/so-accessing-local-variable-doesn-t-improve-performance
Единственное различие в двух последних - это вычисление переменной "index". И, к моему удивлению, оптимизированная версия в большинстве браузеров медленнее (кроме оперы).
Результаты личного тестирования (а не тесты jsPerf):
-
Opera
Original: 8668ms Optimized: 932ms Hybrid: 8696ms
-
хром
Original: 139ms Optimized: 145ms Hybrid: 136ms
-
Safari
Original: 433ms Optimized: 853ms Hybrid: 451ms
-
Firefox
Original: 343ms Optimized: 422ms Hybrid: 350ms
После копания, кажется, обычной практикой является доступ к главным образом локальной переменной из-за поиска области. Поскольку оптимизированная версия вызывает только одну локальную переменную, она должна быть быстрее, чем гибридный код, который вызывает несколько переменных и объектов в дополнение к различным действиям.
Итак, почему "оптимизированная" версия медленнее?
Я думал, что это может быть из-за того, что какой-то движок JavaScript не оптимизирует оптимизированную версию, потому что он не достаточно горячий, но после использования --trace-opt
в хром кажется, что вся версия правильно скомпилирована V8.
В этот момент я немного невежественный и удивляюсь, если кто-нибудь узнает, что происходит?
Я также сделал еще несколько тестовых примеров на этой странице: