Pawn. Оптимизация циклов игроков

1 минут

Замутил тестик всех способов создания циклов MAX_PLAYERS с использованием OnPlayerConnect и OnPlayerDisconnect (ведь тики на них тоже тратятся).

Тест №1🔗︎

В тесте ID'ы подключающихся различны на 10, поэтому способы, которые запоминают все имеющиеся ID'ы будут лидировать.

Код тут:

Спойлер
#include <..\compiler\includes\a_samp>
#include "foreach"

main(){}

#undef MAX_PLAYERS
#define MAX_PLAYERS 200

#define PLAYER_CONNECTING 10
#define PLAYER_DISCONNECTING 10

//
#define foreach_step(%0) for(new p_i,%0 = MAX_ONLINE_PLAYERS[p_i]; (%0 = MAX_ONLINE_PLAYERS[p_i]) != INVALID_PLAYER_ID; p_i++)

new MAX_ONLINE_PLAYERS[MAX_PLAYERS] = {INVALID_PLAYER_ID, ...};


//
#define GetPlayerLastID()	players_lastID

new players_lastID = 0;

forward plcount_OnPlayerConnect(playerid);
public plcount_OnPlayerConnect(playerid)
{
  if(players_lastID < playerid) players_lastID = playerid;
  return 1;
}

forward plcount_OnPlayerDisconnect(playerid,reason);
public plcount_OnPlayerDisconnect(playerid,reason)
{
  #pragma unused reason
  if(players_lastID == playerid && playerid != 0)
  {
    do players_lastID--;
    while((IsPlayerNPC(playerid) || (!IsPlayerConnected(players_lastID))) && (players_lastID > 0));
  }
  return 1;
}
//

// PLIDs
new PLIDs[MAX_PLAYERS] = {-1,...};
new MaxPlayers = 0;

forward plids_OnPlayerConnect(playerid);
public plids_OnPlayerConnect(playerid)
{
  PLIDs[MaxPlayers] = playerid;
  MaxPlayers++;
  return 1;
}

forward plids_OnPlayerDisconnect(playerid,reason);
public plids_OnPlayerDisconnect(playerid,reason)
{
  for(new i=0;i<MaxPlayers;i++)
  {
    if(PLIDs[i] == playerid)
    {
      MaxPlayers--;
      PLIDs[i] = PLIDs[MaxPlayers];
      PLIDs[MaxPlayers] = -1;
      break;
    }
  }
  return 1;
}
//
public OnGameModeInit()
{
  // 200 слотов


  // IsPlayerConnected
  new count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      _OnPlayerConnect(i*10);
    for( new i; i < 200; i ++ )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
    for(new i=0;i<PLAYER_CONNECTING;i++)
      _OnPlayerDisconnect(i*10,0);
  }
  printf("IsPlayerConnected: %d", GetTickCount() - count);


  // GetMaxPlayers
  count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      _OnPlayerConnect(i*10);
    for( new i=GetMaxPlayers()-1 ; i>=0 ; --i )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
    for(new i=0;i<PLAYER_CONNECTING;i++)
      _OnPlayerDisconnect(i*10,0);
  }
  printf("GetMaxPlayers: %d", GetTickCount() - count);


  // GetPlayerLastID
  count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      plcount_OnPlayerConnect(i*10);
    for( new i=0 ; i <= GetPlayerLastID() ; i++ )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
    for(new i=0;i<PLAYER_CONNECTING;i++)
      plcount_OnPlayerDisconnect(i*10,0);
  }
  printf("GetPlayerLastID: %d", GetTickCount() - count);


  // PLIDs
  count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      plids_OnPlayerConnect(i*10);
    for( new i=0 ; i < MaxPlayers ; i++ )
    {
      if(!IsPlayerConnected(PLIDs[i]) || IsPlayerNPC(PLIDs[i])) continue;
    }
    for(new i=0;i<PLAYER_DISCONNECTING;i++)
      plids_OnPlayerDisconnect(i*10,0);
  }
  printf("PLID: %d", GetTickCount() - count);



  // foreach
  count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      Iter_Add(Player,i*10);

    foreach(Player, i)
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }

    for(new i=0;i<PLAYER_DISCONNECTING;i++)
      Iter_Remove(Player,i*10);
  }
  printf("foreach: %d", GetTickCount() - count);


  // foreach by Stepashka
  count = GetTickCount();
  for(new j=0;j<10000;j++)
  {
    for(new i=0;i<PLAYER_CONNECTING;i++)
      step_OnPlayerConnect(i*10);
    foreach_step(i)
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
    for(new i=0;i<PLAYER_DISCONNECTING;i++)
      step_OnPlayerDisconnect(i*10,0);
  }
  printf("foreach_step: %d", GetTickCount() - count);
  return 1;
}

forward _OnPlayerConnect(playerid);
public _OnPlayerConnect(playerid)
{
  return 1;
}

forward _OnPlayerDisconnect(playerid,reason);
public _OnPlayerDisconnect(playerid,reason)
{
  return 1;
}

forward step_OnPlayerConnect(playerid);
public step_OnPlayerConnect(playerid)
{
  for ( new i; i < MAX_PLAYERS; i++)
  {
    if ( INVALID_PLAYER_ID == MAX_ONLINE_PLAYERS[i])
    {
      MAX_ONLINE_PLAYERS[i] = playerid;
      break;
    }
  }
    //...
  return 1;
}

forward step_OnPlayerDisconnect(playerid, reason);
public step_OnPlayerDisconnect(playerid, reason)
{
    for ( new i, bool:foundPlayer = false; i < MAX_PLAYERS; i++)
    {
        if ( playerid == MAX_ONLINE_PLAYERS[i] || true == foundPlayer)
        {
            if ( i+1 < MAX_PLAYERS)
            {
                if ( (MAX_ONLINE_PLAYERS[i] = MAX_ONLINE_PLAYERS[i+1]) == INVALID_PLAYER_ID)
                {
                    break;
                }
                foundPlayer = true;
            }
            else
            {
                MAX_ONLINE_PLAYERS[i] = INVALID_PLAYER_ID;
            }
        }
    }
    //...
  return 1;
}

Результаты:

  • IsPlayerConnected: 509
  • GetMaxPlayers: 503
  • GetPlayerLastID: 591 (мною модифицированный способ от Alex009)
  • PLID: 187 (предложенный вариант)
  • foreach: 246 (foreach от Y_Less'a)
  • foreach_step: 647 (foreach который опубликовал степашка)

Тест №2🔗︎

Дальше я решил количество подключений и отключений привести к более реальному количеству.

Код тут:

Спойлер
#include <..\compiler\includes\a_samp>
#include "foreach"

main(){}

#undef MAX_PLAYERS
#define MAX_PLAYERS 200

#define PLAYER_CONNECTING 50
#define PLAYER_DISCONNECTING 50

//
#define foreach_step(%0) for(new p_i,%0 = MAX_ONLINE_PLAYERS[p_i]; (%0 = MAX_ONLINE_PLAYERS[p_i]) != INVALID_PLAYER_ID; p_i++)

new MAX_ONLINE_PLAYERS[MAX_PLAYERS] = {INVALID_PLAYER_ID, ...};


//
    new
        list_player_IDs[ MAX_PLAYERS ] = { -1, ... },
        maxPlayers = 0;

    #define forscan(%1) for( new __idx = 0, %1 = 0; __idx < maxPlayers; %1 = list_player_IDs[ ++__idx ] )

    stock forscan_connect( playerid )
    {
        list_player_IDs[ maxPlayers ] = playerid;
        maxPlayers++;
    }

    stock forscan_disconnect( playerid, reason )
    {
        for ( new i = 0; i < maxPlayers; i++ )
        {
            if ( list_player_IDs[ i ] == playerid )
            {
                maxPlayers--;
                list_player_IDs[ i ] = list_player_IDs[ maxPlayers ];
                list_player_IDs[ maxPlayers ] = -1;
                break;
            }
        }
    }

//
#define GetPlayerLastID()	players_lastID

new players_lastID = -1;

forward plcount_OnPlayerConnect(playerid);
public plcount_OnPlayerConnect(playerid)
{
  if(players_lastID < playerid) players_lastID = playerid;
  return 1;
}

forward plcount_OnPlayerDisconnect(playerid,reason);
public plcount_OnPlayerDisconnect(playerid,reason)
{
  #pragma unused reason
  if(players_lastID == playerid && playerid != 0)
  {
    do players_lastID--;
    while((IsPlayerNPC(playerid) || (!IsPlayerConnected(players_lastID))) && (players_lastID > 0));
  }
  return 1;
}
//

// PLIDs
new PLIDs[MAX_PLAYERS] = {-1,...};
new MaxPlayers = 0;

forward plids_OnPlayerConnect(playerid);
public plids_OnPlayerConnect(playerid)
{
  PLIDs[MaxPlayers] = playerid;
  MaxPlayers++;
  return 1;
}

forward plids_OnPlayerDisconnect(playerid,reason);
public plids_OnPlayerDisconnect(playerid,reason)
{
  for(new i=0;i<MaxPlayers;i++)
  {
    if(PLIDs[i] == playerid)
    {
      MaxPlayers--;
      PLIDs[i] = PLIDs[MaxPlayers];
      PLIDs[MaxPlayers] = -1;
      break;
    }
  }
  return 1;
}
//
public OnGameModeInit()
{
  // 200 слотов


  // IsPlayerConnected
  new count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    _OnPlayerConnect(i*10);
  for(new j=0;j<10000;j++)
  {
    for( new i; i < 200; i ++ )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_CONNECTING;i++)
    _OnPlayerDisconnect(i*10,0);
  printf("IsPlayerConnected: %d", GetTickCount() - count);


  // GetMaxPlayers
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    _OnPlayerConnect(i*10);
  for(new j=0;j<10000;j++)
  {
    for( new i=GetMaxPlayers()-1 ; i>=0 ; --i )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_CONNECTING;i++)
    _OnPlayerDisconnect(i*10,0);
  printf("GetMaxPlayers: %d", GetTickCount() - count);


  // GetPlayerLastID
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    plcount_OnPlayerConnect(i*10);
  for(new j=0;j<10000;j++)
  {
    for( new i=0 ; i <= GetPlayerLastID() ; i++ )
    {
      if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_CONNECTING;i++)
    plcount_OnPlayerDisconnect(i*10,0);
  printf("GetPlayerLastID: %d", GetTickCount() - count);


  // PLIDs
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    plids_OnPlayerConnect(i*10);
  for(new j=0;j<10000;j++)
  {
    for( new i=0,playerid ; i < MaxPlayers ; i++ )
    {
      playerid = PLIDs[i];
    }
  }
  for(new i=0;i<PLAYER_DISCONNECTING;i++)
    plids_OnPlayerDisconnect(i*10,0);
  printf("PLID: %d", GetTickCount() - count);



  // foreach
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    Iter_Add(Player,i*10);
  for(new j=0;j<10000;j++)
  {
    foreach(Player, i)
    {
      //if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_DISCONNECTING;i++)
    Iter_Remove(Player,i*10);
  printf("foreach: %d", GetTickCount() - count);


  // foreach by Stepashka
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    step_OnPlayerConnect(i*10);
  for(new j=0;j<10000;j++)
  {
    foreach_step(i)
    {
      //if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_DISCONNECTING;i++)
    step_OnPlayerDisconnect(i*10,0);
  printf("foreach_step: %d", GetTickCount() - count);



  // forscan
  count = GetTickCount();
  for(new i=0;i<PLAYER_CONNECTING;i++)
    forscan_connect( i*10 );
  for(new j=0;j<10000;j++)
  {
    forscan(i)
    {
      //if(!IsPlayerConnected(i) || IsPlayerNPC(i)) continue;
    }
  }
  for(new i=0;i<PLAYER_DISCONNECTING;i++)
    forscan_disconnect( i*10, 0 );
  printf("forscan: %d", GetTickCount() - count);

  return 1;
}

forward _OnPlayerConnect(playerid);
public _OnPlayerConnect(playerid)
{
  return 1;
}

forward _OnPlayerDisconnect(playerid,reason);
public _OnPlayerDisconnect(playerid,reason)
{
  return 1;
}

forward step_OnPlayerConnect(playerid);
public step_OnPlayerConnect(playerid)
{
  for ( new i; i < MAX_PLAYERS; i++)
  {
    if ( INVALID_PLAYER_ID == MAX_ONLINE_PLAYERS[i])
    {
      MAX_ONLINE_PLAYERS[i] = playerid;
      break;
    }
  }
    //...
  return 1;
}

forward step_OnPlayerDisconnect(playerid, reason);
public step_OnPlayerDisconnect(playerid, reason)
{
    for ( new i, bool:foundPlayer = false; i < MAX_PLAYERS; i++)
    {
        if ( playerid == MAX_ONLINE_PLAYERS[i] || true == foundPlayer)
        {
            if ( i+1 < MAX_PLAYERS)
            {
                if ( (MAX_ONLINE_PLAYERS[i] = MAX_ONLINE_PLAYERS[i+1]) == INVALID_PLAYER_ID)
                {
                    break;
                }
                foundPlayer = true;
            }
            else
            {
                MAX_ONLINE_PLAYERS[i] = INVALID_PLAYER_ID;
            }
        }
    }
    //...
  return 1;
}

Результаты:

  • IsPlayerConnected: 888
  • GetMaxPlayers: 304
  • GetPlayerLastID: 2169 (мною модифицированный способ от Alex009)
  • PLID: 145 (предложенный вариант)
  • foreach: 123 (foreach от Y_Less'a)
  • foreach_step: 134 (foreach который опубликовал степашка)
  • forscan: 168 (forscan от Jester(более удобный PLID))

Тут результаты куда более приятнее. Забавно, что способ с GetPlayerLastID стал работать в несколько раз медленнее.

Собственно я делаю выбор за foreach Y_Less'а - быстро, удобно, функционально.