Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Оптимизация: int vs uint, i+=1 vs i++ и т.д. (http://www.flasher.ru/forum/showthread.php?t=109099)

WindWalker 07.03.2008 03:56

Оптимизация: int vs uint, i+=1 vs i++ и т.д.
 
Провёл небольшое исследование.
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 инструкции.

Gaen 07.03.2008 09:04

Занятненько...
Спасибо.

terbooter 07.03.2008 12:46

Спасибо.
А я давно подозревал и везде использовал int вместо uint.
uint только в константах.

etc 07.03.2008 13:13

Не забывайте, что это разница в скорости актуальна при использовании полей класса, а не локальных переменных.

Iv 07.03.2008 13:13

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

Torero 07.03.2008 13:14

Спасибо. Интересно. На буржуйском сайте тайминг проводили - самый быстрый цикл выглядел следующим образом:
Код:

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 ?

Iv 07.03.2008 19:28

Из классики:

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

К чему это я свои 5 копеек в этот флейм тащу:
Производительность конкретного кода цикла здесь обсуждается не в рамках единого процесса оптимизации, а как отдельная фишка.

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

Не питайте иллюзий, что сабж вам поможет писать быстрые приложения.

ИМХО

WindWalker 07.03.2008 23:48

Цитата:

Сообщение от 2morrowMan
Использовался Flex Builder 3 ?

Нет, Flex Builder вообще никакой не использовался :)
Использовался консольный компилятор из Flex 2 SDK.
Кстати, хорошая идея! Надо проверить, в Flex 3 SDK, может быть что-то изменилось.


Цитата:

Сообщение от Iv
Из классики:
Только после окончания создания приложения следует протестировать производительность приложения, и только затем, на основании конкретных результатов тестов производить пошаговую оптимизацию, каждый раз обращая внимание на самые проблемные места кода.
.....
Почему не значащим? Потому, что один лишний вызов метода съест с лихвой всю экономию на всех циклах за всё время работы приложения.

А с этим никто и не спорит.

Тем не менее, для того, чтобы оптимизировать, необходимо знать как влияют на быстродействие те или иные операции.
Например, то, что вызов методов в AS3 происходит крайне медленно - это ведь тоже не с потолка взято, а опять же было проверено экспериментально.

И, скажем, тот факт, что в AS1/AS2 использование x & 1 вместо x % 2 не давало никакого выигрыша, тоже кто-то когда-то проверил.

Это я к тому, что методология методологией, но без знания быстродействия конкретных элементарных операций будет вообще непонятно КАК оптимизировать (за исключением оптимизации алгоритма, разумеется).

Gaen 08.03.2008 05:43

А можно подробнее про вызов метода?

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 08.03.2008 17:46

Цитата:

Сообщение от WindWalker
Тем не менее, для того, чтобы оптимизировать, необходимо знать как влияют на быстродействие те или иные операции.

- я, собственно, и пытаюсь донести то, что в данный момент здесь разговор идет о вещах столь незначительных для производительности, что говорить об этом не стоит вовсе.

Gaen 08.03.2008 20:45

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

Цитата:

Сообщение от Iv
- я, собственно, и пытаюсь донести то, что в данный момент здесь разговор идет о вещах столь незначительных для производительности, что говорить об этом не стоит вовсе.

Во-первых, говорить стоит обо всём. Хотя бы просто для общего развития, и чтобы понимать некоторые тонкости внутренней механики.

Во-вторых, если в цикле идёт голая математика, а сам цикл достаточно длинный, то даже такие мелочи начинают иметь значение.

В-третьих, иногда методы всё же вызываются, хотя мы этого и не предполагали. Вот, например, в чём разница между
Код:

var a:int = int(n);
и
Код:

var a:int = n;
где n имеет тип Number?

Разница в том, что в первом случае происходит вызов метода. Правда, что удивительно, на быстродействии это никак не сказывается.
Так что в AVM2 всё очень даже неоднозначно.

Iv 09.03.2008 07:27

Цитата:

Сообщение от GAIKER
Iv, так давайте поговорим о действительно значительных для производительности вещах :)

- я бы сказал читабельность и простота кода. Следование стандартам написания кода. Правильное и значимое именование. Отказ от хаков.
Знание и правильное применение хорошей практики, в частности, шаблонов проектирования.
Как итог - понятный и логичный код без ошибок и лишних операций.

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

Примерно так.

PS:
На моей практике бывали случаи, когда приходилось брать чужой усосаный хаками для производительности код, переименовывать всё и вся, убирать хаки и т.п., после этого разбираться с логикой и делать тесты. Рекордный случай - удаление порядка 17 тысяч в кадр(!) лишних вызовов методов.

Ни в одном из случаев процесс оптимизации не доходил до того, чтобы заменять циклы for на while или заменять типы данных или вызовы математических методов.
Процесс оптимизации прекращался задолго до этого, на уровне изменения логики приложения.

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

Iv 09.03.2008 07:36

WindWalker, согласен. Но тогда, наверное, не стоит подменять понятия и говорить именно о тонкостях внутренней механики, а не об оптимизации.

Gaen 09.03.2008 19:56

Цитата:

Сообщение от Iv
- я бы сказал читабельность и простота кода. Следование стандартам написания кода. Правильное и значимое именование. Отказ от хаков.
Знание и правильное применение хорошей практики, в частности, шаблонов проектирования.
Как итог - понятный и логичный код без ошибок и лишних операций.

Это прямо таки повод для холивара :)

Просто сейчас всё идёт в сторону разрастания итак громоздкого кода ради его универсальности. Т.е. в итоге жертвуем производительностью в пользу удобства написания (прочтения, понимания, отладки, редактирования) кода. Эта позиция понятна: железо постоянно совершенствуется, поэтому теперь можно себе такое позволить.

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

Фреймворки и паттерны это удобно. Это и хорошо, ведь всё-таки их далеко не дураки придумывали. Тем не менее они все являются стандартизованными для общего случая. А общее решение, как известно, проигрывает решению, найденному для данной конкретной ситуации.

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

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

Можно много ещё чего написать, вернуться к тому что я ни в коем случае не говорю что использовать паттерны это плохо итд, но я пожалуй пока остановлюсь. Ибо нахожусь немного в неадеквате :)

С удовольствием выслушаю всё, что мне на это ответят

Iv 09.03.2008 20:11

GAIKER, я не вижу никакого противоречия.
Вот ты написал приложение, оно устраивает и тебя и заказчика по производительности.
Будешь ли ты его оптимизировать? И, даже если будешь, то до какой степени? Нужна ли везде максимальная производительность? Как определить, что производительность уже максимальна и достигнут предел?

Моя практика показывает, что вообще довольно редко доходим до этапа оптимизации кода.
Но в том-то и дело, что оптимизация - отдельный этап и часто обратный универсализации.

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

Iv 09.03.2008 20:29

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

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

Iv 10.03.2008 16:22

Я стартовал топик Портирование, рефакторигн, оптимизации проекта:
http://www.flasher.ru/forum/showthread.php?t=109223
подключайтесь.


Часовой пояс GMT +4, время: 13:40.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.