punkochel
18.11.2020, 10:51
JIT compiler
JIT compiler - это плагин, позволяющий использовать JIT-компиляцию (https://ru.wikipedia.org/wiki/JIT-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F#:~:text=JIT%2D%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F%20(%D0%B0%D0%BD%D0%B3%D0%BB.,%D0%BD%D0%B5%D0%BF%D0%BE%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%20%D0%B2%D0%BE%20%D0%B2%D1%80%D0%B5%D0%BC%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B.) в языке программирования Pawn. Он компилирует байт-код AMX в нативный код x86 перехватывая используемые сервером SA-MP функции AMX API, который в свою очередь вызывается сервером SA-MP для замены интерпретатора виртуальной машины.
Как это работает:
Код компилируется один раз и сохраняется в памяти до выгрузки соответствующего AMX. Из-за этого при загрузке / инициализации скрипта происходит небольшая задержка.
Большая часть процесса компиляции - это прямой перевод кодов операций AMX в последовательности соответствующих машинных инструкций с использованием, по сути, гигантского оператора switch. Но есть места, где JIT-компилятор пытается быть немного умнее: например, он заменяет вызовы общих функций с плавающей точкой (те, что находятся в float.inc) эквивалентными инструкциями математического сопроцессора, встроенного в ЦП, чтобы избежать накладных расходов, а именно вызовов нативных функций.
Ограничения:
Все, что не использует #emit, вероятно, будет работать нормально. В противном случае это зависит от кода. Есть несколько продвинутых хаков #emit, которые просто не работают с этой JIT. Самомодифицирующийся код является одним из примеров;
Если вы используете библиотеку YSI (https://github.com/Y-Less/YSI), то скорее всего, плагин JIT compiler не будет работать и просто приведет к сбою вашего сервера. Однако с последними версиями YSI это может быть не так;
Плагины, которые перехватывают amx_Exec(), например CrashDetect (https://github.com/Zeex/samp-plugin-crashdetect), не могут использоваться вместе с этим плагином. Если такой перехватчик обнаружен, вы получите сообщение об ошибке при запуске, и ваш код будет запускаться через интерпретатор по умолчанию, как обычно.
Тесты производительности (сравнение):
Настройки профайлера:
/*======== Настройки =========================================================*/
// Кол-во итераций в циклах.
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 100;
// Названия отрывков кода.
new const code_snippets_names[2][] =
{
{"native function"},
{"Pawn function"}
};
#include <core>
LowerCase0(string[])
{
static pos;
for (pos = -1; string[++pos] != '\0';)
string[pos] = tolower(string[pos]);
}
LowerCase1(string[])
{
static pos, c;
for (pos = 0; ; ++pos)
{
if (0 != (c = string[pos]))
{
if (c < 'A')
continue;
if ('Z' < c)
continue;
string[pos] = c + ('a' - 'A');
continue;
}
break;
}
}
#define Prerequisites();\
static string[] = "ABCabcAABBCCaabbccXYZxyzXXYYZZxxyyzz";
#define CodeSnippet0();\
LowerCase0(string);
#define CodeSnippet1();\
LowerCase1(string);
/*======== Конец настроек ====================================================*/
Результаты:
Тестирование: <native function> vs <Pawn function>
Режим: интерпретируемый, 10000x100 итераций.
native function: 8001
Pawn function: 2263
Тестирование: <native function> vs <Pawn function>
Режим: с JIT-компиляцией, 10000x100 итераций.
native function: 5072
Pawn function: 213
Установка и использование:
Скачайте плагин со страницы Releases на github (см. ниже);
Извлеките файл jit.dll (для windows), или jit.so (для linux) в папку plugins вашего сервера;
Подключите данный плагин в файле server.cfg в строке pluguns (обычно в самом начале);
Запустите сервер, и если при загрузке плагина не возникло ошибок, то ваш код работает с JIT.
*Примечание: файл jit.inc содержит вспомогательные функции, но для работы плагина, подключать его не обязательно.
Источники:
Скачать плагин: GitHub (https://github.com/Zeex/samp-plugin-jit/releases)
JIT-компиляция: Wikipedia (https://ru.wikipedia.org/wiki/JIT-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F#:~:text=JIT%2D%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F%20(%D0%B0%D0%BD%D0%B3%D0%BB.,%D0%BD%D0%B5%D0%BF%D0%BE%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%20%D0%B2%D0%BE%20%D0%B2%D1%80%D0%B5%D0%BC%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B.)
Благодарности:
Daniel_Cortez (https://pro-pawn.ru/member.php?100-Daniel_Cortez), за помощь в переводе оригинальной статьи.
Автор статьи: punkochel (https://pro-pawn.ru/member.php?9227-punkochel)
Источник тестов: Мифы о Pawn-скриптинге - #7 (https://pro-pawn.ru/showthread.php?13493-%D0%9C%D0%B8%D1%84%D1%8B-%D0%BE-Pawn-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3%D0%B5-7)
Источник материала для статьи: GitHub (https://github.com/Zeex/samp-plugin-jit)
*Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора или Daniel_Cortez (https://pro-pawn.ru/member.php?100-Daniel_Cortez) запрещено!
JIT compiler - это плагин, позволяющий использовать JIT-компиляцию (https://ru.wikipedia.org/wiki/JIT-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F#:~:text=JIT%2D%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F%20(%D0%B0%D0%BD%D0%B3%D0%BB.,%D0%BD%D0%B5%D0%BF%D0%BE%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%20%D0%B2%D0%BE%20%D0%B2%D1%80%D0%B5%D0%BC%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B.) в языке программирования Pawn. Он компилирует байт-код AMX в нативный код x86 перехватывая используемые сервером SA-MP функции AMX API, который в свою очередь вызывается сервером SA-MP для замены интерпретатора виртуальной машины.
Как это работает:
Код компилируется один раз и сохраняется в памяти до выгрузки соответствующего AMX. Из-за этого при загрузке / инициализации скрипта происходит небольшая задержка.
Большая часть процесса компиляции - это прямой перевод кодов операций AMX в последовательности соответствующих машинных инструкций с использованием, по сути, гигантского оператора switch. Но есть места, где JIT-компилятор пытается быть немного умнее: например, он заменяет вызовы общих функций с плавающей точкой (те, что находятся в float.inc) эквивалентными инструкциями математического сопроцессора, встроенного в ЦП, чтобы избежать накладных расходов, а именно вызовов нативных функций.
Ограничения:
Все, что не использует #emit, вероятно, будет работать нормально. В противном случае это зависит от кода. Есть несколько продвинутых хаков #emit, которые просто не работают с этой JIT. Самомодифицирующийся код является одним из примеров;
Если вы используете библиотеку YSI (https://github.com/Y-Less/YSI), то скорее всего, плагин JIT compiler не будет работать и просто приведет к сбою вашего сервера. Однако с последними версиями YSI это может быть не так;
Плагины, которые перехватывают amx_Exec(), например CrashDetect (https://github.com/Zeex/samp-plugin-crashdetect), не могут использоваться вместе с этим плагином. Если такой перехватчик обнаружен, вы получите сообщение об ошибке при запуске, и ваш код будет запускаться через интерпретатор по умолчанию, как обычно.
Тесты производительности (сравнение):
Настройки профайлера:
/*======== Настройки =========================================================*/
// Кол-во итераций в циклах.
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 100;
// Названия отрывков кода.
new const code_snippets_names[2][] =
{
{"native function"},
{"Pawn function"}
};
#include <core>
LowerCase0(string[])
{
static pos;
for (pos = -1; string[++pos] != '\0';)
string[pos] = tolower(string[pos]);
}
LowerCase1(string[])
{
static pos, c;
for (pos = 0; ; ++pos)
{
if (0 != (c = string[pos]))
{
if (c < 'A')
continue;
if ('Z' < c)
continue;
string[pos] = c + ('a' - 'A');
continue;
}
break;
}
}
#define Prerequisites();\
static string[] = "ABCabcAABBCCaabbccXYZxyzXXYYZZxxyyzz";
#define CodeSnippet0();\
LowerCase0(string);
#define CodeSnippet1();\
LowerCase1(string);
/*======== Конец настроек ====================================================*/
Результаты:
Тестирование: <native function> vs <Pawn function>
Режим: интерпретируемый, 10000x100 итераций.
native function: 8001
Pawn function: 2263
Тестирование: <native function> vs <Pawn function>
Режим: с JIT-компиляцией, 10000x100 итераций.
native function: 5072
Pawn function: 213
Установка и использование:
Скачайте плагин со страницы Releases на github (см. ниже);
Извлеките файл jit.dll (для windows), или jit.so (для linux) в папку plugins вашего сервера;
Подключите данный плагин в файле server.cfg в строке pluguns (обычно в самом начале);
Запустите сервер, и если при загрузке плагина не возникло ошибок, то ваш код работает с JIT.
*Примечание: файл jit.inc содержит вспомогательные функции, но для работы плагина, подключать его не обязательно.
Источники:
Скачать плагин: GitHub (https://github.com/Zeex/samp-plugin-jit/releases)
JIT-компиляция: Wikipedia (https://ru.wikipedia.org/wiki/JIT-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F#:~:text=JIT%2D%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F%20(%D0%B0%D0%BD%D0%B3%D0%BB.,%D0%BD%D0%B5%D0%BF%D0%BE%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%20%D0%B2%D0%BE%20%D0%B2%D1%80%D0%B5%D0%BC%D1%8F%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B.)
Благодарности:
Daniel_Cortez (https://pro-pawn.ru/member.php?100-Daniel_Cortez), за помощь в переводе оригинальной статьи.
Автор статьи: punkochel (https://pro-pawn.ru/member.php?9227-punkochel)
Источник тестов: Мифы о Pawn-скриптинге - #7 (https://pro-pawn.ru/showthread.php?13493-%D0%9C%D0%B8%D1%84%D1%8B-%D0%BE-Pawn-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3%D0%B5-7)
Источник материала для статьи: GitHub (https://github.com/Zeex/samp-plugin-jit)
*Исключительно для pro-pawn.ru
Копирование данной статьи на других ресурсах без разрешения автора или Daniel_Cortez (https://pro-pawn.ru/member.php?100-Daniel_Cortez) запрещено!