Флейм
GameDev.ru / Флейм / Форум / [Russian AI Cup] CodeWars 2017 (6 стр)

[Russian AI Cup] CodeWars 2017 (6 стр)

Поделиться
Advanced: Тема повышенной сложности или важная.

Страницы: 1 2 3 4 5 6 7 Следующая

ud1Постоялецwww14 ноя. 201721:32#75
Adler
Ну видно, что они долго оптимизировали. Вот бы еще локал раннер дружественный для отладки сделали. Там минимум сейчас камера не удобная. Хотелось бы, чтоб было сохранение состояния в произвольный момент времени и запуск новой игры ровно от того сохранения. Свой локал раннер походу надо писать, тяжело.
AdlerПостоялецwww15 ноя. 20170:14#76
ud1
> Ну видно, что они долго оптимизировали.
int RemoteProcessClient::readInt() {
    vector<signed char> bytes = this->readBytes(INTEGER_SIZE_BYTES);

    if (this->isLittleEndianMachine() != LITTLE_ENDIAN_BYTE_ORDER) {
        reverse(bytes.begin(), bytes.end());
    }

    int value;
    memcpy(&value, &bytes[0], INTEGER_SIZE_BYTES);
    return value;
}
запустил профайлер:
read lead to a lot of sys calls | [Russian AI Cup] CodeWars 2017
std::vector eat half of cputime in this method | [Russian AI Cup] CodeWars 2017

ЗЫ: судя по первой картинке можно подумать: либо с java стороны байты по одной штуке приходят, либо на стороне с++ каждый раз буфер размером в 100кб создаётся just for lulz.

added: если бы я делал протокол и мне не мешали, то я бы старался за два syscall`а получать PlayerContext.

Правка: 15 ноя. 2017 0:19

ud1Постоялецwww15 ноя. 20170:37#77
Adler
> ЗЫ: судя по первой картинке можно подумать: либо с java стороны байты по одной
> штуке приходят, либо на стороне с++ каждый раз буфер размером в 100кб создаётся
> just for lulz.
Где 100Кб? Посмотрел, byteCount передается ровно столько, сколько хотят прочесть. И насколько помню со старых контестов, со стороны жавы тоже нормально передавали, сразу сообщение целиком, не по байту. Код конечно можно еще облагородить, но в целом вроде норм.
AdlerПостоялецwww15 ноя. 20171:25#78
counter воткнул:
int32 CSimpleSocket::Receive(int32 nMaxBytes)
{
...
    if ((m_pBuffer != NULL) && (nMaxBytes != m_nBufferSize))
    {
        static int counter=0;counter++; // <------------------------- этот = 395856/209 = 1894.04 за кадр в среднем
        delete [] m_pBuffer;
        m_pBuffer = NULL;
    }
...
    switch (m_nSocketType)
    {
        case CSimpleSocket::SocketTypeTcp:
        {
            do 
            {
                static int counter=0;counter++;  // <------------------------- этот = 649411/209 = 3107.22 за кадр в среднем
                m_nBytesReceived = RECV(m_socket, (m_pBuffer + m_nBytesReceived), 
                                        nMaxBytes, m_nFlags);


                TranslateSocketError();
            } while ((GetSocketError() == CSimpleSocket::SocketInterrupted));

            break;
        }
...
RECV - это recv из Winsock2.h

они тут m_pBuffer пересоздают каждый раз когда надо что-то прочитать другого размера.

also: RemoteProcessClient::readBytes вызывается 494891/209=2367.89 раз за кадр в среднем.

итого:
выделений памяти: 1894+2367 = 4261 раз за кадр
вызов recv: 3107 раз за кадр  // вместо двух, если бы всё было сделано идеально. да, на стороне java надо было бы посчитать весь размер заранее, но это должно быть быстрее чем делать дополнительные системные вызовы send/recv.

ЗЫ: ещё воткнул "static int recv_n=0;recv_n+=m_nBytesReceived>0?m_nBytesReceived:0;" в конец CSimpleSocket::Receive
результат: 2963076/209 = 14177.39 байт за кадр.

upd:
оптимизировал readVehicleUpdate

+ Показать

upd:
замерил среднее время выполнения readVehicleUpdates:
до: 6.55 ms/frame
после: 3.69 ms/frame

Правка: 15 ноя. 2017 14:13

AdlerПостоялецwww15 ноя. 20172:37#79
ud1
> И насколько помню со старых контестов, со стороны жавы тоже нормально
> передавали, сразу сообщение целиком, не по байту. Код конечно можно еще
> облагородить, но в целом вроде норм.
смотри, в среднем за кадр надо прочитать 14177.39 байт, но для этого делается 3107 системных вызов recv, то есть в среднем читается по 4.56 байта за один вызов recv.

это не нормально.

Правка: 15 ноя. 2017 2:38

AdlerПостоялецwww15 ноя. 201715:06#80
короче, я зафиксировал seed в "local-runner.default.properties", затем отрыл свою стратегию которая:
  выделяет все юниты
  добавил их в группу 42
  приказывает им вращаться вокруг центра карты

а потом замерил среднее время выполнения readVehicleUpdates для 128 первых кадров: 31.4 ms/frame

стратегии вроде разрешено в среднем тратить только 20ms/frame

полностью переписал readVehicleUpdate:

VehicleUpdate RemoteProcessClient::readVehicleUpdate() {
    if(bool original_version=false)
    {
      if (!readBoolean()) {
          exit(20013);
      }

      long long id = readLong();
      double x = readDouble();
      double y = readDouble();
      int durability = readInt();
      int remainingAttackCooldownTicks = readInt();
      bool selected = readBoolean();//*/

      vector<int> groups = readIntArray();
      return VehicleUpdate(id, x, y, durability, remainingAttackCooldownTicks, selected, groups);
    }

    #define LIST(ADD)\
    ADD(bool,ok,$)\
    ADD(long long,id,$)\
    ADD(double,x,$)\
    ADD(double,y,$)\
    ADD(int,durability,$)\
    ADD(int,remainingAttackCooldownTicks,$)\
    ADD(bool,selected,$)
    //===
    struct t_tmp{
      bool trash_bef[8-1];
      #define F(TYPE,NAME,VALUE)TYPE NAME;
      LIST(F);
      #undef F
      bool groups_size[8-1];
    };
    int size=sizeof(t_tmp)-(8-1)*2+4;
    t_tmp tmp={0};

    //
    if(bool need_fast_networking=true)
    {
      unsigned int offset=0;
      auto m_pBuffer=(char*)&tmp.ok;

      for(;;)
      {
        if(offset>=size)break; 
        auto receivedByteCount=recv(socket.m_socket,m_pBuffer+offset,size-offset,socket.m_nFlags);
        //socket.Receive(size-offset);
        if(receivedByteCount<=0)break;
        offset+=receivedByteCount;
      }
    
      if(offset!=size){
        exit(10012);
      }
    }

    #define F(TYPE,NAME,VALUE)auto&##NAME=tmp.NAME;
    LIST(F);
    #undef F
    #undef LIST
    
    if(!ok)exit(20013);

    vector<int> groups;groups.resize(*(int*)&tmp.groups_size);

    if(bool need_fast_networking=true)if(!groups.empty())
    {
      int size=groups.size()*4;
      unsigned int offset=0;
      auto m_pBuffer=(char*)&groups[0];

      for(;;)
      {
        if(offset>=size)break; 
        auto receivedByteCount=recv(socket.m_socket,m_pBuffer+offset,size-offset,socket.m_nFlags);
        if(receivedByteCount<=0)break;
        offset+=receivedByteCount;
      }
    
      if(offset!=size){
        exit(10012);
      }
    }

    return VehicleUpdate(id, x, y, durability, remainingAttackCooldownTicks, selected, groups);
}
у меня этот код работает на этом же тесте в среднем за 3.9ms/frame для 128 первых кадров.

+ Показать

ЗЫ: ещё я "оптимизировал" StatTimer.h: https://pastebin.com/zBjiHByb

Правка: 15 ноя. 2017 15:07

DiversusПостоялецwww15 ноя. 201715:28#81
Мне кажется на данный момент самая лучшая стратегия у GreanTea http://russianaicup.ru/profile/GreenTea
Квадратно-гнездовой способ битвы его стратегии проигрывает в чистую.
AdlerПостоялецwww15 ноя. 201716:48#82
похоже инфа о том, кто что выделил и какой юнит в каких группах находиться присылается даже про оппонента.

added:
отправил pull request: https://github.com/Russian-AI-Cup-2017/cpp-cgdk/pull/3

Правка: 15 ноя. 2017 17:18

JenersПостоялецwww15 ноя. 201718:51#83
Diversus
Да неплохо, но имхо тут ЯУ рашит не фиго, зря добавили
ZefickПостоялецwww15 ноя. 201719:12#84
Adler
> отправил pull request: https://github.com/Russian-AI-Cup-2017/cpp-cgdk/pull/3
  Надеюсь у них всё не так плохо с ревью кода, чтобы это прошло в продакшн.
+ Показать

Правка: 15 ноя. 2017 19:18

JenersПостоялецwww15 ноя. 201720:09#85
ЕПРСТ объясните как пашет Scale плиз, а то из прввил не фига не въехал
UPD все разобрался я затупок команду перекрывал следующей за ней идущей

Правка: 15 ноя. 2017 20:25

DiversusПостоялецwww15 ноя. 201722:19#86
Что-то я не пойму, как перебрать свою технику в цикле?
Только через VechicleUpdate? А там и своя и чужая техника, или как?
JenersПостоялецwww15 ноя. 201723:29#87
Diversus
GetNewVehicles - содержит не изменяемые параметры "новой" техники в игре
Т.е. в начале игры этот вызов делается в тике 0 мы получаем всю существующую технику в игре (включая свою, и вражескую разница в PlayerID) (Так же как я понял в режиме "туман войны", вражеская техника в этот массив не попадает, а появляется в случае обнаружения). Так же технику с 0 здоровьем можно смело затирать
Далее по ходу игры вызываешь GetVehicleUpdates оно содержит изменяемые поля (x,y, здоровье, ну и ID техники ессесно) 
По ID техники присваиваешь новые параметры своему массиву данных. Как-то так

Правка: 15 ноя. 2017 23:32

AdlerПостоялецwww16 ноя. 201719:29#88
сначала оптимизируешь время работы стратегии где вся тысяча объектов движется все 20000 кадров.
потом находишь тормозное говно в сетевой части стратегии.
исправляешь его.
отправляешь pull request
ждешь.
а потом тебе заявляют:
Мы не принимаем правки в автогенерённый код, так как их невозможно поддерживать в случае изменения клиента. Время работы пустой стратегии на C++ составляет всего несколько секунд при общем лимите в 210 секунд. Подобный уровень оптимизации излишен.

> Время работы пустой стратегии на C++
WTF?

ответил им:

вот в этой игре:
http://russianaicup.ru/game/view/30740

я использовал вот эту стратегию:
https://pastebin.com/raw/Npqqs1m4

посмотрите на время:
time consumed: 84.12 sec
time passed: 242.33 sec
peak memory: 4874240 bytes

что скажите?

PS: им придёт уведомление на комментарий в закрытом pull request`е? или это тоже самое что в /dev/null отправить?

Правка: 16 ноя. 2017 19:42

ud1Постоялецwww16 ноя. 201721:00#89
Adler
Ща чую ты добьешся от них того, что число групп уменьшат. До 5 например.

Страницы: 1 2 3 4 5 6 7 Следующая

/ Форум / Флейм / Программирование

2001—2017 © GameDev.ru — Разработка игр