PDA

Просмотр полной версии : [Урок] Быстрый перебор игроков в сети



Tracker1
25.12.2013, 00:04
Начну с предисловия.
Каждый раз, когда я захожу на форум, особенно в разделы вопросов или уроков, я натыкаюсь на подобные циклы


for (new i = 0; i < MAX_PLAYERS; i++)

или такие


for (new i = 0; i < GetMaxPlayers(); i++)


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

Как же он работает?

Создаем один массив размером MAX_PLAYERS и переменную для подсчёта игроков, изначально равную нулю (предположим, players[MAX_PLAYERS] и num_players).
При запуске мода заполняем массив -1 и обнуляем num_players. Когда у нас подключается игрок, мы записываем его ид в массив, увеличивая num_players.
При отключении игрока, мы просто ставим ид последнего на положение ида отключившегося игрока.
В переборе мы просто вместо playerid используем players[i] и i<num_players.


Ну а теперь реализация кода.
К глобальным переменным добавляем:


new players[MAX_PLAYERS];
new num_players;

В public OnPlayerConnect:


players[num_players++] = playerid;

В public OnPlayerDisconnect:


for (new i = 0; i < num_players; i++)
{
if (players[i] == playerid)
{
players[i] = players[--num_players];
players[num_players] = -1;
break;
}
}


Вот и весь код.
Пример перебора игроков. Предположим у нас какой-нибудь ГФ-подобный мод, и нам надо отправить всем админам сообщение:


for (new i = 0; i < num_players; i++)
if(PlayerInfo[players[i]][pAdmin] > 0)
SendClientMessage(players[i], -1, "Прием");





#include <a_samp>
#include "../include/foreach.inc"

new players[MAX_PLAYERS];
new num_players;
#define tforeach(%0) for(new _i, %0=players[_i]; _i <num_players; %0=players[++_i])

#define PROFILING_ITERS 10_000_000

main()
{
new time = 0;
//
printf("Tracker1's foreach test");
for (new i = 0; i < 125; i++)
players[num_players++] = i;
printf("125 players connected");
for (new i = 50; i < 75; i++)
{
for (new j = 0; j < num_players; j++)
{
if (players[j] == i)
{
players[j]=players[--num_players];
players[num_players] = -1;
break;
}
}
}
printf("50-74 IDs Disconnected");
printf("Starting main cycle "#PROFILING_ITERS" times");
time = GetTickCount();
for (new i = 0; i < PROFILING_ITERS; i++)
tforeach(x)
{
continue;
#pragma unused x
}
printf("Tracker1's foreach test DONE. Time: %d ms.",GetTickCount() - time);
//
print("\n");
//
printf("Y_Less's foreach test");
for (new i = 0; i < 125; i++)
Iter_Add(Player, i);
printf("125 players connected");
for (new i = 50; i < 75; i++)
Iter_Remove(Player, i);
printf("50-74 IDs Disconnected");
printf("Starting main cycle "#PROFILING_ITERS" times");
time = GetTickCount();
for (new i = 0; i < PROFILING_ITERS; i++)
foreach(new x:Player)
continue;
printf("Y_Less's foreach test DONE. Time: %d ms.",GetTickCount() - time);
//
print("\n");
return 1;
}


Без JIT:


Tracker1's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Tracker1's foreach test DONE. Time: 27923 ms.


Y_Less's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Y_Less's foreach test DONE. Time: 16611 ms.


С плагином JIT данная разработка быстрее чем Foreach.


Tracker1's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Tracker1's foreach test DONE. Time: 2472 ms.


Y_Less's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Y_Less's foreach test DONE. Time: 2723 ms.



Всё.

Update
Один быдлокодер любезно написал дефайн, чтобы было больше похоже на foreach от Y_Less.


^_^ Nicholas_West: #define tforeach(%0) for(new _i, %0=players[_i]; _i <num_players; %0=players[++_i])

С ним перебор выглядит вот так:


tforeach(x)
if (PlayerInfo[x][pAdmin]>0)
SendClientMessage(x,-1,"Прием");

Salvacore
25.12.2013, 00:28
тяжеловесным foreach
эм,почему же?
Если скажешь перебегу на твой способ :)

Tracker1
25.12.2013, 00:44
эм,почему же?
Если скажешь перебегу на твой способ :)
Открой foreach.inc и посмотри сколько там кода.

Salvacore
25.12.2013, 00:56
Открой foreach.inc и посмотри сколько там кода.
Я тебя понял.
Пожалуй остановлюсь на твоём решении.

[ForD]
25.12.2013, 10:54
Этот человек никогда не перестанет меня восхищать =D

Daniel_Cortez
25.12.2013, 15:52
Открой foreach.inc и посмотри сколько там кода.

Если грамотно использовать foreach, большая часть его кода отсеется во время компиляции.

Проведём эксперимент. Создадим пустой скрипт, и сделаем в нём перебор всех игроков.

#include <a_samp>

#define FOREACH_NO_PLAYERS (true)
#include <foreach>
new Iterator:Player<MAX_PLAYERS>;

main(){}

public OnPlayerConnect(playerid){
// добавим игрока в итератор
Itter_Add(Player, playerid);
// выведем сообщение о подключении
static const fmt_str[] = "%s [%d] зашёл на сервер.";
new string[sizeof(fmt_str)-2+MAX_PLAYER_NAME-2+5];
GetPlayerName(playerid, string, sizeof(string));
format(string, sizeof(string), fmt_str, string, playerid);
foreach(new i:Player)
SendClientMessage(i, -1, string);
}
public OnPlayerDisconnect(playerid){
// удалим игрока из итератора
Itter_Remove(Player, playerid);
}
Теперь откомпилируем скрипт с параметром -l и откроем сгенерированный листинг препроцессора.
Вот самый важный фрагмент (без содержимого foreach.inc, a_samp.inc и т.п.)

new _Y_ITER_C3:Player@YSII_Cg,Player@YSII_Ag[((500))+1]={((500))*2,((500))*2-1,...};

main(){}

public OnPlayerConnect(playerid){

Itter_AddInternal(_:_Y_ITER_C0:Player@YSII_Cg,_:_Y_ITER_C0:Player@YSII_Ag, playerid,_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1);

static const fmt_str[] = "%s [%d] зашёл на сервер.";
new string[sizeof(fmt_str)-2+(24)-2+5];
GetPlayerName(playerid, string, sizeof(string));
format(string, sizeof(string), fmt_str, string, playerid);
for(new i=_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1;_:( i=_:_Y_ITER_C0:Player@YSII_Ag[ i])!=_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1;)
SendClientMessage(i, -1, string);
}
public OnPlayerDisconnect(playerid){

Itter_RemoveInternal(_:_Y_ITER_C0:Player@YSII_Cg,_:_Y_ITER_C0:Player@YSII_Ag, playerid,_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1);
}
В результате получился массив на MAX_PLAYERS+1 ячеек (Player@YSII_Ag[((500))+1]) и переменная-счётчик для того массива (Player@YSII_Cg).
Перебор игроков производится с помощью цикла for. Названия переменных, мягко говоря, странные, но так нужно для правильной работы макроса foreach, это не должно повлиять на скорость перебора.

Касаемо кода самого foreach.inc, в листинге его будет много. Но это лишь потому, что препроцессор не отсеивает ненужные функции с атрибутом stock, это делается на следующих этапах.
В .amx попадут только функции для добавления/удаления элементов из итератора:

stock
Itter_AddInternal(&count, array[], value, size)
{
if (0 <= value < size && array[value] > size)
{
new
last = size,
next = array[last];
while (next < value)
{
last = next;
next = array[last];
}
array[last] = value;
array[value] = next;
++count;
return 1;
}
return 0;
}

stock
Itter_RemoveInternal(&count, array[], value, size)
{
new
last;
return Itter_SafeRemoveInternal(count, array, value, last, size);
}

stock
Itter_SafeRemoveInternal(&count, array[], value, &last, size)
{
if (0 <= value < size && array[value] <= size)
{
last = size;
new
next = array[last];
while (next != value)
{
last = next;
next = array[last];
}
array[last] = array[value];
array[value] = size + 1;
--count;
return 1;
}
return 0;
}

Впрочем, насчёт скорости перебора, вопрос остаётся неразрешённым. Если будут тесты скорости, доказывающие, что foreach.inc перебирает игроков медленно, будет и повод не использовать его. Пока что смысла отказываться от уже готового и богатого функционалом foreach не вижу.


UPD: Буквально час назад мы с Tracker1 провели замеры скорости перебора игроков тремя способами: Vectorial, tforeach (Tracker1) и foreach (Y_Less). Код теста под спойлером:

#include <a_samp>

#include <Vectoral>

#define tforeach(%0) for(new _i, %0=Players[_i]; _i <players; %0=Players[++_i])
new Players[MAX_PLAYERS] = {-1, ...};
new players;

#define FOREACH_NO_PLAYERS (true)
#include <foreach>
new Iterator:Player<MAX_PLAYERS>;

#define PROFILING_ITERS 1_000_000

main()
{
new time = 0;
//
print("Vectoral test");
new PLAYERSVECTORAL;
PLAYERSVECTORAL = cvector();
for(new i=0; i<125; i++)
cvector_push_back(PLAYERSVECTORAL, i);
print("125 players connected");
for(new i=50,j; i<75; i++)
{
j = cvector_find(PLAYERSVECTORAL, i); // Find player
if(j != -1) // If j == -1, vector isn't containing this player
cvector_remove(PLAYERSVECTORAL, j);
}
print("50-74 IDs Disconnected");
print("Starting main cycle "#PROFILING_ITERS" times");
time = GetTickCount();
for(new i=0; i<PROFILING_ITERS; i++)
for(new j=0,k=cvector_size(PLAYERSVECTORAL), playerid=0; j<k; j++)
{
playerid = cvector_get(PLAYERSVECTORAL, j); // get playerid
#pragma unused playerid
}
printf("Vectoral test DONE. Time: %d ms.", GetTickCount()-time);
//
print("\n");
//
print("Tracker1's foreach test");
for(new i=0; i<125; i++)
Players[players++] = i;
print("125 players connected");
for(new i=50; i<75; i++)
for(new j=0; j<players; j++)
if(Players[j] == i)
{
Players[j] = Players[--players];
Players[players] = -1;
break;
}
print("50-74 IDs Disconnected");
print("Starting main cycle "#PROFILING_ITERS" times");
time = GetTickCount();
for(new i=0; i<PROFILING_ITERS; i++)
tforeach(x)
{
continue;
#pragma unused x
}
printf("Tracker1's foreach test DONE. Time: %d ms.", GetTickCount()-time);
//
print("\n");
//
print("Y_Less's foreach test");
for(new i=0; i<125; i++)
Itter_Add(Player, i);
print("125 players connected");
for(new i=50; i<75; i++)
Itter_Remove(Player, i);
print("50-74 IDs Disconnected");
print("Starting main cycle "#PROFILING_ITERS" times");
time=GetTickCount();
for(new i=0; i<PROFILING_ITERS; i++)
foreach(new x:Player)
continue;
printf("Y_Less's foreach test DONE. Time: %d ms.", GetTickCount()-time);
//
print("\n");
return 1;
}
Результаты:

[22:44:41] Vectoral test
[22:44:41] 125 players connected
[22:44:41] 50-74 IDs Disconnected
[22:44:41] Starting main cycle 100_000 times
[22:44:45] Vectoral test DONE. Time: 3372 ms.
[22:44:45]

[22:44:45] Tracker1's foreach test
[22:44:45] 125 players connected
[22:44:45] 50-74 IDs Disconnected
[22:44:45] Starting main cycle 100_000 times
[22:44:46] Tracker1's foreach test DONE. Time: 997 ms.
[22:44:46]

[22:44:46] Y_Less's foreach test
[22:44:46] 125 players connected
[22:44:46] 50-74 IDs Disconnected
[22:44:46] Starting main cycle 100_000 times
[22:44:46] Y_Less's foreach test DONE. Time: 545 ms.
foreach.inc by Y_Less wins.

UPD (2): Похоже, что с использованием плагина JIT метод Tracker1 вырывается вперёд.

[22:51:24] Vectoral test
[22:51:24] 125 players connected
[22:51:24] 50-74 IDs Disconnected
[22:51:24] Starting main cycle 1_000_000 times
[22:51:41] Vectoral test DONE. Time: 16456 ms.
[22:51:41]

[22:51:41] Tracker1's foreach test
[22:51:41] 125 players connected
[22:51:41] 50-74 IDs Disconnected
[22:51:41] Starting main cycle 1_000_000 times
[22:51:41] Tracker1's foreach test DONE. Time: 531 ms.
[22:51:41]

[22:51:41] Y_Less's foreach test
[22:51:41] 125 players connected
[22:51:41] 50-74 IDs Disconnected
[22:51:41] Starting main cycle 1_000_000 times
[22:51:42] Y_Less's foreach test DONE. Time: 674 ms.

Edwin
26.06.2015, 13:38
А что насчёт "без использования плагина JIT"?

L0ndl3m
26.06.2015, 14:03
А что насчёт "без использования плагина JIT"?

Другие результаты.

id228fanta
04.09.2015, 22:38
-

У тебя в цикле идёт < , то есть последнего игрока не включает.

Можно сделать капельку по-другому, суть не меняется, просто многие игроки используют MAX_PLAYERS, и менять им не в кайф..

В начало мода:

#if defined MAX_PLAYERS
#undef MAX_PLAYERS
#define MAX_PLAYERS 0
#endif


В OnPlayerConnect:

new new_def = MAX_PLAYERS++;
#undef MAX_PLAYERS
#define MAX_PLAYERS new_def


В OnPlayerDisconnect:

new new_def = MAX_PLAYERS--;
#undef MAX_PLAYERS
#define MAX_PLAYERS new_def


В итоге перебор будет выглядеть так:

for(new i; i < MAX_PLAYERS; i++)
{
// кишки
}

L0ndl3m
04.09.2015, 22:41
id228fanta, вы ошибаетесь.

Последний ID игрока может быть равен MAX_PLAYERS - 1.
Максимальное количество игроков: MAX_PLAYERS.

Условие в цикле выполняется верно.

id228fanta
04.09.2015, 22:54
id228fanta, вы ошибаетесь.

Последний ID игрока может быть равен MAX_PLAYERS - 1.
Максимальное количество игроков: MAX_PLAYERS.

Условие в цикле выполняется верно.

Я про способ от автора темы говорю.

То есть проходит так:
подключаются 5 игроков (у последнего ID 4)
В переменную_для_хранения записывается цифра 4, как ID последнего игрока
делаем цикл:

for(new i; i<переменная_для_хранения; i++)
{
// кишки
}

То есть в данном цикле проверка будет проходить такая:

for(new i; i<4; i++)
И будут все ID кроме последнего.

^_^
05.09.2015, 00:14
У тебя в цикле идёт < , то есть последнего игрока не включает.
Можно сделать капельку по-другому, суть не меняется, просто многие игроки используют MAX_PLAYERS, и менять им не в кайф..

Что за вздор?


В начало мода:

#if defined MAX_PLAYERS
#undef MAX_PLAYERS
#define MAX_PLAYERS 0
#endif


В OnPlayerConnect:

new new_def = MAX_PLAYERS++;
#undef MAX_PLAYERS
#define MAX_PLAYERS new_def


В OnPlayerDisconnect:

new new_def = MAX_PLAYERS--;
#undef MAX_PLAYERS
#define MAX_PLAYERS new_def


В итоге перебор будет выглядеть так:

for(new i; i < MAX_PLAYERS; i++)
{
// кишки
}
Лол, такой бредятины я даже на П-И не видел, твой код не то что синтаксически имеет тону ошибок, он даже логически никак не складывается...

id228fanta
05.09.2015, 01:29
Блин, а и правда.. Че-т я тупанул сильно..

Daniel_Cortez
21.08.2016, 14:35
Исправил некоторые орфографические ошибки в 1-м посте, привёл стиль кода в соответствие с гайдом (http://pro-pawn.ru/showthread.php?8347).

Перемещаю тему из раздела "Добавить урок" к другим проверенным статьям.

Alexey Melnik
11.11.2017, 14:36
Предлагаю освежённую версию, по моему мнению, вылизанную донельзя.

К глобальным переменным:

#if MAX_PLAYERS > 255
new players[MAX_PLAYERS];
new index[MAX_PLAYERS];
#else
new players[MAX_PLAYERS char];
new index[MAX_PLAYERS char];
#endif

new num_players;
new buf;

В public OnPlayerConnect:

#if MAX_PLAYERS > 255
players[index[playerid] = num_players++] = playerid;
#else
players{index{playerid} = num_players++} = playerid;
#endif

В public OnPlayerDisconnect:

#if MAX_PLAYERS > 255
players[index[playerid]] = players[--num_players];
#else
players{index{playerid}} = players{--num_players};
#endif

Пример перебора игроков:

buf = num_players;
while (buf--) {
//
}

DeimoS
11.11.2017, 15:02
И в чём различие, кроме пары сэкономленных байт?
И как-то не очень логично у тебя сделан цикл. Зачем тогда массив players, если ты просто делаешь количество итераций равным значению переменной num_players?

Alexey Melnik
11.11.2017, 15:18
Не пары байт, а аж 1,5 кибибайта. Различие в использовании индекса в public OnPlayerDisconnect и более оптимального цикла для перебора.

Код в теле цикла остался прежним.

Nexius_Tailer
11.11.2017, 15:38
Аж 1.5 килобайта. (https://img-fotki.yandex.ru/get/9348/230151719.21/0_db453_2d2cae63_L)

В любом случае, вот здесь ошибка:


#if MAX_PLAYERS > 256
256 char уже хранить не могут, так что в проверке нужно поставить "> 255"

Alexey Melnik
11.11.2017, 15:40
Поправил, спасибо.

DeimoS
11.11.2017, 16:07
Все эти извращения явно стоят того (нет). Лучше ведь изобретать неудобные велосипеды, чем взять готовую библиотеку, которая уже имеет гораздо больший функционал и проверена временем

Alexey Melnik
11.11.2017, 16:12
Это раздел "Уроки", чому б и не поизобретать-то?

DeimoS
11.11.2017, 16:15
Это раздел "Уроки", чому б и не поизобретать-то?

Никто и не запрещает. Это было сказано, скорее, для новичков, которые увидят, что ты говоришь про то, что аж 1,5 кибибайта сэкономлено и побегут быстрее твою версию ставить, не понимая, что толку от этого крайне мало.

Alexey Melnik
11.11.2017, 16:23
Ну, это больше сарказм был, чем какое-то достоинство. Вообще новичкам рекомендую выпилить эти проверки, потому что тогда их во всех циклах придется ставить или макрос писать.

DeimoS
11.11.2017, 17:19
Новичкам стоит советовать foreach, который позволяет создавать свои итераторы без лишних заморочек (просто используя конкретные функции). Будет гораздо больше профита, если вы, например, сделаете отдельный итератор для авторизированных игроков и отдельный итератор для админов, а не будете пихать проверки в итератор всех игроков. Вы потратите чуть больше памяти, но выиграете в скорости.

seriu
14.11.2017, 00:21
Новичкам стоит советовать foreach, который позволяет создавать свои итераторы без лишних заморочек (просто используя конкретные функции). Будет гораздо больше профита, если вы, например, сделаете отдельный итератор для авторизированных игроков и отдельный итератор для админов, а не будете пихать проверки в итератор всех игроков. Вы потратите чуть больше памяти, но выиграете в скорости.

лол, давно так использовал, но из-за незнания что так можно сделать с инклуидом foreach использовал метод
tracker1, потом узнал об этом способе ( когда уже начал что-то понимать )

получалось, что-то типо этого:

const max_online_admin = 50+1; // всегда +1 ячейка

new bust_admin[max_online_admin] = {-1,...};
new bust_admins;
#define aforeach(%0) for(new _i, %0=bust_admin[_i]; _i <bust_admins; %0=bust_admin[++_i])

stock Invites_Admin(playerid)
{
if(Admin_Level{playerid} == 0) return 1;
if(bust_admins > max_online_admin-1) return 1;
bust_admin[bust_admins++] = playerid;
for(new i;i<sizeof(CheatShow);i++){
TextDrawShowForPlayer(playerid, CheatShow[i]);
}
if(Admin_Level{playerid} < 7)
{
static login_adm[] = "<Alogin> %s[%d] авторизировался как модератор %i уровня";
new str[sizeof(login_adm)+(-2+MAX_PLAYER_NAME)+2];
format(str,sizeof(str),login_adm,PI[playerid][pName],playerid,Admin_Level{playerid});
SendAdmin(0xFFCC00FF,str);
}
else SendClientMessage(playerid,0xFFCC00FF,!"Вы успешно авторизовались как спец. администратор");
return 1;
}
stock Leave_Admin(playerid,reas = 0)
{
if(Admin_Level{playerid} == 0) return 1;
if(Admin_Level{playerid} < 7)
{
new str_leave_adm[10+MAX_PLAYER_NAME char];
strcat(str_leave_adm,!"<Alogin> ");
strcat(str_leave_adm,PI[playerid][pName]);
if(reas == 1) strcat(str_leave_adm,!" disconnect");
else strcat(str_leave_adm,!" unlogged");
SendAdmin(0xFFCC00FF,str_leave_adm);
}
else SendClientMessage(playerid,0xFFCC00FF,!"Вы вышли из под администрирования");
Admin_Level{playerid} = 0;
for(new i;i<sizeof(CheatShow);i++){
TextDrawHideForPlayer(playerid, CheatShow[i]);
}
for(new i=0;i<bust_admins;i++)
{
if(bust_admin[i] == playerid)
{
bust_admin[i] = bust_admin[--bust_admins];
bust_admin[bust_admins]=-1;
break;
}
}
return 1;
}

DeimoS
14.11.2017, 00:48
Ну вот а foreach хранит в себе всё то же самое, только в удобной обёртке, из-за чего и теряет пару лишних миллисекунд. Но если из-за них отказываться от foreach, то можете смело вообще от циклов отказываться, расписывая все итерации вручную, дабы сэкономить то время, что тратится на работу цикла (а это инициализация переменной + условие + действия с ней).
В Китае вас очень будут чтить за это (http://lurkmore.to/%D0%98%D0%BD%D0%B4%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9_%D0%BA%D0%BE%D0%B4#K.D0.B8.D1.82.D0.B0.D0.B9.D1.81.D0.BA.D0.B8.D0.B9_.D0.BA.D0.BE.D0.B4)

Daniel_Cortez
14.11.2017, 16:05
Обновил код и результаты теста скорости в уроке, удалил из теста плагин Vectorial (почему-то не получилось найти его на оффе; впрочем, он всё равно заранее был в безвыигрышном положении из-за оверхеда на вызов нативных функций).



Ну вот а foreach хранит в себе всё то же самое, только в удобной обёртке, из-за чего и теряет пару лишних миллисекунд.
На самом деле там нет и одной тысячной миллисекунды разницы.



Tracker1's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Tracker1's foreach test DONE. Time: 2472 ms.


Y_Less's foreach test
125 players connected
50-74 IDs Disconnected
Starting main cycle 10_000_000 times
Y_Less's foreach test DONE. Time: 2723 ms.

(2723 - 2472) / 10000000 = 0,0000251 (мс)
И это только с JIT. Без JIT поделие Tracker1 работает почти в 2 раза медленнее foreach (см. 1-й пост).

DeimoS
14.11.2017, 18:02
Обновил код и результаты теста скорости в уроке, удалил из теста плагин Vectorial (почему-то не получилось найти его на оффе; впрочем, он всё равно заранее был в безвыигрышном положении из-за оверхеда на вызов нативных функций).



На самом деле там нет и одной тысячной миллисекунды разницы.

(2723 - 2472) / 10000000 = 0,0000251 (мс)
И это только с JIT. Без JIT поделие Tracker1 работает почти в 2 раза медленнее foreach (см. 1-й пост).

Я говорил лишь для примера. Такие тесты уже проводили. Просто не помню где.
Да и не важно, была бы там разница в паре миллисекунд или в секунде, ибо инклуд даёт удобную оболочку, которая позволяет делать код проще и компактнее, что уменьшает риск допущения ошибки. Глупо пытаться выиграть в скорости там, где это не требуется. Особенно в ущерб простоты кода.

Вообще я бы советовал всем "аптимизаторам-3000" прочесть два этих поста с офф форума
Оригинал (http://forum.sa-mp.com/showpost.php?p=799977&postcount=339)



Дефайны влияют на вес?
Инклюды и дефайны - это всего лишь препроцессор языка. Они меняют исходный код до того, как он попадет компилятору. У компилятора павн есть ключик, если с которым скомпилировать файл, то получим результат работы препроцессора - будет просто один файл, в который вставлены все инклюды и все дефайны заменены на свои значения.

А вообще есть правило программирования - не пессимизировать, то есть не пытаться оптимизировать то, что оптимизировать не нужно и выигрыш от оптимизации чего будет минимален (а какой будет выигрыш на начальном этапе разработки, как правило, сказать нельзя).

На хостинге есть ресурсы - процессор и память. Можно оптимизовать как по одному параметру, так и по другому. Но оптимизировать сразу оба как правило очень сложно (если возможно). Вот ты смущаешься размеру amx, а тебе это мешает? Это проблема? Стоит вообще смотреть в эту сторону?

Тут и в англоязычном разделе многие предлагают использовать битовые поля и прочие методики сокращения использования памяти. Да, безусловно, памяти от этого используется меньше. А на сколько возрастает использование процессора? Кто ни будь задавался таким вопросом и пытался замерить? Например, архитектура х86 очень старая и у процессора есть оп-коды, которые позволяют прочитать из памяти любой байт (по любому адресу). В более новых архитектурах, например в Itanium, такой возможности нет и из памяти можно прочитать только машинное слово (по скольку архитектура 64 разрядная, то 8 байт) и только по адресу кратному величине этого машинного слова. С одной стороны непонятно, за чем такое ограничение, но если приглядеться, то станет видно, что даже x86 процессоры, если у них просят прочитать байт из памяти по адресу, не кратному машинному слову (4ым или 8и байтам), то делает это намного медленней чем если адрес кратен. И по этой причине современные компиляторы во время оптимизации стараются выравнивать элементы структур по величине машинного слова. По этой причине в С++ sizeof(bool) == 4. Мы тратим на хранение 1 бита информации 4 байта, но мы с этим битом работаем быстро.

Все кричат, что нужно использовать строки по 128 слов, вместо 256, но в англоязычном разделе показали, что скорость возрастает не более чем на 5%. Стоит ли такое оптимизировать, учитывая что во многих случаях это усложнит код?

К чему я это все написал. Прежде чем что либо оптимизировать хорошо бы подумать, для чего это делается и что вы будете ухудшать взамен. Если вы оптимизируете использование памяти, то очень часто будет ухудшаться скорость работы, также как и на оборот. И в обоих случаях будет усложняться код - нужно будет его больше тестировать и больше времени потратить на нахождение ошибок (ведь известно, у одного программиста, количество ошибок в основном пропорционально объему кода, грубо говоря, количество строк).


Оригинал (http://forum.sa-mp.com/showpost.php?p=840085&postcount=365)


Но преждевременная оптимизация, как правило, зло еще большее. А причина очень проста: заранее, когда еще ничего нет, нельзя сказать, что будет слабым местом.
Многие скажут ну и что - я все буду оптимизировать. Вот обычная ситуация:

Есть задача, на реализацию которой уйдет день. Но мы не думаю начинаем все оптимизировать - в итоге написание заняло 5 дней (оптимизация = более сложный код = больше вероятность ошибок = больше времени на тестирование, не говоря уже о поддержке в будущем). В результате оптимизации было выиграно 5 процентов процессорного времени и 10 процентов использования памяти. Почему так? Потому что на момент оптимизации не было ясно что будет узким местом и оптимизация чего может дать заметный выигрыш.

Но безусловно, не стоит пессимизировать код - делать его заранее не оптимальным, без явной на то необходимости. Яркий пример пессимизации - это использование циклов от 0 до максимального количества игроков, на каждой итерации которого вызывается ИзПлаерКоннектед, вместо того чтобы использовать foreach (http://forum.sa-mp.com/showthread.php?t=92679)

Часть информации из них уже потеряла свою актуальность, ибо это пережитки 2010-ых, но общая мысль в них, как по мне, очень правильная. В своё время они "вправили" мне голову и я перестал тратить время на то, чтоб днями придумывать как выдачить из кода пару лишних миллисекунд.

Daniel_Cortez
14.11.2017, 19:59
Я говорил лишь для примера.
Даже если и так, это выглядело как либо дезинформация, либо неосведомлённость. Пара миллисекунд в некоторых ситуациях всё же могут иметь значение, чего уж точно не скажешь о 25 наносекундах.

И на самом деле я даже не знаю, может быть стоит удалить эту статью? В ней едва объясняется принцип работы итераторов (а это кое-что посложнее просто добавления/удаления элементов из обычного массива), но при этом у новичков от неё может остаться ложное представление об оптимизации кода. В конце концов, неоптимизированный код - это плохо, но ещё больший грех - чрезмерная оптимизация в ущерб читаемости кода.

DeimoS
14.11.2017, 20:58
Даже если и так, это выглядело как либо дезинформация, либо неосведомлённость. Пара миллисекунд в некоторых ситуациях всё же могут иметь значение, чего уж точно не скажешь о 25 наносекундах.

Эмм, обычно в таких ситуациях следует отказываться от реализаций при помощи циклов в угоду тех же индивидуальных таймеров.

Daniel_Cortez
15.11.2017, 13:24
Эмм, обычно в таких ситуациях следует отказываться от реализаций при помощи циклов в угоду тех же индивидуальных таймеров.
Вообще-то я про все ситуации в целом, где может пригодиться пара миллисекунд, не только про глобальные/индивидуальные таймеры. Твоё "эмм" здесь не очень уместно.

В любом случае, вопрос об удалении темы всё ещё в силе.

DeimoS
21.11.2017, 04:57
Вообще-то я про все ситуации в целом, где может пригодиться пара миллисекунд, не только про глобальные/индивидуальные таймеры.

Ну в SA-MP я таких примеров что-то представить не могу. Все автовызываемые функции, которые могут вызываться довольно часто, либо уже имеют ID игрока в качестве параметра (OnPlayerUpdate, напр), либо там циклы на всех игроков ну совсем неуместны (OnVehicleDeath, напр), ибо можно реализовать гораздо более оптимизированнее (пожертвовав памятью, как вариант), при этом, не потеряв точности.
А в случае с какими-то разовыми вызовами кода с циклом эта пара миллисекунд погоды обычно не делает (точнее, компактность и простота кода всё равно дадут больше преимуществ). Гораздо больше проку там обычно от переработки всего алгоритма, если действительно есть проблемы.


Твоё "эмм" здесь не очень уместно.

Это просто уже дурная привычка :) Не бери на своё счёт.


В любом случае, вопрос об удалении темы всё ещё в силе.

Ну лично я за. Либо стоит пометить, что тема не совсем актуальна, указав ссылку на foreach (как ты сделал со своим итератором для авто)

narwn
30.11.2017, 17:50
вот тут, new players[500]; можно ли использовать вместе 500 - MAX_PLAYERS?

Daniel_Cortez
30.11.2017, 19:07
вот тут, new players[500]; можно ли использовать вместе 500 - MAX_PLAYERS?
Не можно, а нужно. Видимо, я исправил эту оплошность в тесте производительности, когда обновлял его, а в самом начале темы упустил. Сейчас уже поправил и там.