![Цитата](images/styles/AnimatedArena/misc/quote_icon.png)
Сообщение от
Tracker1
Открой foreach.inc и посмотри сколько там кода.
Если грамотно использовать foreach, большая часть его кода отсеется во время компиляции.
Проведём эксперимент. Создадим пустой скрипт, и сделаем в нём перебор всех игроков.
PHP код:
#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 и т.п.)
PHP код:
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 попадут только функции для добавления/удаления элементов из итератора:
PHP код:
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). Код теста под спойлером:
Код
PHP код:
#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.