Сравнение производительности кода на Pawn (профилирование)
Привет всем участникам Pro-Pawn.
В данном уроке я объясню вам, как сравнивать производительность разных вариантов кода. Сделано это будет с помощью написанного мной профайлера.
Примеры вопросов, на которые можно найти ответ с помощью профилирования:- Работает ли функция strcat быстрее, чем format, и если да, то в каких случаях?
- Как лучше проверять подключение игрока: с помощью IsPlayerConnected или добавлением в итератор (foreach.inc) и использованием функции Itter_Contains ?
Код профайлера:
PHP код:
// 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 = 1_000;
// Названия отрывков кода.
new const code_snippets_names[2][] =
{
{"отрывок 1"},
{"отрывок 2"}
};
// Здесь вы можете объявить переменные, используемые в профилируемых отрывках кода
// и выполнить некоторые действия непосредственно перед профилированием.
#define Prerequisites();\
// <ваш код>
/*
Собственно, сами отрывки кода, которые нужно тестировать.
Если код состоит из нескольких строк, переносите их обратным слэшем.
Пример:
#define CodeSnippet1();\
DoSomething();\
DoSomethingElse();
*/
#define CodeSnippet0();\
// <ваш код>
#define CodeSnippet1();\
// <ваш код>
/*======== Конец настроек ===================================================*/
// Не рекомендую изменять следующий код, если вы в нём не разбираетесь.
#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);
}
Условия использования:
Открыть/закрыть - Вам предоставляется право на копирование данного ПО, изменение и распространение и другие виды использования при соблюдении условий данной лицензии.
- Данное ПО предоставляется "как есть", без каких-либо гарантий (как явно выраженных, так и подразумеваемых).
Автор ПО не несёт никакой ответственности за ущерб, причинённый в результате использования данного ПО и/или каких-либо других действий с ним.
- В данном ПО и во всех производных от него работах заявление об авторстве и ссылка на страницу с данной лицензией должны оставаться в неизменном виде (т.е. как в оригинале). Также, если это производная работа, она должна быть явным образом помечена как изменённая версия и не выдаваться за оригинал.
- Вы можете копировать, распространять и публиковать настройки профайлера отдельно - в этом случае указание авторства и ссылки на данную страницу не требуется.
А теперь более простым языком*:
- Не выдавайте данный код за свой.
- Строки с упоминанием об авторстве и ссылкой на эту страницу должны оставаться в коде, в неизменном виде.
- Вы можете изменять код, если соблюдаются остальные условия использования (например, можете удалить все комментарии, кроме копирайта и ссылки на эту страницу).
- Если вы публикуете изменённую версию профайлера, обязательно указывайте, что это изменённая версия, а не оригинал.
- Настройки профайлера не попадают под эти условия, можете делать с ними всё, что захотите.
*Приведённое выше упрощённое содержание не является полной лицензией.
Настройка и использование:
Для начала разберём назначение каждой настраиваемой константы:
- Количество итераций цикла профилирования.
PROFILER_ITERATIONS_MINOR - кол-во итераций перед измерением времени выполнения.
PROFILER_ITERATIONS_MAJOR - кол-во измерений времени.
Весь процесс профилирования сделан с помощью вложенных циклов. Таким образом уменьшаются погрешности от многократных вызовов GetTickCount.
Также сглаживаются погрешности из-за разной нагрузки на ЦП в разные моменты времени. Например, во время профилирования может включиться запланированная по расписанию проверка антивирусом - это намного меньше повлияет на результаты профилирования в случае с вложенными циклами.
- Названия отрывков кода.
Для этой цели предназначен массив строк code_snippets_names.
- Отрывки кода.
Для указания сравниваемых отрывков кода предназначены макросы "CodeSnippet0();", "CodeSnippet1();", ... "CodeSnippet15();".
Также для подготовительных действий (объявление и инициализация переменных, etc.) есть макрос Prerequisites.
Рассмотрим использование профайлера на примере измерения производительности функций format и strcat для конкатенации строк:
PHP код:
/*======== Настройки =========================================================*/
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 1_000;
new const code_snippets_names[2][] =
{
{"format"},
{"strcat"}
};
#define Prerequisites();\
static const str1[] = "1234567890123456789012345678901234567890";\
static const str2[] = "1234567890123456789012345678901234567890";\
new buffer[128];
#define CodeSnippet0();\
format(buffer, sizeof(buffer), "%s%s", str1, str2);
#define CodeSnippet1();\
buffer = str1, strcat(buffer, str2);
/*======== Конец настроек ====================================================*/
Автор статьи: Daniel_Cortez
Копирование данной статьи на других ресурсах без разрешения автора запрещено!