[Include] FoxForeach Fast and simple player loop
#1

This include contain function for fast loop for player.

PHP код:
/****************************************************************************************************
 *                                                                                                  *
 *                                           FoX Foreach                                            *
 *                                                                                                  *
 * Copyright © 2016 Abyss Morgan. All rights reserved.                                              *
 *                                                                                                  *
 * Download: https://github.com/AbyssMorgan/SA-MP/tre...nclude/SAM                           *
 *                                                                                                  *
 * Plugins: None                                                                                    *
 * Modules: None                                                                                    *
 *                                                                                                  *
 * File Version: 1.2.1                                                                              *
 * SA:MP Version: 0.3.7                                                                             *
 *                                                                                                  *
 * Functions:                                                                                       *
 * FoxForeach(variable,tag); //Tags: Player, Bot, Character                                         *
 *                                                                                                  *
 * Player Functions:                                                                                *
 * CountFoxPlayer();                                                                                *
 * FoxForeachRandomPlayer();                                                                        *
 * bool:IsPlayerInFoxPool(playerid);                                                                *
 *                                                                                                  *
 * Bot Functions:                                                                                   *
 * CountFoxBot();                                                                                   *
 * FoxForeachRandomBot();                                                                           *
 * bool:IsBotInFoxPool(playerid);                                                                   *
 *                                                                                                  *
 * Character Functions:                                                                             *
 * CountFoxCharacter();                                                                             *
 * FoxForeachRandomCharacter();                                                                     *
 * bool:IsCharacterInFoxPool(playerid);                                                             *
 *                                                                                                  *
 * Extended functions:                                                                              *
 * foX(variable,beginning,end);                                                                     *
 * foreach(variable);                                                                               *
 *                                                                                                  *
 * Auto functions:                                                                                  *
 * FoxForeachInit();                                                                                *
 * bool:FoxAddPlayer(playerid);                                                                     *
 * bool:FoxRemovePlayer(playerid);                                                                  *
 * bool:FoxAddBot(playerid);                                                                        *
 * bool:FoxRemoveBot(playerid);                                                                     *
 * bool:FoxAddCharacter(playerid);                                                                  *
 * bool:FoxRemoveCharacter(playerid);                                                               *
 *                                                                                                  *
 ****************************************************************************************************/ 
Differences between foreach.inc and FoxForeach.inc

foreach.inc:
PHP код:
foreach(new Player){
    
SendClientMessage(i,0xFFFFFFFF,"Hello players!");
}
foreach(new 
Bot){
    
SendClientMessage(i,0xFFFFFFFF,"Hello bot!");
}
foreach(new 
Character){
    
SendClientMessage(i,0xFFFFFFFF,"Hello all!");

FoxForeach.inc:
PHP код:
FoxForeach(i,Player){
    
SendClientMessage(i,0xFFFFFFFF,"Hello players!");
}
FoxForeach(i,Bot){
    
SendClientMessage(i,0xFFFFFFFF,"Hello bots!");
}
FoxForeach(i,Character){
    
SendClientMessage(i,0xFFFFFFFF,"Hello all!");

Speed Test for Operation:

Test #1 Foreach for 500 players, 1000 operations per player
Test #2 Foreach for 400 players, 1000 operations per player (Players id 0 - 99 left the server)

FoxForeach:
Test #1 For 500000 iteration in 489 ms
Test #2 For 400000 iteration in 393 ms

Foreach:
Test #1 For 500000 iteration in 500 ms
Test #2 Server Crash. Why ?

Speed Test Script

Download:
FoxForeach.inc

FoxForeach.inc]
Reply
#2

Quote:

Differences between foreach.inc and FoxForeach.inc

The difference presented here is totally untrue. Foreach counts in the connected one or more of registered entries. So yea, every player in a foreach loop is connected, that's what makes the difference in foreach and GetPlayerPoolSize.
Reply
#3

Quote:
Originally Posted by Gammix
Посмотреть сообщение
The difference presented here is totally untrue. Foreach counts in the connected one or more of registered entries. So yea, every player in a foreach loop is connected, that's what makes the difference in foreach and GetPlayerPoolSize.
I dont use GetPlayerPoolSize() what a problem ?

I tested using this:
https://sampforum.blast.hk/showthread.php?tid=570868
Reply
#4

every player in foreach iteration is connected players
Reply
#5

So this example is incorrect, because the test does not check the player connections.

Test:
https://github.com/AbyssMorgan/SA-MP...Foreach%20Test
Reply
#6

Your speed test is flawed.
Quote:
Originally Posted by Yashas
Посмотреть сообщение
Don't use SendClientMessage inside the command. It invalidates the speed test.

Here is the reason.

Let's say ZCMD takes 10ms to process a command in OnPlayerCommandText and iZCMD takes 5ms to do the same.

An empty command takes 1ms to execute and a command with one SendClientMessage takes 20ms.

Now when you do the speed tests, you will find ZCMD to take 30ms and iZCMD to take 25ms. Now you obviously will say they are almost the same even though iZCMD processes commands twice as fast as ZCMD.
All the foreach includes are going to have the same speed while looping since they use the same idea. The only faster foreach can be made using native calls to plugin.

Код:
native SLE_algo_foreach_list_init(list:listid, &val);
native SLE_algo_foreach_list_get(feid);

#define foreach::list(%0(%1)) for(new %1, fel_%0@%1_id = SLE_algo_foreach_list_init(%0, %1); SLE_algo_foreach_list_get(fel_%0@%1_id);)
Код:
class ForeachList
{
public:
	ForeachList(AMX * amx, cell * counter, std::list<cell>::const_iterator itr, std::list<cell>::const_iterator end) : amx(amx), counter(counter), itr(itr), end(end)
	{}
.
.
.
.
	inline cell iterate()
	{
		bool cond = itr != end;
		if (cond)
		{
			*counter = *itr++;
		}
		return cond;
	}
	AMX * amx;
	cell * counter;
	std::list<cell>::const_iterator itr;
	std::list<cell>::const_iterator end;
};

cell AMX_NATIVE_CALL SLE_algo_foreach_list_init(AMX* amx, cell* params)
{
	unsigned int id = -1;
	if (IsValidListID(static_cast<int>(params[1])))
	{		
		cell* addr = NULL;
		amx_GetAddr(amx, params[2], &addr);
		if (unused_foreach_list_ids.size())
		{
			id = unused_foreach_list_ids.back();
			unused_foreach_list_ids.pop_back();
		}
		else
		{
			id = foreach_list_loop_maxid = active_foreach_list_loops.size();
			active_foreach_list_loops.push_back(nullptr);
		}
		active_foreach_list_loops[id] = new ForeachList(amx, addr, active_lists[static_cast<int>(params[1])]->container->cbegin(), active_lists[static_cast<int>(params[1])]->container->cend());
	}
	return id;
}
cell AMX_NATIVE_CALL SLE_algo_foreach_list_get(AMX* amx, cell* params)
{
	// Assuming that people don't try to screw with these internal functions
	// As long as the foreach define makes this call, it shall always be valid.
	// TO:CHECK If iterator remains valid when nodes are removed/added to the list while the foreach is running
	int foreachid = static_cast<int>(params[1]);
	if (foreachid > static_cast<int>(foreach_list_loop_maxid) || foreachid < 0) return 0;
	return active_foreach_list_loops[foreachid]->iterate();
}
That makes calls to a plugin which returns the data. Its faster because making function calls are slightly faster than dereferencing arrays and the rest of the work of returning the correct value is done within a plugin which hardly takes any time.

This idea wont work in PAWN because you are going to de-reference an array after calling the function which is slow whereas in my case the hard work was done by the plugin and you know that plugin can do things amazingly fast.

Areas where you can look for improvements are:
  • Insertion
  • Deletion
Maybe use a sorted array? I haven't tried and I don't know if you will gain an overall significant improvement in performance.
Reply
#7

Quote:
Originally Posted by Yashas
Посмотреть сообщение
Your speed test is flawed.


All the foreach includes are going to have the same speed while looping since they use the same idea. The only faster foreach can be made using native calls to plugin.

Код:
native SLE_algo_foreach_list_init(list:listid, &val);
native SLE_algo_foreach_list_get(feid);

#define foreach::list(%0(%1)) for(new %1, fel_%0@%1_id = SLE_algo_foreach_list_init(%0, %1); SLE_algo_foreach_list_get(fel_%0@%1_id);)
Код:
class ForeachList
{
public:
	ForeachList(AMX * amx, cell * counter, std::list<cell>::const_iterator itr, std::list<cell>::const_iterator end) : amx(amx), counter(counter), itr(itr), end(end)
	{}
.
.
.
.
	inline cell iterate()
	{
		bool cond = itr != end;
		if (cond)
		{
			*counter = *itr++;
		}
		return cond;
	}
	AMX * amx;
	cell * counter;
	std::list<cell>::const_iterator itr;
	std::list<cell>::const_iterator end;
};

cell AMX_NATIVE_CALL SLE_algo_foreach_list_init(AMX* amx, cell* params)
{
	unsigned int id = -1;
	if (IsValidListID(static_cast<int>(params[1])))
	{		
		cell* addr = NULL;
		amx_GetAddr(amx, params[2], &addr);
		if (unused_foreach_list_ids.size())
		{
			id = unused_foreach_list_ids.back();
			unused_foreach_list_ids.pop_back();
		}
		else
		{
			id = foreach_list_loop_maxid = active_foreach_list_loops.size();
			active_foreach_list_loops.push_back(nullptr);
		}
		active_foreach_list_loops[id] = new ForeachList(amx, addr, active_lists[static_cast<int>(params[1])]->container->cbegin(), active_lists[static_cast<int>(params[1])]->container->cend());
	}
	return id;
}
cell AMX_NATIVE_CALL SLE_algo_foreach_list_get(AMX* amx, cell* params)
{
	// Assuming that people don't try to screw with these internal functions
	// As long as the foreach define makes this call, it shall always be valid.
	// TO:CHECK If iterator remains valid when nodes are removed/added to the list while the foreach is running
	int foreachid = static_cast<int>(params[1]);
	if (foreachid > static_cast<int>(foreach_list_loop_maxid) || foreachid < 0) return 0;
	return active_foreach_list_loops[foreachid]->iterate();
}
That makes calls to a plugin which returns the data. Its faster because making function calls are slightly faster than dereferencing arrays and the rest of the work of returning the correct value is done within a plugin which hardly takes any time.

This idea wont work in PAWN because you are going to de-reference an array after calling the function which is slow whereas in my case the hard work was done by the plugin and you know that plugin can do things amazingly fast.

Areas where you can look for improvements are:
  • Insertion
  • Deletion
Maybe use a sorted array? I haven't tried and I don't know if you will gain an overall significant improvement in performance.
The test is flawed okay, but why foreach caused a server crash, when FoxForeach work further.
In this case, my solution for someone who is not interested in adding the plugin, you'll probably better.
Reply
#8

Update v1.2.1:
- Fix Init Hook
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)