Просмотр полной версии : Оптимизация: int vs uint, i+=1 vs i++ и т.д.
WindWalker
07.03.2008, 03:56
Провёл небольшое исследование.
1. Что лучше: int или uint, и почему вообще между ними есть разница.
2. Что лучше: i+=1 или i++.
Исследование проводилось следующим образом.
Нижеописанный класс был откомпилирован с помощью командного компилятора из Flex SDK (надо отметить, что компилятор Flash CS3 выдаёт те же результаты).
Затем полученный .swf был декомпилирован с помощью abcdump из проекта Tamarin.
package {
public class Test {
private var a:int;
private var b:uint;
private function int_field():void {
a=0;
a++;
a+=1;
}
private function uint_field():void {
b=0;
b++;
b+=1;
}
private function int_local():void {
var c:int;
c=0;
c++;
c+=1;
}
private function uint_local():void {
var d:uint;
d=0;
d++;
d+=1;
}
}
}
Итого, у нас есть 3 операции, и 4 случая их использования: для локальной переменной и для поля, для uint и для int.
Вот так выглядит присвоение x = 0:
Поле int:
findproperty private::a
pushbyte 0
initproperty private::a
Поле uint:
findproperty private::b
pushbyte 0
initproperty private::b
Локальная int:
pushbyte 0
setlocal1
Локальная uint:
pushbyte 0
convert_u
setlocal1
Вот так выглядит i++:
Поле int:
findpropstrict private::a
dup
setlocal1
getproperty private::a
increment_i
setlocal2
getlocal1
getlocal2
setproperty private::a
kill 2
kill 1
Поле uint:
findpropstrict private::b
dup
setlocal1
getproperty private::b
increment
setlocal2
getlocal1
getlocal2
setproperty private::b
kill 2
kill 1
Локальная int:
inclocal_i 1
Локальная uint:
getlocal1
increment
convert_u
setlocal1
Вот так выглядит i+=1:
Поле int:
findproperty private::a
getlex private::a
pushbyte 1
add
initproperty private::a
Поле uint:
findproperty private::b
getlex private::b
pushbyte 1
add
initproperty private::b
Локальная int:
getlocal1
pushbyte 1
add
convert_i
setlocal1
Локальная uint:
getlocal1
pushbyte 1
add
convert_u
setlocal1
Выводы:
1. Для полей нет разницы между int и uint (кроме области допустимых значений, разумеется).
2. При работе с локальными переменными типа uint часто используется инструкция конвертации convert_u, которая хоть и не значительно, но тем не менее может снизить произодительность. convert_i тоже иногда используется, но реже.
3. При работе с полями объекта выражение i+=1 компилируется в гораздо меньшее количество инструкций, чем i++ и выполняется значительно быстрее.
4. Наиболее быстрым выражением является i++ если i - это локальная переменная типа int. В этом случае выражение компилируется всего в одну инструкцию AVM2. Если i типа uint, то получается уже 4 инструкции.
terbooter
07.03.2008, 12:46
Спасибо.
А я давно подозревал и везде использовал int вместо uint.
uint только в константах.
Не забывайте, что это разница в скорости актуальна при использовании полей класса, а не локальных переменных.
выигрыш/проигрыш производительности составляет настолько мизерную разницу, что я не могу привести реальный пример, где бы это сыграло хоть малейшую роль.
Спасибо. Интересно. На буржуйском сайте тайминг проводили - самый быстрый цикл выглядел следующим образом:
var i:int;
while(i++<100000){
...
}
WindWalker
07.03.2008, 14:10
Конечно, всё ещё во многом зависит от того, как байт-код будет переведён в нативнй код jit-компилятором.
Однако, здравый смысл подсказывает, что
inclocal_i 1
должно работать быстрее, чем
getlocal1
increment
convert_u
setlocal1
Хотя не исключён вариант, что inclocal_i компилируется в итоге абсолютно в то же самое, что и 4 инструкции.
А колупать исходники AVM2 как-то не очень хочется :)
2morrowMan
07.03.2008, 18:31
Использовался Flex Builder 3 ?
Из классики:
На первом этапе программирования нужно сосредоточиться на правильной архитектуре приложения, читабельности кода, прозрачности и правильности логики.
Только после окончания создания приложения следует протестировать производительность приложения, и только затем, на основании конкретных результатов тестов производить пошаговую оптимизацию, каждый раз обращая внимание на самые проблемные места кода.
К чему это я свои 5 копеек в этот флейм тащу:
Производительность конкретного кода цикла здесь обсуждается не в рамках единого процесса оптимизации, а как отдельная фишка.
Боюсь, что знание методологии оптимизации кода в данном топике подменяется на следование ничему не значащим фактам.
Почему не значащим? Потому, что один лишний вызов метода съест с лихвой всю экономию на всех циклах за всё время работы приложения.
Не питайте иллюзий, что сабж вам поможет писать быстрые приложения.
ИМХО
WindWalker
07.03.2008, 23:48
Использовался Flex Builder 3 ?
Нет, Flex Builder вообще никакой не использовался :)
Использовался консольный компилятор из Flex 2 SDK.
Кстати, хорошая идея! Надо проверить, в Flex 3 SDK, может быть что-то изменилось.
Из классики:
Только после окончания создания приложения следует протестировать производительность приложения, и только затем, на основании конкретных результатов тестов производить пошаговую оптимизацию, каждый раз обращая внимание на самые проблемные места кода.
.....
Почему не значащим? Потому, что один лишний вызов метода съест с лихвой всю экономию на всех циклах за всё время работы приложения.
А с этим никто и не спорит.
Тем не менее, для того, чтобы оптимизировать, необходимо знать как влияют на быстродействие те или иные операции.
Например, то, что вызов методов в AS3 происходит крайне медленно - это ведь тоже не с потолка взято, а опять же было проверено экспериментально.
И, скажем, тот факт, что в AS1/AS2 использование x & 1 вместо x % 2 не давало никакого выигрыша, тоже кто-то когда-то проверил.
Это я к тому, что методология методологией, но без знания быстродействия конкретных элементарных операций будет вообще непонятно КАК оптимизировать (за исключением оптимизации алгоритма, разумеется).
А можно подробнее про вызов метода?
WindWalker
08.03.2008, 10:49
Вот на этих страничках можно почитать про исследование быстродействия различных арифметических операций.
http://www.rockonflash.com/blog/?p=63
http://osflash.org/as3_speed_optimizations
Было определено, что методы класса Math работают довольно медленно.
На самом деле дело не в том, что методы какие-то тормознутые, а в том, что вообще происходит вызов метода.
Например, вместо Math.abs(x) лучше использовать x<0?-x:x
Но если кто-то решит создать класс с быстрой математикой, например такой:
package {
public class FastMath {
public static function abs(x:Number):Number {
return x<0?-x:x;
}
}
}
То абсолютно никакого выиграша не получит. А скорее всего даже наоборот будет проигрыш.
А всё дело в том, что сам вызов метода и возврат назад кушают больше, чем содержимое метода.
Тем не менее, для того, чтобы оптимизировать, необходимо знать как влияют на быстродействие те или иные операции.
- я, собственно, и пытаюсь донести то, что в данный момент здесь разговор идет о вещах столь незначительных для производительности, что говорить об этом не стоит вовсе.
Iv, так давайте поговорим о действительно значительных для производительности вещах :)
2morrowMan
08.03.2008, 22:27
Вместо Math.floor можно спользовать: value >> 0; :)
Можно собрать список вот таких фич... :)
Post Update:
Протестил:
var time:Number = 0;
runFloorTest();
runFloorUintTest();
runFloorShiftTest();
function runFloorTest():void
{
time = getTimer();
for(var i:uint=0;i<10000000;i++)
{
var n:Number = 1.5;
var test:Number = Math.floor(n);
}
trace("FloorTest: ", (getTimer()-time));
}
function runFloorUintTest():void
{
time = getTimer();
for(var i:uint=0;i<10000000;i++)
{
var n:Number = 1.5;
var test:Number = uint(n);
}
trace("FloorUintTest: ", (getTimer()-time));
}
function runFloorShiftTest():void
{
time = getTimer();
for(var i:uint=0;i<10000000;i++)
{
var n:Number = 1.5;
var test:Number = n >> 0;
}
trace("FloorShiftTest: ", (getTimer()-time));
}
Результат:
FloorTest: 2046
FloorUintTest: 392
FloorShiftTest: 280
WindWalker
09.03.2008, 02:30
- я, собственно, и пытаюсь донести то, что в данный момент здесь разговор идет о вещах столь незначительных для производительности, что говорить об этом не стоит вовсе.
Во-первых, говорить стоит обо всём. Хотя бы просто для общего развития, и чтобы понимать некоторые тонкости внутренней механики.
Во-вторых, если в цикле идёт голая математика, а сам цикл достаточно длинный, то даже такие мелочи начинают иметь значение.
В-третьих, иногда методы всё же вызываются, хотя мы этого и не предполагали. Вот, например, в чём разница между
var a:int = int(n);
и
var a:int = n;
где n имеет тип Number?
Разница в том, что в первом случае происходит вызов метода. Правда, что удивительно, на быстродействии это никак не сказывается.
Так что в AVM2 всё очень даже неоднозначно.
Iv, так давайте поговорим о действительно значительных для производительности вещах :)
- я бы сказал читабельность и простота кода. Следование стандартам написания кода. Правильное и значимое именование. Отказ от хаков.
Знание и правильное применение хорошей практики, в частности, шаблонов проектирования.
Как итог - понятный и логичный код без ошибок и лишних операций.
Оптимизация кода - только как отдельный, завершающий и четко направленный на узкие места процесс, при наличии тестов.
Прекращение оптимизации по достижении достаточной производительности.
Примерно так.
PS:
На моей практике бывали случаи, когда приходилось брать чужой усосаный хаками для производительности код, переименовывать всё и вся, убирать хаки и т.п., после этого разбираться с логикой и делать тесты. Рекордный случай - удаление порядка 17 тысяч в кадр(!) лишних вызовов методов.
Ни в одном из случаев процесс оптимизации не доходил до того, чтобы заменять циклы for на while или заменять типы данных или вызовы математических методов.
Процесс оптимизации прекращался задолго до этого, на уровне изменения логики приложения.
PPS:
И да, не юзайте чужие хакерские приемы не проверив. И в этом топике в коде есть ошибки.
Четко отдавайте себе отчет, готовы ли вы взамен на выигрыш в 0.0003 миллисекунды жертвовать читабельностью кода и риском внесения ошибки.
WindWalker, согласен. Но тогда, наверное, не стоит подменять понятия и говорить именно о тонкостях внутренней механики, а не об оптимизации.
- я бы сказал читабельность и простота кода. Следование стандартам написания кода. Правильное и значимое именование. Отказ от хаков.
Знание и правильное применение хорошей практики, в частности, шаблонов проектирования.
Как итог - понятный и логичный код без ошибок и лишних операций.
Это прямо таки повод для холивара :)
Просто сейчас всё идёт в сторону разрастания итак громоздкого кода ради его универсальности. Т.е. в итоге жертвуем производительностью в пользу удобства написания (прочтения, понимания, отладки, редактирования) кода. Эта позиция понятна: железо постоянно совершенствуется, поэтому теперь можно себе такое позволить.
А почему бы не продолжать писать продуманный код, получая выигрыш в скорости при совершенствовании железа?
Фреймворки и паттерны это удобно. Это и хорошо, ведь всё-таки их далеко не дураки придумывали. Тем не менее они все являются стандартизованными для общего случая. А общее решение, как известно, проигрывает решению, найденному для данной конкретной ситуации.
Конечно для этого требуется более высокая квалификация кодера, но на то он и программер чтобы думать. Недостаточно просто выучить язык, нужно уметь правильно всё придумать и организовать.
Сейчас программить становится всё легче и легче, соответственно растёт число быдлокодеров, которые пишут тормозной код, потому что его легче всего написать.
Можно много ещё чего написать, вернуться к тому что я ни в коем случае не говорю что использовать паттерны это плохо итд, но я пожалуй пока остановлюсь. Ибо нахожусь немного в неадеквате :)
С удовольствием выслушаю всё, что мне на это ответят
GAIKER, я не вижу никакого противоречия.
Вот ты написал приложение, оно устраивает и тебя и заказчика по производительности.
Будешь ли ты его оптимизировать? И, даже если будешь, то до какой степени? Нужна ли везде максимальная производительность? Как определить, что производительность уже максимальна и достигнут предел?
Моя практика показывает, что вообще довольно редко доходим до этапа оптимизации кода.
Но в том-то и дело, что оптимизация - отдельный этап и часто обратный универсализации.
В этом ряду совершенно особняком стоят публичные библиотеки кода, поскольку заведомо неизвестно где и на какой нагрузке их код будет работать.
Но и здесь имеется предел, дальше которого не стоит оптимизировать код только потому, что выигрыш ничтожен по сравнению, например, с накладными расходами на вызов метода.
И еще замечание относительно публичных библиотек: прежде чем перейти к процессу оптимизации, библиотека должна пожить. Авторы должны услышать замечания, исправить баги, знать, где на практике пользователи чаще всего сталкиваются с проблемами недостаточности производительности.
Поэтому, если вы используете публичные либы, то не стесняйтесь писать их авторам об обнаруженных недостатках или своих пожеланиях - они именно для этого выложили код.
Если хотите, это ваша плата за использование.
Я стартовал топик Портирование, рефакторигн, оптимизации проекта:
http://www.flasher.ru/forum/showthread.php?t=109223
подключайтесь.
Работает на vBulletin ® версия 3.7.3. Copyright ©2000-2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Copyright © 1999-2008 Flasher.ru. All rights reserved.