VVWVV
05.04.2018, 17:34
Введение
Множество разработчиков желают оптимизировать свой код, используя различные техники оптимизации. Довольно интересным методом оптимизации является использование оператора множественного выбора (или switch) и обычного условного оператора (или if).
Обычный условный оператор
Обычный условный оператор предполагает выражение, которые будет проверено на этапе исполнения, то есть в процессе работы программы. Таким образом, сначала оператору необходимо вычислить все выражения, и только потом сравнить их. На данные вычисления уходит достаточного много времени. Тем более, если вам необходимо несколько сравнений для одного и того же значения (к примеру, значения из массива).
new baz = 4;
if ((baz == 1) || (baz == 2) || (baz == 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).
Оператор множественного выбора
Оператор множественного выбора предполагает значение, которое будет сравниваться с числами. Эти числа необходимо занести в тело оператора.
new baz = 4;
switch (baz) {
case 1,2,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 (профилирование) (http://pro-pawn.ru/showthread.php?12585)".
Тестирование: <if> vs <switch>
Режим: интерпретируемый, 10000x10000 итераций.
if: 18286
switch: 10852
// Profiler v1.3 (copyright (c) 2014-2017 Daniel_Cortez) \\ Pro-Pawn.ru
// Условия использования данного кода: http://pro-pawn.ru/showthread.php?12585
/*======== Настройки =========================================================*/
// Кол-во итераций в циклах.
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 10_000;
// Названия отрывков кода.
new const code_snippets_names[2][] =
{
{"if"},
{"switch"}
};
// Здесь вы можете объявить переменные, используемые в профилируемых отрывках кода
// и выполнить некоторые действия непосредственно перед профилированием.
#define Prerequisites(); \
new baz;
#define CodeSnippet0(); \
if ((baz==1) || (baz==2) || (baz==4)) {}
#define CodeSnippet1(); \
switch (baz) { \
case 1,2,4: {} \
}
/*======== Конец настроек ===================================================*/
// Не рекомендую изменять следующий код, если вы в нём не разбираетесь.
#tryinclude <a_samp>
#if defined _samp_included
#define LINE_BREAK ""
#else
#define LINE_BREAK "\n"
#include <core>
#include <time>
#define GetTickCount() tickcount()
#endif
#define _PROFILE_START(%0) \
__t[%0] = GetTickCount(); \
for (__j = 0; __j < PROFILER_ITERATIONS_MINOR; ++__j) \
{
#define _PROFILE_END(%0) __t[%0] = GetTickCount()-__t[%0]
#if defined _samp_included
#define PROFILE_START(%0); _PROFILE_START(%0)
#define PROFILE_END(%0); \
} \
_PROFILE_END(%0);
#else
#define PROFILE_START(%0); _PROFILE_START(%0)
#define PROFILE_END(%0);\
} \
if ((_PROFILE_END(%0)) < 0) {--__i; continue;}
#endif
#define CODE_SNIPPET_EXISTS(%0) \
(sizeof(code_snippets_names) >= ((%0) + 1)) && (defined CodeSnippet%0)
new code_snippets_time[sizeof(code_snippets_names)] = {0, ...};
main()
{
new __i, __j, __t[sizeof(code_snippets_names)];
#emit zero.pri
#emit lctrl 7
#emit stor.s.pri __i
#if CODE_SNIPPET_EXISTS(2)
static const ending[] =
#if (sizeof(code_snippets_names) <= 4)
"ка";
#else
"ков";
#endif
printf(
"Тестирование: %d отрыв%s кода." LINE_BREAK,
sizeof(code_snippets_names), ending
);
#else
printf(
"Тестирование: <%s> vs <%s>" LINE_BREAK,
code_snippets_names[0], code_snippets_names[1]
);
#endif
static const JIT_status_strings[2][] =
{"интерпретируемый", "с JIT-компиляцией"};
printf(
"Режим: %s, %dx%d итераций.\a" LINE_BREAK,
JIT_status_strings[__i],
PROFILER_ITERATIONS_MAJOR, PROFILER_ITERATIONS_MINOR
);
Prerequisites();
for (__i = 0; __i < PROFILER_ITERATIONS_MAJOR; ++__i)
{
PROFILE_START(0);
CodeSnippet0();
PROFILE_END(0);
PROFILE_START(1);
CodeSnippet1();
PROFILE_END(1);
#if CODE_SNIPPET_EXISTS(2)
PROFILE_START(2);
CodeSnippet2();
PROFILE_END(2);
#endif
#if CODE_SNIPPET_EXISTS(3)
PROFILE_START(3);
CodeSnippet3();
PROFILE_END(3);
#endif
#if CODE_SNIPPET_EXISTS(4)
PROFILE_START(4);
CodeSnippet4();
PROFILE_END(4);
#endif
#if CODE_SNIPPET_EXISTS(5)
PROFILE_START(5);
CodeSnippet5();
PROFILE_END(5);
#endif
#if CODE_SNIPPET_EXISTS(6)
PROFILE_START(6);
CodeSnippet6();
PROFILE_END(6);
#endif
#if CODE_SNIPPET_EXISTS(7)
PROFILE_START(7);
CodeSnippet7();
PROFILE_END(7);
#endif
#if CODE_SNIPPET_EXISTS(8)
PROFILE_START(8);
CodeSnippet8();
PROFILE_END(8);
#endif
#if CODE_SNIPPET_EXISTS(9)
PROFILE_START(9);
CodeSnippet9();
PROFILE_END(9);
#endif
#if CODE_SNIPPET_EXISTS(10)
PROFILE_START(10);
CodeSnippet10();
PROFILE_END(10);
#endif
#if CODE_SNIPPET_EXISTS(11)
PROFILE_START(11);
CodeSnippet11();
PROFILE_END(11);
#endif
#if CODE_SNIPPET_EXISTS(12)
PROFILE_START(12);
CodeSnippet12();
PROFILE_END(12);
#endif
#if CODE_SNIPPET_EXISTS(13)
PROFILE_START(13);
CodeSnippet13();
PROFILE_END(13);
#endif
#if CODE_SNIPPET_EXISTS(14)
PROFILE_START(14);
CodeSnippet14();
PROFILE_END(14);
#endif
#if CODE_SNIPPET_EXISTS(15)
PROFILE_START(15);
CodeSnippet15();
PROFILE_END(15);
#endif
for (__j = 0; __j < sizeof(code_snippets_names); ++__j)
code_snippets_time[__j] += __t[__j];
}
for (__i = 0; __i < sizeof(code_snippets_names); ++__i)
printf(
"%s: %d" LINE_BREAK,
code_snippets_names[__i], code_snippets_time[__i]
);
printf("\a\a" LINE_BREAK);
}
Статью подготовил: VVWVV (http://pro-pawn.ru/member.php?4348)
Если у вас все ещё остались вопросы по оптимизации кода при использовании данных операторов, то пишите их.
Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора запрещено!
Множество разработчиков желают оптимизировать свой код, используя различные техники оптимизации. Довольно интересным методом оптимизации является использование оператора множественного выбора (или switch) и обычного условного оператора (или if).
Обычный условный оператор
Обычный условный оператор предполагает выражение, которые будет проверено на этапе исполнения, то есть в процессе работы программы. Таким образом, сначала оператору необходимо вычислить все выражения, и только потом сравнить их. На данные вычисления уходит достаточного много времени. Тем более, если вам необходимо несколько сравнений для одного и того же значения (к примеру, значения из массива).
new baz = 4;
if ((baz == 1) || (baz == 2) || (baz == 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).
Оператор множественного выбора
Оператор множественного выбора предполагает значение, которое будет сравниваться с числами. Эти числа необходимо занести в тело оператора.
new baz = 4;
switch (baz) {
case 1,2,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 (профилирование) (http://pro-pawn.ru/showthread.php?12585)".
Тестирование: <if> vs <switch>
Режим: интерпретируемый, 10000x10000 итераций.
if: 18286
switch: 10852
// Profiler v1.3 (copyright (c) 2014-2017 Daniel_Cortez) \\ Pro-Pawn.ru
// Условия использования данного кода: http://pro-pawn.ru/showthread.php?12585
/*======== Настройки =========================================================*/
// Кол-во итераций в циклах.
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 10_000;
// Названия отрывков кода.
new const code_snippets_names[2][] =
{
{"if"},
{"switch"}
};
// Здесь вы можете объявить переменные, используемые в профилируемых отрывках кода
// и выполнить некоторые действия непосредственно перед профилированием.
#define Prerequisites(); \
new baz;
#define CodeSnippet0(); \
if ((baz==1) || (baz==2) || (baz==4)) {}
#define CodeSnippet1(); \
switch (baz) { \
case 1,2,4: {} \
}
/*======== Конец настроек ===================================================*/
// Не рекомендую изменять следующий код, если вы в нём не разбираетесь.
#tryinclude <a_samp>
#if defined _samp_included
#define LINE_BREAK ""
#else
#define LINE_BREAK "\n"
#include <core>
#include <time>
#define GetTickCount() tickcount()
#endif
#define _PROFILE_START(%0) \
__t[%0] = GetTickCount(); \
for (__j = 0; __j < PROFILER_ITERATIONS_MINOR; ++__j) \
{
#define _PROFILE_END(%0) __t[%0] = GetTickCount()-__t[%0]
#if defined _samp_included
#define PROFILE_START(%0); _PROFILE_START(%0)
#define PROFILE_END(%0); \
} \
_PROFILE_END(%0);
#else
#define PROFILE_START(%0); _PROFILE_START(%0)
#define PROFILE_END(%0);\
} \
if ((_PROFILE_END(%0)) < 0) {--__i; continue;}
#endif
#define CODE_SNIPPET_EXISTS(%0) \
(sizeof(code_snippets_names) >= ((%0) + 1)) && (defined CodeSnippet%0)
new code_snippets_time[sizeof(code_snippets_names)] = {0, ...};
main()
{
new __i, __j, __t[sizeof(code_snippets_names)];
#emit zero.pri
#emit lctrl 7
#emit stor.s.pri __i
#if CODE_SNIPPET_EXISTS(2)
static const ending[] =
#if (sizeof(code_snippets_names) <= 4)
"ка";
#else
"ков";
#endif
printf(
"Тестирование: %d отрыв%s кода." LINE_BREAK,
sizeof(code_snippets_names), ending
);
#else
printf(
"Тестирование: <%s> vs <%s>" LINE_BREAK,
code_snippets_names[0], code_snippets_names[1]
);
#endif
static const JIT_status_strings[2][] =
{"интерпретируемый", "с JIT-компиляцией"};
printf(
"Режим: %s, %dx%d итераций.\a" LINE_BREAK,
JIT_status_strings[__i],
PROFILER_ITERATIONS_MAJOR, PROFILER_ITERATIONS_MINOR
);
Prerequisites();
for (__i = 0; __i < PROFILER_ITERATIONS_MAJOR; ++__i)
{
PROFILE_START(0);
CodeSnippet0();
PROFILE_END(0);
PROFILE_START(1);
CodeSnippet1();
PROFILE_END(1);
#if CODE_SNIPPET_EXISTS(2)
PROFILE_START(2);
CodeSnippet2();
PROFILE_END(2);
#endif
#if CODE_SNIPPET_EXISTS(3)
PROFILE_START(3);
CodeSnippet3();
PROFILE_END(3);
#endif
#if CODE_SNIPPET_EXISTS(4)
PROFILE_START(4);
CodeSnippet4();
PROFILE_END(4);
#endif
#if CODE_SNIPPET_EXISTS(5)
PROFILE_START(5);
CodeSnippet5();
PROFILE_END(5);
#endif
#if CODE_SNIPPET_EXISTS(6)
PROFILE_START(6);
CodeSnippet6();
PROFILE_END(6);
#endif
#if CODE_SNIPPET_EXISTS(7)
PROFILE_START(7);
CodeSnippet7();
PROFILE_END(7);
#endif
#if CODE_SNIPPET_EXISTS(8)
PROFILE_START(8);
CodeSnippet8();
PROFILE_END(8);
#endif
#if CODE_SNIPPET_EXISTS(9)
PROFILE_START(9);
CodeSnippet9();
PROFILE_END(9);
#endif
#if CODE_SNIPPET_EXISTS(10)
PROFILE_START(10);
CodeSnippet10();
PROFILE_END(10);
#endif
#if CODE_SNIPPET_EXISTS(11)
PROFILE_START(11);
CodeSnippet11();
PROFILE_END(11);
#endif
#if CODE_SNIPPET_EXISTS(12)
PROFILE_START(12);
CodeSnippet12();
PROFILE_END(12);
#endif
#if CODE_SNIPPET_EXISTS(13)
PROFILE_START(13);
CodeSnippet13();
PROFILE_END(13);
#endif
#if CODE_SNIPPET_EXISTS(14)
PROFILE_START(14);
CodeSnippet14();
PROFILE_END(14);
#endif
#if CODE_SNIPPET_EXISTS(15)
PROFILE_START(15);
CodeSnippet15();
PROFILE_END(15);
#endif
for (__j = 0; __j < sizeof(code_snippets_names); ++__j)
code_snippets_time[__j] += __t[__j];
}
for (__i = 0; __i < sizeof(code_snippets_names); ++__i)
printf(
"%s: %d" LINE_BREAK,
code_snippets_names[__i], code_snippets_time[__i]
);
printf("\a\a" LINE_BREAK);
}
Статью подготовил: VVWVV (http://pro-pawn.ru/member.php?4348)
Если у вас все ещё остались вопросы по оптимизации кода при использовании данных операторов, то пишите их.
Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора запрещено!