Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 7 из 7
  1. #1
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±

    Оптимизация. Что выбрать: switch или if?

    Введение
    Множество разработчиков желают оптимизировать свой код, используя различные техники оптимизации. Довольно интересным методом оптимизации является использование оператора множественного выбора (или switch) и обычного условного оператора (или if).

    Обычный условный оператор
    Обычный условный оператор предполагает выражение, которые будет проверено на этапе исполнения, то есть в процессе работы программы. Таким образом, сначала оператору необходимо вычислить все выражения, и только потом сравнить их. На данные вычисления уходит достаточного много времени. Тем более, если вам необходимо несколько сравнений для одного и того же значения (к примеру, значения из массива).
    1. new baz = 4;
    2. if ((baz == 1) || (baz == 2) || (baz == 4)) {
    3. /* какой-то код.. */
    4. }
    В таком случае компилятору придётся создать несколько инструкций, которые будут каждый раз загружать в регистр значение из памяти; если говорить проще, то они будут каждый раз копироваться в регистр, и только потом сравниваться. Поэтому в этом случае лучше воспользоваться оператором множественного выбора (или switch).

    Код:
    load.s.pri fffffffc ; Загрузка значения из переменной (1)
    const.alt 1         ; Загрузка значения в регистр alt.
    eq                  ; Сравнение.
    jnz 1               ; Переход к телу цикла.
    load.s.pri fffffffc ; Загрузка значения из переменной (2)
    const.alt 2         ; Загрузка значения в регистр alt.
    eq                  ; Сравнение.
    jnz 1               ; Переход к телу цикла.
    load.s.pri fffffffc ; Загрузка значения из переменной (3)
    const.alt 4         ; Загрузка значения в регистр alt.
    eq                  ; Сравнение.
    jnz 1               ; Переход к телу цикла.
    Случаи использования: Тем не менее следует использовать данный оператор, если вместо операторов равенство (==) или неравенство (!=) используются операторы больше (>), меньше (<), больше или равно (>=), меньше или равно (<=). Тем более, когда они используются при обозначении диапазона значений (например, диапазон значений для n от нуля до трёх включительно – 0 < n <= 3).

    Оператор множественного выбора
    Оператор множественного выбора предполагает значение, которое будет сравниваться с числами. Эти числа необходимо занести в тело оператора.
    1. new baz = 4;
    2. switch (baz) {
    3. case 1,2,4: { /* какой-то код.. */ }
    4. }
    Таким образом, компилятор создаст таблицу значений, где будут наши предполагаемые числа и переходы к их инструкциям, а также всего лишь одна инструкция копирования.

    Код:
    load.s.pri fffffffc ; Только одно копирование из памяти.
    switch 0            ; Начало оператора.
    l.2                 ; Переход к телу
    jump 1
    l.0                 ; Таблица
    casetbl
    case 3 1            ; Обычная ветка, если ничего не нашлось.
    case 1 2            ; Сравнение.
    case 2 2            ; Сравнение.
    case 4 2            ; Сравнение.
    Случаи использования: Не следует использовать данный оператор при обозначении диапазона значений (например, case 0…3), поскольку компилятор создаст слишком большую таблицу, которая займёт больше времени, чем от использования простого условного оператора.

    Тестирование
    Для сравнения скорости двух операторов был использован алгоритм из темы "Сравнение производительности кода на Pawn (профилирование)".
    Код:
    Тестирование: <if> vs <switch>
    Режим: интерпретируемый, 10000x10000 итераций.
    if: 18286
    switch: 10852
     Полный код
    1. // Profiler v1.3 (copyright (c) 2014-2017 Daniel_Cortez) \\ Pro-Pawn.ru
    2. // Условия использования данного кода: <a href="http://pro-pawn.ru/showthread.php?12585" target="_blank">http://pro-pawn.ru/showthread.php?12585</a>
    3.  
    4.  
    5. /*======== Настройки =========================================================*/
    6. // Кол-во итераций в циклах.
    7. const PROFILER_ITERATIONS_MAJOR = 10_000;
    8. const PROFILER_ITERATIONS_MINOR = 10_000;
    9.  
    10. // Названия отрывков кода.
    11. new const code_snippets_names[2][] =
    12. {
    13. {"if"},
    14. {"switch"}
    15. };
    16.  
    17. // Здесь вы можете объявить переменные, используемые в профилируемых отрывках кода
    18. // и выполнить некоторые действия непосредственно перед профилированием.
    19. #define Prerequisites(); \
    20.   new baz;
    21.  
    22. #define CodeSnippet0(); \
    23.   if ((baz==1) || (baz==2) || (baz==4)) {}
    24.  
    25. #define CodeSnippet1(); \
    26.   switch (baz) { \
    27.   case 1,2,4: {} \
    28.   }
    29. /*======== Конец настроек ===================================================*/
    30.  
    31.  
    32.  
    33. // Не рекомендую изменять следующий код, если вы в нём не разбираетесь.
    34. #tryinclude <a_samp>
    35. #if defined _samp_included
    36. #define LINE_BREAK ""
    37. #else
    38. #define LINE_BREAK "\n"
    39. #include <core>
    40. #include <time>
    41. #define GetTickCount() tickcount()
    42. #endif
    43.  
    44. #define _PROFILE_START(%0) \
    45.   __t[%0] = GetTickCount(); \
    46.   for (__j = 0; __j < PROFILER_ITERATIONS_MINOR; ++__j) \
    47.   {
    48. #define _PROFILE_END(%0) __t[%0] = GetTickCount()-__t[%0]
    49.  
    50. #if defined _samp_included
    51. #define PROFILE_START(%0); _PROFILE_START(%0)
    52. #define PROFILE_END(%0); \
    53.   } \
    54.   _PROFILE_END(%0);
    55. #else
    56. #define PROFILE_START(%0); _PROFILE_START(%0)
    57. #define PROFILE_END(%0);\
    58.   } \
    59.   if ((_PROFILE_END(%0)) < 0) {--__i; continue;}
    60. #endif
    61.  
    62. #define CODE_SNIPPET_EXISTS(%0) \
    63.   (sizeof(code_snippets_names) >= ((%0) + 1)) && (defined CodeSnippet%0)
    64.  
    65.  
    66. new code_snippets_time[sizeof(code_snippets_names)] = {0, ...};
    67.  
    68. main()
    69. {
    70. new __i, __j, __t[sizeof(code_snippets_names)];
    71. #emit zero.pri
    72. #emit lctrl 7
    73. #emit stor.s.pri __i
    74. #if CODE_SNIPPET_EXISTS(2)
    75. static const ending[] =
    76. #if (sizeof(code_snippets_names) <= 4)
    77. "ка";
    78. #else
    79. "ков";
    80. #endif
    81. "Тестирование: %d отрыв%s кода." LINE_BREAK,
    82. sizeof(code_snippets_names), ending
    83. );
    84. #else
    85. "Тестирование: <%s> vs <%s>" LINE_BREAK,
    86. code_snippets_names[0], code_snippets_names[1]
    87. );
    88. #endif
    89. static const JIT_status_strings[2][] =
    90. {"интерпретируемый", "с JIT-компиляцией"};
    91. "Режим: %s, %dx%d итераций.\a" LINE_BREAK,
    92. JIT_status_strings[__i],
    93. PROFILER_ITERATIONS_MAJOR, PROFILER_ITERATIONS_MINOR
    94. );
    95. Prerequisites();
    96. for (__i = 0; __i < PROFILER_ITERATIONS_MAJOR; ++__i)
    97. {
    98. PROFILE_START(0);
    99. CodeSnippet0();
    100. PROFILE_END(0);
    101. PROFILE_START(1);
    102. CodeSnippet1();
    103. PROFILE_END(1);
    104. #if CODE_SNIPPET_EXISTS(2)
    105. PROFILE_START(2);
    106. CodeSnippet2();
    107. PROFILE_END(2);
    108. #endif
    109. #if CODE_SNIPPET_EXISTS(3)
    110. PROFILE_START(3);
    111. CodeSnippet3();
    112. PROFILE_END(3);
    113. #endif
    114. #if CODE_SNIPPET_EXISTS(4)
    115. PROFILE_START(4);
    116. CodeSnippet4();
    117. PROFILE_END(4);
    118. #endif
    119. #if CODE_SNIPPET_EXISTS(5)
    120. PROFILE_START(5);
    121. CodeSnippet5();
    122. PROFILE_END(5);
    123. #endif
    124. #if CODE_SNIPPET_EXISTS(6)
    125. PROFILE_START(6);
    126. CodeSnippet6();
    127. PROFILE_END(6);
    128. #endif
    129. #if CODE_SNIPPET_EXISTS(7)
    130. PROFILE_START(7);
    131. CodeSnippet7();
    132. PROFILE_END(7);
    133. #endif
    134. #if CODE_SNIPPET_EXISTS(8)
    135. PROFILE_START(8);
    136. CodeSnippet8();
    137. PROFILE_END(8);
    138. #endif
    139. #if CODE_SNIPPET_EXISTS(9)
    140. PROFILE_START(9);
    141. CodeSnippet9();
    142. PROFILE_END(9);
    143. #endif
    144. #if CODE_SNIPPET_EXISTS(10)
    145. PROFILE_START(10);
    146. CodeSnippet10();
    147. PROFILE_END(10);
    148. #endif
    149. #if CODE_SNIPPET_EXISTS(11)
    150. PROFILE_START(11);
    151. CodeSnippet11();
    152. PROFILE_END(11);
    153. #endif
    154. #if CODE_SNIPPET_EXISTS(12)
    155. PROFILE_START(12);
    156. CodeSnippet12();
    157. PROFILE_END(12);
    158. #endif
    159. #if CODE_SNIPPET_EXISTS(13)
    160. PROFILE_START(13);
    161. CodeSnippet13();
    162. PROFILE_END(13);
    163. #endif
    164. #if CODE_SNIPPET_EXISTS(14)
    165. PROFILE_START(14);
    166. CodeSnippet14();
    167. PROFILE_END(14);
    168. #endif
    169. #if CODE_SNIPPET_EXISTS(15)
    170. PROFILE_START(15);
    171. CodeSnippet15();
    172. PROFILE_END(15);
    173. #endif
    174. for (__j = 0; __j < sizeof(code_snippets_names); ++__j)
    175. code_snippets_time[__j] += __t[__j];
    176. }
    177. for (__i = 0; __i < sizeof(code_snippets_names); ++__i)
    178. "%s: %d" LINE_BREAK,
    179. code_snippets_names[__i], code_snippets_time[__i]
    180. );
    181. printf("\a\a" LINE_BREAK);
    182. }


    Статью подготовил: VVWVV

    Если у вас все ещё остались вопросы по оптимизации кода при использовании данных операторов, то пишите их.

    Исключительно для pro-pawn.ru
    Копирование данной статьи на других ресурсах без разрешения автора запрещено!
    Последний раз редактировалось VVWVV; 05.04.2018 в 19:31.

  2. #2
    Аватар для Nexius_Tailer
    Пользователь

    Статус
    Оффлайн
    Регистрация
    04.01.2015
    Адрес
    Гомель, Беларусь
    Сообщений
    547
    Репутация:
    158 ±
    Некоторые тесты скорости между конкретными случаями использования обоих операторов были бы очень уместны (особенно для примера, где быстрее тот или иной оператор, и уже как вывод на основе тестов описывалось бы когда что лучше использовать).
    Не хотите постоянно проверять обновления моих скриптов?
    Подключите его последним, после всех остальных
    Nexius's Update Checker

  3. #3
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Nexius_Tailer Посмотреть сообщение
    Некоторые тесты скорости между конкретными случаями использования обоих операторов были бы очень уместны (особенно для примера, где быстрее тот или иной оператор, и уже как вывод на основе тестов описывалось бы когда что лучше использовать).
    Я ж их... сейчас..

  4. #4
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Оператор множественного выбора
    (...)
    Не следует использовать данный оператор при обозначении диапазона значений (например, case 0…3)
    Диапазон из 4 значений как раз достаточно мал, чтобы оператор switch работал с ним быстрее, чем if. Нужно как минимум 9-10 значений в диапазоне, чтобы switch начал проигрывать - по крайней мере, это справедливо в случае со специфичным для GCC ядром интерпретатора.

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

    Над формулировками в теме тоже стоит поработать (то и дело просматривается тавтология) - если не против, могу с этим помочь.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  5. #5
    Аватар для Elrmrnt-Kritik
    Пользователь

    Статус
    Оффлайн
    Регистрация
    05.11.2017
    Сообщений
    136
    Репутация:
    10 ±
    Нужно как минимум 9-10 значений в диапазоне, чтобы switch начал проигрывать
    Не оговорка? Может выигрывать? Либо if, а не switch :)

  6. #6
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от Elrmrnt-Kritik Посмотреть сообщение
    Не оговорка? Может выигрывать? Либо if, а не switch :)
    Не оговорка. Чем больший диапазон значений перебирается в switch, тем дольше он будет это делать, так как, например, в случае "case 0..10", switch не просто сравнит число с 0 и с 10, после выдав ответ, а будет сравнивать значение, указанное в switch 11 раз подряд (с 0, с 1, с 2, с 3, с 4 и т.п.).
    Если же указать "if(0 <= var <= 10)", то это будет работать так же, как и "if(var >= 0 && var <= 10)", то бишь, будет только 2 сравнения, а не 11.

    А в маленьких диапазонах switch выигрывает за счёт того, что значение переменной, указанной в switch, записывается в память один раз и потом сравнивается со всеми указанными в case значениями, когда, в случае с if, обращение к переменной происходит каждое сравнение (даже вот этот код: "if(0 <= var <= 10)" - на деле будет генерировать 2 отдельных обращения к переменной var, дабы сначала сравнить её с 0, а потом сравнить с 10). Иными словами, switch будет просто меньше инструкций генерировать и всё такое.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  7. #7
    Аватар для VVWVV
    ?

    Статус
    Оффлайн
    Регистрация
    09.07.2015
    Сообщений
    731
    Репутация:
    353 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Диапазон из 4 значений как раз достаточно мал, чтобы оператор switch работал с ним быстрее, чем if. Нужно как минимум 9-10 значений в диапазоне, чтобы switch начал проигрывать - по крайней мере, это справедливо в случае со специфичным для GCC ядром интерпретатора.

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

    Над формулировками в теме тоже стоит поработать (то и дело просматривается тавтология) - если не против, могу с этим помочь.
    Я ее написал за 10-20 минут, поскольку больше времени у меня не было. Конечно, я буду только рад исправлениям. Кстати, ты можешь переписать(удалить) ее в тему мифов.
    Последний раз редактировалось VVWVV; 07.04.2018 в 11:18.

 

 

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

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

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

Ваши права

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