Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Страница 1 из 4 1 2 3 ... ПоследняяПоследняя
Показано с 1 по 10 из 34
  1. #1
    Аватар для Tracker1
    Проверенный

    Статус
    Оффлайн
    Регистрация
    30.07.2013
    Сообщений
    54
    Репутация:
    84 ±

    Быстрый перебор игроков в сети

    Начну с предисловия.
    Каждый раз, когда я захожу на форум, особенно в разделы вопросов или уроков, я натыкаюсь на подобные циклы
    PHP код:
    for (new 0MAX_PLAYERSi++) 
    или такие
    PHP код:
    for (new 0GetMaxPlayers(); i++) 
    В такие моменты у меня рождается чувство, что такты процессора проюбываются зря впустую.
    И поэтому я решил показать быстрый способ перебора игроков в сети, без лишних итераций цикла.

    Как же он работает?
     Теория. Attention: требуются знания и голова на плечах

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


    Ну а теперь реализация кода.
    К глобальным переменным добавляем:
    PHP код:
    new players[MAX_PLAYERS];
    new 
    num_players
    В public OnPlayerConnect:
    PHP код:
    players[num_players++] = playerid
    В public OnPlayerDisconnect:
    PHP код:
        for (new 0num_playersi++)
        {
            if (
    players[i] == playerid)
            {
                
    players[i] = players[--num_players];
                
    players[num_players] = -1;
                break;
            }
        } 
    Вот и весь код.
    Пример перебора игроков. Предположим у нас какой-нибудь ГФ-подобный мод, и нам надо отправить всем админам сообщение:
    PHP код:
        for (new 0num_playersi++)
            if(
    PlayerInfo[players[i]][pAdmin] > 0)
                
    SendClientMessage(players[i], -1"Прием"); 
     Тест скорости

     code
    PHP код:
    #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 
    0125i++)
            
    players[num_players++] = i;
        
    printf("125 players connected");
        for (new 
    5075i++)
        {
            for (new 
    0num_playersj++)
            {
                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 
    0PROFILING_ITERSi++)
            
    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 
    0125i++)
            
    Iter_Add(Playeri);
        
    printf("125 players connected");
        for (new 
    5075i++)
            
    Iter_Remove(Playeri);
        
    printf("50-74 IDs Disconnected");
        
    printf("Starting main cycle "#PROFILING_ITERS" times");
        
    time GetTickCount();
        for (new 
    0PROFILING_ITERSi++)
            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.
    PHP код:
     ^_Nicholas_West#define tforeach(%0) for(new _i, %0=players[_i]; _i <num_players; %0=players[++_i]) 
    С ним перебор выглядит вот так:
    PHP код:
    tforeach(x)
        if (
    PlayerInfo[x][pAdmin]>0)
            
    SendClientMessage(x,-1,"Прием"); 
    Последний раз редактировалось Daniel_Cortez; 30.11.2017 в 19:06. Причина: update (3)

  2. 7 пользователя(ей) сказали cпасибо:
    Flime (28.06.2014) KShaddix (02.05.2014) L0ndl3m (25.12.2013) Osetin (25.12.2013) Salvacore (25.12.2013) Seviel (01.02.2017) [ForD] (25.12.2013)
  3. #2
    Аватар для Salvacore
    Инжунер-погромист

    Статус
    Оффлайн
    Регистрация
    10.05.2013
    Адрес
    Аҧсуа бызшәа
    Сообщений
    2,271
    Репутация:
    418 ±
    тяжеловесным foreach
    эм,почему же?
    Если скажешь перебегу на твой способ :)
    Последний раз редактировалось Salvacore; 25.12.2013 в 00:39.

  4. #3
    Аватар для Tracker1
    Проверенный

    Статус
    Оффлайн
    Регистрация
    30.07.2013
    Сообщений
    54
    Репутация:
    84 ±
    Цитата Сообщение от #Carleone Посмотреть сообщение
    эм,почему же?
    Если скажешь перебегу на твой способ :)
    Открой foreach.inc и посмотри сколько там кода.

  5. Пользователь сказал cпасибо:
    Salvacore (25.12.2013)
  6. #4
    Аватар для Salvacore
    Инжунер-погромист

    Статус
    Оффлайн
    Регистрация
    10.05.2013
    Адрес
    Аҧсуа бызшәа
    Сообщений
    2,271
    Репутация:
    418 ±
    Цитата Сообщение от Tracker1 Посмотреть сообщение
    Открой foreach.inc и посмотри сколько там кода.
    Я тебя понял.
    Пожалуй остановлюсь на твоём решении.

  7. #5
    Аватар для [ForD]
    Пользователь

    Статус
    Оффлайн
    Регистрация
    14.11.2013
    Адрес
    Свердловская обл.
    Сообщений
    688
    Репутация:
    103 ±
    Этот человек никогда не перестанет меня восхищать =D
    PAWN Compiler делает то, что вы приказали ему сделать, а не то, что вы хотели, чтобы он сделал..

    25% времени в программировании уходит на размышления о том, что пользователь может сделать не так.[HR]/Брайан Хьюмс/





    #FIXSAMP

  8. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от 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(Playerplayerid);
        
    // выведем сообщение о подключении
        
    static    const    fmt_str[] = "%s [%d] зашёл на сервер.";
        new    
    string[sizeof(fmt_str)-2+MAX_PLAYER_NAME-2+5];
        
    GetPlayerName(playeridstringsizeof(string));
        
    format(stringsizeof(string), fmt_strstringplayerid);
        foreach(new 
    i:Player)
            
    SendClientMessage(i, -1string);
    }
    public 
    OnPlayerDisconnect(playerid){
        
    // удалим игрока из итератора
        
    Itter_Remove(Playerplayerid);

    Теперь откомпилируем скрипт с параметром -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_Agplayerid,_:_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(playeridstringsizeof(string));
        
    format(stringsizeof(string), fmt_strstringplayerid);
        for(new  
    i=_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1;_:( i=_:_Y_ITER_C0:Player@YSII_Agi])!=_:_Y_ITER_C1:_Y_ITER_C2:sizeof Player@YSII_Ag-1;)
            
    SendClientMessage(i, -1string);
    }
    public 
    OnPlayerDisconnect(playerid){

        
    Itter_RemoveInternal(_:_Y_ITER_C0:Player@YSII_Cg,_:_Y_ITER_C0:Player@YSII_Agplayerid,_:_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[], valuesize)
    {
        if (
    <= 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[], valuesize)
    {
        new
            
    last;
        return 
    Itter_SafeRemoveInternal(count, array, valuelastsize);
    }

    stock
        Itter_SafeRemoveInternal
    (&count, array[], value, &lastsize)
    {
        if (
    <= 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=0i<125i++)
            
    cvector_push_back(PLAYERSVECTORALi);
        print(
    "125 players connected");
        for(new 
    i=50,ji<75i++)
        {
            
    cvector_find(PLAYERSVECTORALi); // Find player
            
    if(!= -1// If j == -1, vector isn't containing this player
                
    cvector_remove(PLAYERSVECTORALj);
        }
        print(
    "50-74 IDs Disconnected");
        print(
    "Starting main cycle "#PROFILING_ITERS" times");
        
    time GetTickCount();
        for(new 
    i=0i<PROFILING_ITERSi++)
            for(new 
    j=0,k=cvector_size(PLAYERSVECTORAL), playerid=0j<kj++)
            {
                
    playerid cvector_get(PLAYERSVECTORALj); // get playerid
                #pragma unused playerid
            
    }
        
    printf("Vectoral test DONE. Time: %d ms."GetTickCount()-time);
        
    //
        
    print("\n");
        
    //
        
    print("Tracker1's foreach test");
        for(new 
    i=0i<125i++)
            
    Players[players++] = i;
        print(
    "125 players connected");
        for(new 
    i=50i<75i++)
            for(new 
    j=0j<playersj++)
                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=0i<PROFILING_ITERSi++)
            
    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=0i<125i++)
            
    Itter_Add(Playeri);
        print(
    "125 players connected");
        for(new 
    i=50i<75i++)
            
    Itter_Remove(Playeri);
        print(
    "50-74 IDs Disconnected");
        print(
    "Starting main cycle "#PROFILING_ITERS" times");
        
    time=GetTickCount();
        for(new 
    i=0i<PROFILING_ITERSi++)
            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.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  9. 3 пользователя(ей) сказали cпасибо:
    iWors (04.02.2017) L0ndl3m (25.12.2013) Osetin (25.12.2013)
  10. #7
    Аватар для Edwin
    Пользователь

    Статус
    Оффлайн
    Регистрация
    14.02.2014
    Адрес
    Беларусь
    Сообщений
    196
    Репутация:
    22 ±
    А что насчёт "без использования плагина JIT"?

  11. #8
    Аватар для L0ndl3m
    Пользователь

    Статус
    Оффлайн
    Регистрация
    19.10.2013
    Адрес
    Ярославль
    Сообщений
    1,366
    Репутация:
    774 ±
    Цитата Сообщение от Edwin Посмотреть сообщение
    А что насчёт "без использования плагина JIT"?
    Другие результаты.

  12. #9
    Аватар для id228fanta
    Пользователь

    Статус
    Оффлайн
    Регистрация
    18.07.2015
    Сообщений
    19
    Репутация:
    2 ±
    -

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

    Можно сделать капельку по-другому, суть не меняется, просто многие игроки используют 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++)
        {
        // кишки
        }
    Последний раз редактировалось Osetin; 07.01.2016 в 04:45. Причина: Восстановил пост

  13. #10
    Аватар для L0ndl3m
    Пользователь

    Статус
    Оффлайн
    Регистрация
    19.10.2013
    Адрес
    Ярославль
    Сообщений
    1,366
    Репутация:
    774 ±
    id228fanta, вы ошибаетесь.

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

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

 

 
Страница 1 из 4 1 2 3 ... ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •