PDA

Просмотр полной версии : [CMD] /cc (ZCMD/DC_CMD)



Daniel_Cortez
02.09.2014, 18:16
Думаю, не нужно объяснять, что делает эта команда - она есть в 90% всех модов.
Для тех, кто думает, что обычный цикл for на 100 итераций никак не оптимизировать: вынужден вас огорчить...

В обычном виде на каждой итерации цикла функции SendClientMessageToAll передаются одни и те же параметры, поэтому с помощью #emit передача параметров вынесена за пределы цикла - в теле цикла оставлен только сам вызов функции.

И да, я смею называть это максимальной оптимизацией. Возможно, даже слишком.



CMD:cc(playerid, params[]) // by Daniel_Cortez \\ pro-pawn.ru
{
// проверка на админа (при необходимости замените на свою)
if(PlayerInfo[playerid][pAdmin] != 0)
{
// объявить строковую константу и счётчик цикла
static const str[] = "";
new i = 100;

// трюк, предотвращающий краш компилятора (баг sysreq.c)
// (баг исправлен в патчах от Zeex, в компиляторах версий 0x030A и новее
// обход бага не нужен, для чего и используется директива #if)
#if __Pawn < 0x030A
{ if(0 == i) SendClientMessageToAll(0, str); }
#endif

// передать параметры для SendClientMessageToAll
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8

// вызвать функцию 100 раз
do{
#emit sysreq.c SendClientMessageToAll
}while(--i);

// освободить стековое пространство, зарезервированное под параметры
#emit stack 12
}
return 1;
}


P.S.: Данная команда представляет собой всего лишь пример оптимизации с помощью #emit (в частности, пропуска передачи через стек одних и тех же аргументов функции при её многократном вызове). Этот приём, как и любой другой, связанный с #emit, имеет смысл только для оптимизации самых узких мест, влияющих на производительность сервера.

Автор: Daniel_Cortez (http://pro-pawn.ru/member.php?100-Daniel_Cortez)


Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Копирование данной статьи на других ресурсах без разрешения автора запрещено!

Batka1337
03.03.2016, 23:20
Блин в комментах обосрали(((

https://pp.vk.me/c630620/v630620040/2aacb/pvDkFPmpTGE.jpg

middlematt
03.03.2016, 23:36
То чувство, когда ты только что прочитал эти комментарии и увидел их на портале.

Иван Бубнов
03.03.2016, 23:37
То чувство, когда ты только что прочитал эти комментарии и увидел их на портале.

link go

DeimoS
04.03.2016, 09:53
Блин в комментах обосрали(((

https://pp.vk.me/c630620/v630620040/2aacb/pvDkFPmpTGE.jpg

Но его код ведь меньше((

Daniel_Cortez
04.03.2016, 11:42
Я просто оставлю это здесь: http://pro-pawn.ru/showthread.php?12773

$continue$
04.03.2016, 16:22
Ну или:

https://pp.vk.me/c630023/v630023369/168ed/qd9CD29TmNc.jpg

DeadScripter
05.03.2016, 22:08
Я просто оставлю это здесь: http://pro-pawn.ru/showthread.php?12773

Твоими фактами кидаться в табло "Супыр скрыптырам" буду. И док-ва есть и пояснения. Вообще класс.

TheMallard
06.03.2016, 10:13
Так говоришь, будто жалобу на дудвансах пишешь.

Geebrox
11.05.2016, 00:25
И да, я смею называть это максимальной оптимизацией. Возможно, даже слишком.


new i = 100;

to


new i[1 char];
i{0} = 100;

:crazy:

Daniel_Cortez
11.05.2016, 05:11
new i = 100;

to


new i[1 char];
i{0} = 100;

:crazy:
И в чём профит (кроме мазохизма с массивом из 1 элемента)?

Nexius_Tailer
11.05.2016, 12:32
new i = 100;

to


new i[1 char];
i{0} = 100;

:crazy:
Какой толк экономить память (да ещё и такое ничтожное количество), если процессорное время дороже?

TheMallard
11.05.2016, 14:03
Микрооптимизации вообще невыгодны по своей сути.

L0ndl3m
11.05.2016, 14:14
И в чём профит (кроме мазохизма с массивом из 1 элемента)?
Тем более обращение к ячейке массива всегда медленнее, чем обращение к обычной переменной.

Daniel_Cortez
11.05.2016, 16:54
Какой толк экономить память (да ещё и такое ничтожное количество), если процессорное время дороже?

Тем более обращение к ячейке массива всегда медленнее, чем обращение к обычной переменной.
Дело даже не в этом.

Если честно, я надеялся получить ответ от Geebrox, но раз он решил покинуть сию тему, мне придётся самому расставить точки над "ё".
Никто не заметил самого главного: никакой "экономии памяти" в том подходе с "1 char" нет. Нельзя сделать массив размером в четверть ячейки, полторы ячейки, etc. - в любом случае размер будет округлён до целой ячейки, так работает оператор char (http://pro-pawn.ru/showthread.php?13706).

Nash_Brigers
11.05.2016, 17:29
Дело даже не в этом.

Если честно, я надеялся получить ответ от Geebrox, но раз он решил покинуть сию тему, мне придётся самому расставить точки над "ё".
Никто не заметил самого главного: никакой "экономии памяти" в том подходе с "1 char" нет. Нельзя сделать массив размером в четверть ячейки, полторы ячейки, etc. - в любом случае размер будет округлён до целой ячейки, так работает оператор char (http://pro-pawn.ru/showthread.php?13706).Я хотел об этом написать, но после того как Londlem не обратил на это внимания - решил не вмешиваться))

Nexius_Tailer
11.05.2016, 20:27
Дело даже не в этом.

Если честно, я надеялся получить ответ от Geebrox, но раз он решил покинуть сию тему, мне придётся самому расставить точки над "ё".
Никто не заметил самого главного: никакой "экономии памяти" в том подходе с "1 char" нет. Нельзя сделать массив размером в четверть ячейки, полторы ячейки, etc. - в любом случае размер будет округлён до целой ячейки, так работает оператор char (http://pro-pawn.ru/showthread.php?13706).
А я проверил. Вышло вроде даже больше в итоге, но даже если бы это работало, лично я не вижу смысла от такого экономия памяти вообще, потому что чаще всего ради этого жертвуется скорость, которая важнее.

Geebrox
11.05.2016, 23:13
Разве я писал что так будет быстрее или памяти меньше уйдет?
Я не просто так поставил этот смайл: :crazy:
Мда, критики уровень "Бог"
Если я хотел бы что то доказать, я обязательно об этом написал бы...

Daniel_Cortez
12.05.2016, 06:03
Разве я писал что так будет быстрее или памяти меньше уйдет?
Я не просто так поставил этот смайл: :crazy:
Мда, критики уровень "Бог"
Если я хотел бы что то доказать, я обязательно об этом написал бы...
Всегда удивлялся таким людям: сначала напакостят в теме, а потом пытаются в чём-то обвинить других.
Я не знаю, пытались ли вы намеренно ввести других пользователей в заблуждение, что-то предложить, не зная теории, или это был просто троллинг - в любом случае начали это вы, и теперь ваша попытка выставить себя белым и пушистым выглядит цинично.

Daniel_Cortez
14.08.2016, 13:41
Исправлен краш, возникавший при использовании стандартного компилятора Pawn (до этого я использовал при тестировании только модифицированный компилятор от Zeex). Суть в том, что если использовать #emit сразу же после блока if, то созданные с помощью #emit инструкции почему-то попадают в тот блок.
Иными словами, код


if(i==0)
SendClientMessageToAll(0, str);
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8

превращается компилятором в


if(i==0)
{
SendClientMessageToAll(0, str);
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8
}

Zeex уже знает об этом баге (https://github.com/Zeex/pawn/wiki/Known-compiler-bugs), но ещё не исправил его в своей версии компилятора.

Проблема проявлялась только при использовании стандартного компилятора, т.к. только при его использовании компилировался блок if. Решена она была следующим образом: после блока if я добавил "i = 100;" - это действие не имеет никакого смысла (т.к. переменная i уже создаётся с изначальным значением 100), но позволяет отделить #emit от if.


if(i==0)
SendClientMessageToAll(0, str);
i = 100;
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8

ziggi
14.08.2016, 15:18
А это не поможет?

if(i==0) {
SendClientMessageToAll(0, str);
}

Daniel_Cortez
14.08.2016, 15:24
А это не поможет?

if(i==0) {
SendClientMessageToAll(0, str);
}
Нет, дело в неправильном адресе в инструкции перехода после проверки условия. Если сразу после тела if использовать #emit, адрес перехода всегда оказывается после #emit.

ziggi
14.08.2016, 18:29
Нет, дело в неправильном адресе в инструкции перехода после проверки условия. Если сразу после тела if использовать #emit, адрес перехода всегда оказывается после #emit.

Понял, а если так?


do {
SendClientMessageToAll(0, str);
} while (false);

Daniel_Cortez
14.08.2016, 19:30
Понял, а если так?


do {
SendClientMessageToAll(0, str);
} while (false);

Тело ветвления (или цикла в твоём случае) не должно выполняться. Нужно всего лишь зареференсить нативную функцию, прежде чем использовать её в #emit.
В принципе можно вместо if сделать отдельную функцию с атрибутом public и в ней перечислить нужные нативки. Я бы так и сделал с самого начала, но, ИМХО, для такой небольшой работы, как эта, всё будет куда лучше выглядеть в пределах одной функции.
Ещё есть такой вариант: не запихивать вызов SCMA ни в какие ветвления или циклы, а сам цикл do-while ниже сделать на 99 итераций вместо 100. Я подумаю насчёт его применения.

HarrWe
09.10.2016, 23:32
Поставил на сервер месяц назад только сейчас заметил фриз при использование команды, возможно ли как нибудь избежать этого фриза? Или только уменьшением кол. повтора цикла?

Daniel_Cortez
10.10.2016, 07:45
Поставил на сервер месяц назад только сейчас заметил фриз при использование команды, возможно ли как нибудь избежать этого фриза? Или только уменьшением кол. повтора цикла?
Смотря, что вы имеете в виду под "фризом" (разве это так сложно выражаться нормальным языком?)
Если вы про временное зависание игры, то это проблема клиента, а не моего кода. Уменьшение количества итераций может помочь, но тогда игроки всё ещё смогут увидеть часть стёртого чата, прокрутив его вверх.

#enotya
02.11.2016, 22:02
Автор, будьте добры, почистите немного свой ПМ.
Хотелось бы попросить разрешение на публикацию.
Заранее спасибо.

Nexius_Tailer
02.11.2016, 22:53
Такими темпами скоро и системы приветствия сервера такими же востребованными будут

Daniel_Cortez
03.11.2016, 08:47
Такими темпами скоро и системы приветствия сервера такими же востребованными будут
Добавил пояснение в 1-й пост.

P.S.: Данная команда представляет собой всего лишь пример оптимизации с помощью #emit (в частности, пропуска передачи через стек одних и тех же аргументов функции при её многократном вызове). Данный приём, как и любой другой, связанный с #emit, имеет смысл только для оптимизации самых узких мест, влияющих на производительность сервера.
Но всё же странно. Всегда думал, что это и без пояснений очевидно, но, похоже, что для кого-то нет.

Nexius_Tailer
03.11.2016, 16:06
Кто-то не понял сарказма, это было скорее #enotya адресовано

VVWVV
23.11.2016, 14:54
Исправлен краш, возникавший при использовании стандартного компилятора Pawn (до этого я использовал при тестировании только модифицированный компилятор от Zeex). Суть в том, что если использовать #emit сразу же после блока if, то созданные с помощью #emit инструкции почему-то попадают в тот блок.
Иными словами, код


if(i==0)
SendClientMessageToAll(0, str);
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8

превращается компилятором в


if(i==0)
{
SendClientMessageToAll(0, str);
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8
}

Zeex уже знает об этом баге (https://github.com/Zeex/pawn/wiki/Known-compiler-bugs), но ещё не исправил его в своей версии компилятора.

Проблема проявлялась только при использовании стандартного компилятора, т.к. только при его использовании компилировался блок if. Решена она была следующим образом: после блока if я добавил "i = 100;" - это действие не имеет никакого смысла (т.к. переменная i уже создаётся с изначальным значением 100), но позволяет отделить #emit от if.


if(i==0)
SendClientMessageToAll(0, str);
i = 100;
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8


Это ещё можно исправить таким образом:


if (i == 0)
{
SendClientMessageToAll(0, str);
} {}


Не будет генерироваться лишних инструкций.

Что было:


zero.pri
push.pri
push.c 8
sysreq.c 2 ; SendClientMessageToAll
stack c
l.4
const.pri 64
stor.s.pri fffffffc

push.c 4
push.c ffffffff
push.c 8


Что стало:


zero.pri
push.pri
push.c 8
sysreq.c 2 ; SendClientMessageToAll
stack c
l.4
push.c 4
push.c ffffffff
push.c 8

Daniel_Cortez
26.12.2016, 17:10
Это ещё можно исправить таким образом:


if (i == 0)
{
SendClientMessageToAll(0, str);
} {}

Или так:

{ if(0 == i) SendClientMessageToAll(0, str); }
ИМХО, более эстетичный способ, но да, суть всё та же: нужен пустой локальный блок, чтобы обойти баг с наименьшими потерями.

Обновил 1-й пост.

HarrWe
12.07.2017, 15:12
Пытался реализовать подобное для SendClientMessage, но в итоге так ничего и не вышло, и пару вопросов возникло,

// передать параметры для SendClientMessageToAll
#emit push.c str
#emit push.c 0xFFFFFFFF
#emit push.c 8

Что это за аргумент #emit push.c 8?


static const str[] = "";
new i = 100;
#if __Pawn < 0x030A
{ if(0 == i) SendClientMessage(playerid,0,str); }
#endif
#emit push.c playerid
#emit push.c 0xFFFFFFFF
#emit push.c str

do{
#emit sysreq.c SendClientMessage
}while(--i);

#emit stack 12

Daniel_Cortez
12.07.2017, 15:58
Общий размер аргументов функции в байтах: 2 * (cellbits / charbits).

HarrWe
12.07.2017, 16:05
Окей, но работать всё равно не хочет. Что не так?


static const str[] = "";
new i = 100;


#if __Pawn < 0x030A
{ if(0 == i) SendClientMessage(playerid, 0, str); }
#endif


#emit push.c playerid
#emit push.c 0xFFFFFFFF
#emit push.c str
#emit push.c 8


do{
#emit sysreq.c SendClientMessage
}while(--i);


#emit stack 12

Daniel_Cortez
12.07.2017, 16:18
Вы не знаете самых основ #emit и ждёте, что всё сделают за вас, вот что не так. Изучите теорию, потом уже пытайтесь адаптировать чужой код. На оффе есть несколько хороших уроков.