Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.

Реклама



**Как получить V.I.P** (Перейти)
Чтобы заказать рекламу на Pro-Pawn.Ru, обращайтесь в Skype.
Баннерная реклама 100руб/мес, Текстовая 50руб/мес.
Показано с 1 по 1 из 1
  1. #1
    Аватар для Daniel_Cortez
    new fuck_logic[0] = EOS;

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    1,567
    Репутация:
    2076 ±

    Мифы о Pawn-скриптинге - #3

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


    Миф 3: "Лучше использовать массивы вместо обычных переменных."

    Статус: Опровергнут.

    Описание:
      Открыть/закрыть
    Бывают такие уникумы, которые любят впихивать массивы куда ни попадя, даже если они занимают всего 1 ячейку. Либо вместо нескольких переменных используют массив (например, используют массив из 3 ячеек для хранения координат игрока, когда можно обойтись 3 одиночными переменными).
    На деле же доступ к ячейкам массива медленнее, чем к одиночным переменным. Для сравнения: доступ к обычным переменным происходит по их физическим адресам (взять значение из памяти по адресу - 1 операция), а к ячейкам массива по физическому адресу массива и смещению (взять адрес массива, прибавить смещение элемента, по полученному адресу взять значение из памяти - 3 операции).
    Обычно быдлокодеры аргументируют использование массивов тем, что "так записывать проще!" или в особо запущенных случаях "так оптимизированнее!"

    Пример кода:
    PHP код:
        new Float:pos[3];
        
    GetPlayerPos(playeridpos[1], pos[2], pos[3]); 

    Доказательство:
      Открыть/закрыть
    Как обычно, сравним два образца кода, использовав приведённый выше пример с получением игрока.
    В одном образце координаты игрока будут записываться в массив, а в другом в отдельные переменные.

    Образец 1:
    PHP код:
    #include <a_samp>
    main()
    {
        new 
    Floatpos[3];
        
    GetPlayerPos(0pos[0], pos[1], pos[2]);

    Образец 2:
    PHP код:
    #include <a_samp>
    main()
    {
        new 
    Float:xFloat:yFloat:z;
        
    GetPlayerPos(0xyz);

    Объявление массива в первом образце записывается короче, чем объявление 3 отдельных переменных во втором.
    Но в то же время приходится при каждом обращении к массиву записывать индексы.
    Длина 1-го отрывка - 97 символов, второго - 94. Получается, что код без массива записывается даже короче, чем с массивом.
    Можно переименовать массив "pos" в что-нибудь из одной буквы (например, "p"), тогда получится выигрыш в 3 символа у отрывка с массивом, но назначение массива будет не так просто определить по названию. К тому же, вы всё равно потеряете больше времени на то, чтобы каждый раз дотянуться до фигурных скобок при каждом обращении к массиву.

    Итак, запись кода с массивом нисколько не проще. Но что же с оптимизацией?

    Скомпилируем оба образца с параметром "-v2", чтобы получить подробные результаты компиляции.
    Образец 1:
    Код:
    Pawn compiler 3.2.3664	 	 	Copyright (c) 1997-2006, ITB CompuPhase
    
    Header size:            124 bytes
    Code size:              136 bytes
    Data size:                0 bytes
    Stack/heap size:      16384 bytes; estimated max. usage=14 cells (56 bytes)
    Total requirements:   16644 bytes
    
    Done.
    Образец 2:
    Код:
    Pawn compiler 3.2.3664	 	 	Copyright (c) 1997-2006, ITB CompuPhase
    
    Header size:            124 bytes
    Code size:              108 bytes
    Data size:                0 bytes
    Stack/heap size:      16384 bytes; estimated max. usage=14 cells (56 bytes)
    Total requirements:   16616 bytes
    
    Done.
    Различаются только размеры секции кода (Code size), а вместе с ними и общие требования к памяти (Total requirements).
    Меньше размер секции кода во втором образце.

    Теперь скомпилируем образцы с параметром "-a" и сравним ассемблерные листинги.
    Образец 1:
    Код:
    CODE 0	; 0
    ;program exit point
    	halt 0
    
    	proc	; main
    	; line 3
    	; line 4
    	;$lcl pos fffffff4
    	stack fffffff4
    	zero.pri
    	addr.alt fffffff4
    	fill c
    	; line 5
    	addr.pri fffffff4
    	add.c 8
    	push.pri
    	;$par
    	addr.pri fffffff4
    	add.c 4
    	push.pri
    	;$par
    	push.adr fffffff4
    	;$par
    	push.c 0
    	;$par
    	push.c 10
    	sysreq.c 0	; GetPlayerPos
    	stack 14
    	;$exp
    	stack c
    	zero.pri
    	retn
    
    
    STKSIZE 1000
    Образец 2:
    Код:
    CODE 0	; 0
    ;program exit point
    	halt 0
    
    	proc	; main
    	; line 3
    	; line 4
    	;$lcl x fffffffc
    	push.c 0
    	;$exp
    	;$lcl y fffffff8
    	push.c 0
    	;$exp
    	;$lcl z fffffff4
    	push.c 0
    	;$exp
    	; line 5
    	push.adr fffffff4
    	;$par
    	push.adr fffffff8
    	;$par
    	push.adr fffffffc
    	;$par
    	push.c 0
    	;$par
    	push.c 10
    	sysreq.c 0	; GetPlayerPos
    	stack 14
    	;$exp
    	stack c
    	zero.pri
    	retn
    
    
    STKSIZE 1000
    Во втором образце всё, как и должно быть: в стеке создаются 3 переменные и в GetPlayerPos передаются их адреса (инструкции push.adr).
    А вот в первом образце для передачи pos[1] и pos[2] берётся адрес массива (addr.pri), прибавляется смещение (add.c) и уже полученный результат помещается в стек (push.pri).
    Как исключение, для передачи pos[0] в стек помещается только базовый адрес массива (push.adr): у нулевой ячейки смещение равно нулю, поэтому компилятор отбрасывает добавление смещения.
    В итоге имеем по 2 лишних инструкции при доступе к каждой ячейке массива, кроме нулевой.

    Наконец, протестируем скорость доступа к обычным переменным и к ячейкам массива.
    Для этой цели, как всегда, воспользуемся профайлером:
    PHP код:
    /*Настройки.*/
    const PROFILE_ITERATIONS_MAJOR 1000_000;
    const 
    PROFILE_ITERATIONS_MINOR 1_000;

    new const 
    code_snippets_names[2][] =
    {
        {
    "Ячейки массива"},
        {
    "Одиночные переменные"}
    };

    // Функция для подавления варнинга 203 (переменные a, x, y, и z
    // никак не используются, им только присваиваются значения).
    stock DoNothing(Float:a[], Float:bFloat:cFloat:d)
    {
        
    #pragma unused a, b, c, d
    }

    #define Prerequisites();\
        
    new Float:a[3];\
        new 
    Float:xFloat:yFloat:z;\
        
    DoNothing(axyz); // Подавить варнинг 203.

    #define CodeSnippet1();\
        
    a[0] = 0.0a[1] = 0.0a[2] = 0.0;

    #define CodeSnippet2();\
        
    0.00.00.0;
    /*Конец настроек.*/ 
    Результаты (без JIT и с JIT соответственно):
    Код:
    Тестирование: <Одиночные переменные> vs <Ячейки массива>
    Режим: интерпретируемый, 1000000x1000 итераций.
    Массив: 97382
    Одиночные переменные: 31840
    Код:
    Тестирование: <Одиночные переменные> vs <Ячейки массива>
    Режим: с JIT-компиляцией, 1000000x1000 итераций.
    Массив: 4065
    Одиночные переменные: 3385
    Результат предсказуем: доступ к одиночным переменным происходит быстрее, чем к ячейкам массива.
    Всё в точности, как и было сказано в теории (см. пункт "Описание").

    В предыдущем сравнении было замечено, что компилятор оптимизирует код доступа к 0-й ячейке массива.
    Проверим, насколько действенна эта оптимизация и может ли она сравниться с доступом к одиночной переменной.
    PHP код:
    /*Настройки.*/
    const PROFILE_ITERATIONS_MAJOR 1000_000;
    const 
    PROFILE_ITERATIONS_MINOR 1_000;

    new const 
    code_snippets_names[2][] =
    {
        {
    "a[0] = 1"},
        {
    "b = 1"}
    };

    // Функция для подавления варнинга 203 (переменные a, x, y, и z
    // никак не используются, им только присваиваются значения).
    stock DoNothing(a[], b)
    {
        
    #pragma unused a, b
    }

    #define Prerequisites();\
        
    new a[1];\
        new 
    b;\
        
    DoNothing(ab);

    #define CodeSnippet1();\
        
    a[0] = 1;

    #define CodeSnippet2();\
        
    1;
    /*Конец настроек.*/ 
    Результат (без JIT):
    Код:
    Тестирование: <a[0] = 1> vs <b = 1>
    Режим: интерпретируемый, 1000000x1000 итераций.
    a[0] = 1: 28093
    b = 1: 32096
    Довольно интересный результат: доступ к 0-й ячейке даже быстрее, чем к обычной переменной.
    Сначала я подумал, что во всём виновата неэффективная реализация интерпретатора под Windows (в исходном коде интерпретатора под Linux вместо конструкции switch используются расширения GNU C, благодаря которым интерпретатор работает быстрее).
    Поэтому я провёл тот же самый тест на сервере под Linux:
    Код:
    [18:29:11] Тестирование: <a[0] = 1> vs <b = 1>
    [18:29:11] Режим: интерпретируемый, 1000000x1000 итераций.
    [18:30:02] a[0] = 1: 22747
    [18:30:02] b = 1: 25903
    Под Linux код действительно выполнялся быстрее, но преимущество так и осталось за доступом к 0-й ячейке массива.
    С другой стороны, чтобы измерить эту разницу в скорости (3000 тиков ~ 3 секунды), понадобился один миллиард итераций (1000000 x 1000, см. результаты).
    А теперь разделите 3000 на 1 миллиард. Будет ли такое преимущество заметно на практике? Вряд ли. Зато можно сделать весь свой код неразборчивым, заменив все переменные на массивы из 1 ячейки.

    Кроме того, мы ведь так и не провели тесты с использованием JIT.
    Windows:
    Код:
    Тестирование: <a[0] = 1> vs <b = 1>
    Режим: c JIT-компиляцией, 1000000x1000 итераций.
    a[0] = 1: 3909
    b = 1: 3984
    Linux:
    Код:
    [19:12:55] Тестирование: <a[0] = 1> vs <b = 1>
    [19:12:55] Режим: c JIT-компиляцией, 10000000x1000 итераций.
    [19:13:07] a[0] = 1: 5171
    [19:13:07] b = 1: 5065
    Как видно, JIT ставит всё на свои места: оба образца выполняются примерно одинаково быстро.

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



    Специально для Pro-Pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора запрещено!
    Индивидуально в PM и Skype по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).
    SA-MP 0.4 is a lie

  2. 16 пользователя(ей) сказали cпасибо:
    #Vito (17.11.2015)$continue$ (11.11.2015)Anton Styazhkin (09.12.2015)Avertus (12.11.2015)Danny_Marcelo (16.11.2015)DeimoS (23.01.2016)Jonick (19.05.2016)Maranzalla (22.02.2016)Nurick (12.11.2015)Osetin (14.11.2015)Seviel (31.12.2016)Unreal (09.12.2015)vovandolg (07.11.2016)wAx (11.11.2015)[ForD] (12.11.2015)^_^ (13.12.2015)
 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •