Callback
Компетентный
Доброго времени суток, уважаемые посетители портала. Сегодня речь пойдёт об оптимизации.
Предисловие:
PHP:
Все что вы будете делать - делаете на свой страх и риск. Я ничего Вам не навязываю.
- Массивы медленнее обычных переменных
- Не используйте CallLocalFunction и funcidx когда вы уверены что функция существует
- Natives быстрее кастомных функций
- Условия в циклах
- Присваивание одинаковых значений нескольким переменным
- Объявление локальных переменных
- Ускорение, за счет математических операций
- memcpy, strfind и так далее для работы с массивами
- switch быстрее чем if else
- Стоит ли использовать CallRemoteFunction
- Доступ к элементам массива несколько раз
- Не смешивайте float и integer значения
1. Массивы медленнее обычных переменных
Давайте на секундочку представим, что у нас есть следующий код:
PHP:
new Float:pos[3];
GetPlayerPos(playerid, pos[0], pos[1], pos[2]);
GetPlayerPos(playerid, pos[0] + 5, pos[1] + 5, pos[2] + 5);
На ASM это будет выглядеть вот так:
PHP:
zero.pri
addr.alt fffffff4
fill c ; // Эти 3 инструкции отвечают за обнуление массива
break ; 38
addr.pri fffffff4 ; // Узнаем адрес массива
add.c 8 ; // Добавляем индекс (2*4 байт)
load.i ; // Это позволит получить значение, хранящееся по этому адресу
push.pri ;
addr.pri fffffff4 ; // То же что и выше
add.c 4
load.i
push.pri
addr.pri fffffff4 ; // То же что и выше
load.i
push.pri
push.c 10 // Говорит нам сколько аргументов было получено
call myfunc
PHP:
new х, у, z;
myfunc(x, у, z);
PHP:
push.s fffffff4 // расположение z в стэке
push.s fffffff8 // расположение y в стэке
push.s fffffffc // расположение x в стэке
push.c 10 // 12 байт ( 10 в hex - 12 )
call myfunc
Когда вы хотите получить доступ к элементу массива, компилятор использует следующий алгоритм:
ArrayBase + Индекс = место, где хранится массив[Индекс], а затем вы получите данные, хранящиеся в этом месте.
Поэтому вы должны уже сейчас понимать, почему массивы работают медленнее, чем обычные переменные.
Это не значит, что Вы не должны использовать массивы. Вы должны вместо этого использовать массивы с умом. Не делайте массивы без причины,
когда то же самое можно просто сделать, используя обычные переменные.
На мой взгляд,используя X, Y и Z делает код более читабельным, чем с помощью массива. Так что нет ничего в поддержку массивов.
Тесты Скорости:
PHP:
Массив (10 итераций): 2444, 2448, 2473
Не Массив (10 итераций): 972, 975, 963
2. Не используйте CallLocalFunction и funcidx когда вы знаете что функция существует
Знаете ли вы, что CallLocalFunction и funcidx медленные функции? Они очень медленные, потому что они должны проверить имя функции, которое вы передаете
в качестве аргумента.
Ну, например, такой код:
PHP:
if(funcidx("OnPlayerEatBanana") == -1)
необъявлённой функции \ переменной или же напрямую проверить так:
PHP:
#if defined OnPlayerEatBanana
// Если функция найдена
// Так зачем лишний раз затрагивать Ваш процессор?
#endif
Вместо этого:
PHP:
if(CallLocalFunction("OnPlayerEatBanana", "ii", playerid, bananaid))
PHP:
#if defined OnPlayerEatBanana
if(OnPlayerEatBanana(playerid,bananaid))
#else
// Эта функция не была объявлена
#endif
PHP:
Вызов напрямую: 204, 226, 218
CallLocalFunction: 1112, 1097, 1001
Кот ещё не понял:
PHP:
myfunc(...); // быстрее
CallLocalFunction("myfunc", ...); // медленнее
поскольку будет много функций. В коде, где проводился тест скорости, была лишь только одна public функция!
3.Стандартные natives намного быстрее, чем кастомные функции
Причина, почему native намного быстрее происходит потому, что "родные" функции непосредственно выполняются вашим компьютером, а все
кастомные функции выполняются на машине где запущен сервер.
Каждую инструкцию машина должна декодировать инструкции затем получить операнды и затем выполнить инструкцию.
Расшифровка и выборка операндов требует некоторых ресурсов процессора.
Вот пример, который использует функцию strcat чтобы скопировать две строки:
PHP:
stock strcpy(dest[], src[], sz = sizeof(dest))
{
dest[0] = 0;
return strcat(dest, src, sz);
}
PHP:
strcpy ( кастомная функция ) против родной функции strcat
strcat ( нативная стандартная функция ): 697, 700, 718, 705
strcpy ( кастомная функция ): 5484, 5422, 5507, 5562
4. Условия в циклах
Я сразу приведу Вам два примера, а вы скажите какой из них по Вашему мнению быстрее:
Пример №1:
PHP:
for(new i; i <= GetPlayerPoolSize(); i++)
PHP:
for(new i, j = GetPlayerPoolSize(); i <= j; i++)
Почему?
В примере №1, GetPlayerPoolSize вызывается при каждой итерации! Тогда зачем вызывать её снова и снова?
В примере №2 мы создаем локальную переменную, которая хранит значение, возвращаемое GetPlayerPoolSize и использует его при итерации.
Поэтому вызов функции происходит только один раз, тем самым мы избегаем большей затраты ресурсов.
Также можно ещё немного улучшить выполнение цикла
PHP:
for(new i = GetPlayerPoolSize(); i != -1; i--)
PHP:
for(new i = GetPlayerPoolSize()+1; --i != -1; )
Тесты скорости:
PHP:
С оптимизацией: 1102, 1080, 1069, 1091
Без оптимизации: 2374, 2359, 2429, 2364
Если это Вас не удивило, давайте вспомним про очень медленную CallRemoteFunction, и представим что Вы тот самый счастливчик который пишет код именно так:
PHP:
for(new i = 0; i < CallRemoteFunction("GetPlayersInTeam","i",TEAM_ID);i++)
// Посчитаем сколько веков уйдет на всё это?
Сама функция memset:
Чтобы видеть скрытое содержание Зарегистрируйтесь на форуме!
[/url]Какой по Вашему код будет быстрее:
Вариант №1
PHP:
x = abc;
y = abc;
z = abc;
PHP:
x =
y =
z = abc;
x = y = z = abc;
Позволь я помогу тебе, показав как это выглядит на ASM
Вариант №1
PHP:
load.pri c ; // Загружаем abc
stor.pri 8 ; // Записываем в x
break ; 20
load.pri c ; // Загружаем abc
stor.pri 4 ; // Записываем в y
break ; 34
load.pri c ; // Загружаем abc
stor.pri 0 ; // Записываем в z
PHP:
Вариант №2
load.pri c ; // Загружаем abc
stor.pri 0 ; // Записываем в x
stor.pri 4 ; // Записываем в y
stor.pri 8 ; // Записываем в z
Позволь я помогу тебе, показав как это выглядит на ASM
Вариант №1
PHP:
load.pri c ; // Загружаем abc
stor.pri 8 ; // Записываем в x
break ; 20
load.pri c ; // Загружаем abc
stor.pri 4 ; // Записываем в y
break ; 34
load.pri c ; // Загружаем abc
stor.pri 0 ; // Записываем в z
PHP:
load.pri c ; // Загружаем abc
stor.pri 0 ; // Записываем в x
stor.pri 4 ; // Записываем в y
stor.pri 8 ; // Записываем в z
тогда как вторая версия получит АВС только один раз и устанавливает значения x,y и z.
Очевидный вывод заключается в том, что вариант 2 быстрее.
Когда у вас есть большие массивы, которые должны быть обнулены или должны принять любое другое значение, используйте функцию memset.
Тесты Скорости:
Устанавливаем каждому элементу 3-х мерного массива в 100 элементов, значение 0
С помощью функции memset: 363, 367, 372
Используя цикл for: 6662, 6642, 6687
memset в 18 раз быстрее, чем установка значений через цикл!
6. Объявление локальных переменных
Я видел моды, где все локальные переменные помещаются в верхней части функции, хотя есть некоторые которые нужно использовать лишь иногда.
Примеры должны внести ясность.
Плохой код:
PHP:
public OnPlayerDoSomething(playerid)
{
new actionid = GetPlayerAction(playerid), pee_id, peed_on_whome, amount_of_pee;
if(actionid == PLAYER_PEE)
{
}
}
PHP:
public OnPlayerDoSomething(playerid)
{
new actionid = GetPlayerAction(playerid);
if(actionid == PLAYER_PEE)
{
new pee_id,peed_on_whome,amount_of_pee;
}
}
и затем инициализирует его с нуля.
Поэтому Вы не должны не просто так создавать локальные переменные, если Вы не уверены, что собираетесь их использовать.
7.Ускорение, за счет математических операций
Я всегда держу ручку и бумагу на столе, пока я пишу программы. Я пишу на бумаге уравнения и делаю небольшие перемещения и изменения и получить более простое уравнение.
Вот классический пример, который повысит производительность:
PHP:
new Float:x, Float:y, Float:z;
GetPlayerVelocity(playerid, x, y, z);
if( floatsqrt( (x*x) + (y*y) + (z*z)) > 5) // Будет медленнее, чем
PHP:
new Float:x, Float:y, Float:z;
GetPlayerVelocity(playerid, x, y, z);
if( ((x*x) + (y*y) + (z*z)) > 25) // Чем это
Если вы помните курс математики 5-го класса, то должны знать что такое корень квадратный. Выше мы заменили медленную функцию floatsqrt на повышение значения в проверке.
Вот ещё один пример:
PHP:
for(new i, j = GetTickCount(); ; i++)
{
if( j - LastTick[i] > MAX_TIME_ALLOWED) // Медленнее чем
{
}
}
for(new i, j = GetTickCount()-MAX_TIME_ALLOWED; ; i++) // Чем это
{
if(j > LastTick[i] )
{
}
}
8.Функции memcpy, strfind и т. д. Работа с массивами
Все строки и массивы это одно и то же. Единственное отличие заключается в том, что строка имеет нулевой символ (\0) в то время как обычный массив нет.
Мы рассмотрим этот пример, скопируем содержимое одного массива в другой:
PHP:
new DefaultPlayerArray[100] = {1,2,3,4,5,6,7,8,9,10};
new PlayerArray[MAX_PLAYERS][100];
for(new i = sizeof(DefaultPlayerArray);i != -1;i--)
{
PlayerArray[playerid][i] = DefaultPlayerArray[i];
}
PHP:
memcpy(PlayerArray[playerid], DefaultPlayerArray, 0, sizeof(DefaultPlayerArray)*4, sizeof(PlayerArray));
Тесты скорости:
PHP:
Версия С Циклом:
4286ms
4309ms
4410ms
PHP:
Функция memcpy:
60ms
62ms
60ms
Аналогичным образом Вы можете использовать strfind, strmid и многие другие строковые функции.
Единственная проблема заключается в том, что при нахождении str функцией элемента '0' в массиве, функция завершается, поскольку значение 0 означает '\0', то есть: нулевой символ.
9 switch быстрее чем else if
Иногда switch поможет Вам выполнить проверку в два раза быстрее:
PHP:
new playerid = 99;
if(playerid == 64){}
else if(playerid == 63) {}
else if(playerid == 62) {}
else if(playerid == 61) {}
else if(playerid == 60) {}
else if(playerid == 59) {}
else if(playerid == 58) {}
else {}
PHP:
switch(playerid)
{
case 64: {}
case 63: {}
case 62: {}
case 61: {}
case 60: {}
case 59: {}
case 58: {}
default: {}
}
else if:
PHP:
556ms
553ms
576ms
PHP:
210ms
212ms
216ms
10. Стоит ли использовать CallRemoteFunction
Прежде всего я хотел бы сказать, что CallRemoteFunction ужасно медленная функция и её следует избегать, когда это возможно.
CallRemoteFunction обычно используется для обновления переменных, когда у вас есть другие подключенные скрипты.
11.Доступ к элементам массива несколько раз
Давайте возьмем пример, чтобы понять о чем мы говорим здесь:
Вариант №1:
PHP:
new val = value[x][y][z];
for(new i = 50; i != -1; --i) Arr[i] = val;
PHP:
for(new i = 50; i != -1; --i) Arr[i] = value[x][y][z];
Первый - быстрее. Так как в первом мы не будем давать машине делать обращение к адресу массива каждую итерацию.
Тесты Скорости:
PHP:
Вариант №1: 2280, 2330, 2350
Вариант №2: 8008, 8183, 8147
12.Не смешивайте Float и Int числа в выражениях
Может быть, этот слишком просто, но я вижу, как люди очень часто делают эту "ошибку".
Никогда не смешивайте Float и Int (даже если вы не получаете предупреждений от компилятора).
Всегда используйте те же типы данных в одном выражении!
Например:
PHP:
new Float:result = 2.0 + 1;
PHP:
new Float:result = 2.0 + float(1); // Это медленно
PHP:
new Float:result = 2.0 + 1.0; // Это быстро
PHP:
new mindist = 10;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < mindist)
PHP:
new mindist = 10;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < float(mindist)) // Это медленно
PHP:
new Float:mindist = 10.0;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < mindist) // Это быстро
Последнее редактирование:
