Русское сообщество по скриптингу

Сравнение методов получения списка игроков

Статьи или фрагменты кода для новичков и уже опытных скриптеров по AMXX.

Модератор: Chuvi

Правила форума
1. Запрещено материться и оскорблять других участников форума.
2. Запрещен флуд, оффтоп, дабл постинг во всех разделах форума, кроме раздела "Болтовня".
3. Запрещено взламывать сайт/форум или наносить любой вред проекту.
4. Запрещено рекламировать другие ресурсы.
5. Запрещено создавать темы без информативного названия. Название темы должно отображать ее смысл.

В данном разделе форума разрешено создавать темы, касающие только обучающему материалу по AMX Mod X.

Сравнение методов получения списка игроков

Сообщение Subb98 » 10 май 2016, 02:11

Автор: serfreeman1337
Первоисточник: Вы должны зарегистрироваться, чтобы видеть ссылки.

Иногда перед нами появляется задача узнать индексы нужных нам игроков. Правильнее всего это сделать при помощи функции Вы должны зарегистрироваться, чтобы видеть ссылки., но чаще всего в плагинах можно заметить цикл от 1-ого до 32-ух с проверкой индекса при помощи нативов AMXX. Такой метод называется loop или "петля". Применять его в плагинах никак неправильно. И вот почему. Это вопрос оптимизации, основное правило которого звучит так:

Чем меньше вызовов функций, тем лучше.

Разберем пример с получением индексов всех живых игроков. Код у нас будет таким:

Код: Выделить всё
public function(){ // метод loop
    new pnum = get_playersnum()
 
    for
(new player = 1 ; player <= pnum ; player++){
        if(!is_user_alive(player))
            continue
 
        
// ...
    }

Теперь подсчитаем кол-во обращения к функциям AMXX и какое время потребовалось на выполнение кода. В этом нам поможет сборка AMXX для профилирования плагинов - Вы должны зарегистрироваться, чтобы видеть ссылки.. Версия старая, но рабочая :)

Код: Выделить всё
date: Sun Aug 10 08:42:44 2014 map: de_inferno // профилирование loop
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000001 / 0.000001 / 0.000001
   n |                  register_srvcmd |          1 | 0.000022 / 0.000022 / 0.000022
   n |                   get_maxplayers |          1 | 0.000001 / 0.000001 / 0.000001
   n |                    is_user_alive |         32 | 0.000012 / 0.000000 / 0.000001
   p |                         function |          1 | 0.000011 / 0.000011 / 0.000011
   p |                      plugin_init |          1 | 0.000001 / 0.000001 / 0.000001
0 natives, 0 public callbacks, 2 function calls were not executed.

Код выполнился за 0.000011 секунд. К функциям обращались 33 раза. А теперь сравним это с get_players. Код такой:

Код: Выделить всё
public function(){ // get_players
    new players[32],pnum 
    get_players
(players,pnum,"a")
    
    for
(new i,player ; i < pnum ; i++){
        player = players[i]
            
        
// ..
    }

Данные профилирования:

Код: Выделить всё
date: Sun Aug 10 08:48:37 2014 map: de_inferno // профилирование get_players
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000002 / 0.000002 / 0.000002
   n |                  register_srvcmd |          1 | 0.000022 / 0.000022 / 0.000022
   n |                      get_players |          1 | 0.000003 / 0.000003 / 0.000003
   p |                         function |          1 | 0.000002 / 0.000002 / 0.000002
   p |                      plugin_init |          1 | 0.000001 / 0.000001 / 0.000001
0 natives, 0 public callbacks, 2 function calls were not executed.

Наш код выполнился за 0.000002 секунд. Обращение к функциям AMXX было всего однажды. Для наглядности, давайте проведем стресс-тест, выполним эти блоки кода 1000 раз. Код будет такой:

Код: Выделить всё
public plugin_init() { // сравнение методов
    register_plugin(PLUGIN, VERSION, AUTHOR)
    
    register_srvcmd
("test5","functionLoop")
    register_srvcmd("test6","functionPlayers")
    
}
 
public functionLoop
(){
    new s
    
    while
(s++ < 1000){
        new pnum = get_maxplayers()
        
        for
(new player = 1 ; player <= pnum ; player ++){
            if(!is_user_alive(player))
                continue
                
            
// ...
        }
    }
}
 
public functionPlayers
(){
    new s
    
    while
(s++ < 1000){
        new players[32],pnum 
        get_players
(players,pnum,"a")
        
        for
(new i,player ; i < pnum ; i++){
            player = players[i]
                
            
// ..
        }
    }
}

Результаты:

Код: Выделить всё
date: Sun Aug 10 08:55:26 2014 map: de_inferno // данные сравнения
type |                             name |      calls | time / min / max
-------------------------------------------------------------------
   n |                  register_plugin |          1 | 0.000002 / 0.000002 / 0.000002
   n |                  register_srvcmd |          2 | 0.000041 / 0.000020 / 0.000022
   n |                   get_maxplayers |       1000 | 0.000280 / 0.000000 / 0.000001
   n |                    is_user_alive |      32000 | 0.009030 / 0.000000 / 0.000090
   n |                      get_players |       1000 | 0.000369 / 0.000000 / 0.000002
   p |                     functionLoop |          1 | 0.009689 / 0.009689 / 0.009689
   p |                  functionPlayers |          1 | 0.000320 / 0.000320 / 0.000320
   p |                      plugin_init |          1 | 0.000002 / 0.000002 / 0.000002
0 natives, 0 public callbacks, 3 function calls were not executed.

get_players - 0.000320. loop - 0.009689. Существенная разница, не так ли? Правильный метод быстрее быдлокодого в 30 раз. Ответ очевиден, использование метода loop – признак плохого кода и неопытности автора, ну, или лени :p

=============================================================================================
Полезный комментарий к статье с первоисточника:

Radius писал(а):Попробуйте прогнать вот эти оптимизированные варианты. Даже с оптимизацией loop выигрывает в 3 раза. На моем процессоре результат 10 секунд loop и 34 секунды get_players.

Код: Выделить всё
functionLoop()
{
    static time;
    time = get_systime();
    new s, pnum = get_maxplayers()
    
    while
(s++ < cellmax/100){
        for(new player = 1 ; player <= pnum ; player++){
            if (is_user_alive(player)) {
                
            
}
        }
    }
    
    server_print
("%d sec", get_systime() - time);
}
 
functionPlayers
()
{
    static time;
    time = get_systime();
    new s, players[32], pnum, player
    
    while
(s++ < cellmax/100){
        get_players(players,pnum,"a")
        
        for
(new i; i < pnum ; i++){
            player = players[i]
        }
    }
    
    server_print
("%d sec", get_systime() - time);
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Сравнение методов получения списка игроков

Сообщение Chuvi » 10 май 2016, 05:24

Subb98, а теперь самое ржачное.
Код функции get_players

Код: Выделить всё
static cell AMX_NATIVE_CALL get_players(AMX *amx, cell *params) /* 4 param */
{
    int iNum = 0;
    int ilen;
    char* sptemp = get_amxstring(amx, params[3], 0, ilen);
    int flags = UTIL_ReadFlags(sptemp);

    cell *aPlayers = get_amxaddr(amx, params[1]);
    cell *iMax = get_amxaddr(amx, params[2]);

    int team = 0;

    if (flags & 48)
    {
        sptemp = get_amxstring(amx, params[4], 0, ilen);

        if (flags & 16)
        {
            if (flags & 64)
                team = g_teamsIds.findTeamId(sptemp);
            else
                team 
= g_teamsIds.findTeamIdCase(sptemp);
        }
    }

    for (int i = 1; i <= gpGlobals->maxClients; ++i)
    {
        CPlayer* pPlayer = GET_PLAYER_POINTER_I(i);
        if (pPlayer->ingame || ((flags & 256) && pPlayer->initialized))
        {
            if (pPlayer->IsAlive() ? (flags & 2) : (flags & 1))
                continue;
            if (pPlayer->IsBot() ? (flags & 4) : (flags & 8))
                continue;
            if ((flags & 16) && (pPlayer->teamId != team))
                continue;
            if ((flags & 128) && (pPlayer->pEdict->v.flags & FL_PROXY))
                continue;
            if (flags & 32)
            {
                if (flags & 64)
                {
                    if (stristr(pPlayer->name.chars(), sptemp) == NULL)
                        continue;
                }
                else if (strstr(pPlayer->name.chars(), sptemp) == NULL)
                    continue;
            }
            aPlayers[iNum++] = i;
        }
    }

    *iMax = iNum;
    
    return 1
;
}


Как можно заметить, там присутствует тот же самый loop.
Ну да, не тратится время на вызов из AMXX и передачу результатов обратно.
Плагинами на заказ не занимаюсь.
Своих дел хватает.
Аватара пользователя
Chuvi
Модератор
 
Сообщения: 2253
Зарегистрирован: 24 ноя 2011, 08:03
Благодарил (а): 127 раз.
Поблагодарили: 562 раз.

Re: Сравнение методов получения списка игроков

Сообщение Subb98 » 10 май 2016, 06:32

Chuvi, да я в курсе. :-) Комментарий Radius'а в данной статье - один из самых ценных участков материала, как бы. Я хотел на него сослаться для ответа Вы должны зарегистрироваться, чтобы видеть ссылки., но сделал это через вебкэш гугла, т.к. в тот момент сайт выдавал мне ошибку БД, поэтому решил статью перенести к нам.
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Сравнение методов получения списка игроков

Сообщение Fedcomp » 10 май 2016, 08:58

> использование метода loop – признак плохого кода и неопытности автора
Да, особенно если get_players работает неправильно, да? таки проверил есть у него проблемы или нет?

P.S может еще все на биты переведем?
Не помогаю в ЛС - есть форум.
Плагины тоже не пишу, на форуме достаточно хороших скриптеров.


"я ставлю зависимости потому что мне приятно" - subb98 @ 2017
Аватара пользователя
Fedcomp
Администратор
 
Сообщения: 4936
Зарегистрирован: 28 авг 2009, 20:47
Благодарил (а): 813 раз.
Поблагодарили: 1317 раз.
Языки программирования: =>
pawn / php / python / ruby
javascript / rust

Re: Сравнение методов получения списка игроков

Сообщение RevCrew » 10 май 2016, 09:30

Subb98, что то не понятно, радиус доказал обратное? что loop быстрее get_players?
Аватара пользователя
RevCrew
Скриптер
 
Сообщения: 1648
Зарегистрирован: 15 июл 2013, 20:45
Благодарил (а): 273 раз.
Поблагодарили: 357 раз.
Языки программирования: Unkown

Re: Сравнение методов получения списка игроков

Сообщение Subb98 » 10 май 2016, 10:08

RevCrew, ну да. Однако, для чистоты эксперимента, не помешает провести и свои тесты. У каждого, всё же, свои железки и, мало ли, как они могут влиять на итог тестирования.

Добавлено спустя 28 секунд:
Fedcomp писал(а):> использование метода loop – признак плохого кода и неопытности автора
Да, особенно если get_players работает неправильно, да?

Во-первых, ещё неизвестно наверняка, корректно или нет работает get_players, я видел официальную инфу, что это было исправлено в версии 1.8.2. Официальной инфы об оставшихся багах в поздних версиях не видел, поэтому "будем посмотреть". Во-вторых, почему ты говоришь это мне? Не я автор статьи. И я написал выше, для чего эта статья здесь.

Fedcomp писал(а):таки проверил есть у него проблемы или нет?

Ещё не проверял.

Fedcomp писал(а):P.S может еще все на биты переведем?

Чем тебе не угодили биты?
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Сравнение методов получения списка игроков

Сообщение Fedcomp » 10 май 2016, 10:39

Subb98 писал(а):Чем тебе не угодили биты?

Уже изучаешь C++? я слышал там можно делать ассемблерные вставки. Знаешь как быстро?
Не помогаю в ЛС - есть форум.
Плагины тоже не пишу, на форуме достаточно хороших скриптеров.


"я ставлю зависимости потому что мне приятно" - subb98 @ 2017
Аватара пользователя
Fedcomp
Администратор
 
Сообщения: 4936
Зарегистрирован: 28 авг 2009, 20:47
Благодарил (а): 813 раз.
Поблагодарили: 1317 раз.
Языки программирования: =>
pawn / php / python / ruby
javascript / rust

Re: Сравнение методов получения списка игроков

Сообщение Subb98 » 10 май 2016, 10:42

Fedcomp, эти "вставки" можно делать в скриптах AMXX? Если нет, то при чём тут они? Вопрос конкретно по битам: чем они тебе не угодили?
«Очень хорошо. Лучше вы, чем я» © Donald J. Trump
Аватара пользователя
Subb98
Модератор
 
Сообщения: 5485
Зарегистрирован: 24 мар 2011, 19:42
Откуда: г. Пермь
Благодарил (а): 1329 раз.
Поблагодарили: 2343 раз.
Опыт программирования: Больше трех лет
Языки программирования: PHP

Re: Сравнение методов получения списка игроков

Сообщение Asmodai » 10 май 2016, 12:17

В некотором смысле можно)
Аватара пользователя
Asmodai
Адмирал
 
Сообщения: 466
Зарегистрирован: 24 фев 2011, 20:48
Благодарил (а): 0 раз.
Поблагодарили: 393 раз.
Языки программирования: Counter-Strike 1.6

Re: Сравнение методов получения списка игроков

Сообщение Nixon133 » 10 май 2016, 14:28

Два метода:

Код: Выделить всё
   
   f 
|                         funcLoop |      10000 0.002449 0.000000 0.000052
   f 
|                    funcGetPlayer |      10000 0.000650 0.000000 0.000077


Функция loop использует is_user_alive. Если заменить на бит (к примеру свой мод и есть булевая которая true\false для смерти), то результат будет почти одинаков:

Код: Выделить всё

   f 
|                         funcLoop |      10000 0.000749 0.000000 0.000009
   f 
|                    funcGetPlayer |      10000 0.000543 0.000000 0.000008
скуйп - legaalize
Аватара пользователя
Nixon133
 
Сообщения: 476
Зарегистрирован: 13 ноя 2012, 12:20
Благодарил (а): 107 раз.
Поблагодарили: 56 раз.
Опыт программирования: Больше трех лет
Языки программирования: C, Pawn

След.

Вернуться в Статьи / фрагменты кода

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9