• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

C++ High CPU Usage classicAttackSpeed

Addams

OTMadness.com OTSes Services
Staff member
Board Moderator
Joined
Sep 29, 2009
Messages
2,920
Solutions
342
Reaction score
1,688
Location
Egypt
I have been trying this for a few days but still cannot find a fix, so in the meantime, I am trying myself. I decided to post this here in case someone has a solution.
CPU is going up to 80% by just one function, using TFS 1.4.2 with many commits of 1.5.

I have classicAttackSpeed = true in my config.lua

Code:
[17/08/2023 19:30:40]
Thread: 1 Cpu usage: 71.1587% Idle: 28.7749% Other: 0.066461% Players online: 46
Time (ms)     Calls     Rel usage %    Real usage % Description
16767       314       78.54636%       55.89254% std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)

Here's the checkCreatures function.
C++:
void Game::checkCreatures(size_t index)
{
    g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)));

    auto& checkCreatureList = checkCreatureLists[index];
    auto it = checkCreatureList.begin(), end = checkCreatureList.end();
    while (it != end) {
        Creature* creature = *it;
        if (creature->creatureCheck) {
            if (creature->getHealth() > 0) {
                creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
                creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
                creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
            }
            ++it;
        } else {
            creature->inCheckCreaturesVector = false;
            it = checkCreatureList.erase(it);
            ReleaseCreature(creature);
        }
    }

    cleanup();
#ifdef STATS_ENABLED
    g_stats.playersOnline = getPlayersOnline();
#endif
}
and here's the doAttacking function.
C++:
void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();
        bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED);

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!classicSpeed && !canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        if (!classicSpeed) {
            setNextActionTask(task, false);
        } else {
            g_scheduler.addEvent(task);
        }

        if (result) {
            lastAttack = OTSYS_TIME();
        }
    }
}
That is the full log at the exact same time as the single-function log above.
Code:
[17/08/2023 19:30:40]
Thread: 1 Cpu usage: 71.1587% Idle: 28.7749% Other: 0.066461% Players online: 46
 Time (ms)     Calls     Rel usage %    Real usage % Description
     16767       314       78.54636%       55.89254% std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)
      1063      6211        4.98369%        3.54632% std::bind(&Game::checkCreatureAttack, &g_game, getID())
       805      2373        3.77382%        2.68540% &Game::playerSay
       573        77        2.68796%        1.91272% std::bind(&Game::checkDecay, this)
       547       588        2.56446%        1.82484% std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment, lastTimerEventId)
       468      6377        2.19267%        1.56028% std::bind(&Game::checkCreatureWalk, &g_game, getID())
       437      1150        2.05126%        1.45965% [&]() { sendAll(bufferedProtocols); }
       191       247        0.89923%        0.63988% std::bind(&Game::executeDeath, &g_game, getID())
        76       730        0.36022%        0.25633% std::bind(&Game::updateCreatureWalk, &g_game, getID())
        74       660        0.34983%        0.24893% &Game::playerUseWithCreature
        72       256        0.34151%        0.24302% std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, spriteId)
        60    142808        0.28539%        0.20308% std::bind(&ProtocolGame::writeToSpectatorsOutputBuffer, this, msg)
        53       352        0.24831%        0.17669% std::bind(&Spawn::checkSpawn, this)
        37       399        0.17724%        0.12612% &Game::playerMove
        26      1096        0.12200%        0.08682% &Game::playerReceivePingBack
        23       318        0.10789%        0.07677% &Game::playerSetAttackedCreature
 
What if
C++:
void Game::checkCreatures(size_t index)
{
    auto& checkCreatureList = checkCreatureLists[index];
    auto it = checkCreatureList.begin();

    while (it != checkCreatureList.end()) {
        Creature* creature = *it;
        if (creature && creature->creatureCheck) {
            if (creature->getHealth() > 0) {
                creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
                creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
                creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
            } else {
                creature->onDeath();
            }
            ++it;
        } else {
            if (creature) {
                creature->inCheckCreaturesVector = false;
                ReleaseCreature(creature);
            }
            it = checkCreatureList.erase(it);
        }
    }
    
    cleanup();

    // Schedule the next checkCreatures call
    size_t nextIndex = (index + 1) % EVENT_CREATURECOUNT;
    g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, [this, nextIndex]() {
        checkCreatures(nextIndex);
    }));
}
 
Throws a warning and an error.
C++:
warning C4002: too many arguments for function-like macro invocation 'createSchedulerTask'
error C2059: syntax error: 'string'
 
@Addams
Change this:
C++:
g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, [this, nextIndex]() {
    checkCreatures(nextIndex);
}));

to:
C++:
g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, nextIndex)));
 
What if
C++:
void Game::checkCreatures(size_t index)
{
    auto& checkCreatureList = checkCreatureLists[index];
    auto it = checkCreatureList.begin();

    while (it != checkCreatureList.end()) {
        Creature* creature = *it;
        if (creature && creature->creatureCheck) {
            if (creature->getHealth() > 0) {
                creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
                creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
                creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
            } else {
                creature->onDeath();
            }
            ++it;
        } else {
            if (creature) {
                creature->inCheckCreaturesVector = false;
                ReleaseCreature(creature);
            }
            it = checkCreatureList.erase(it);
        }
    }
  
    cleanup();

    // Schedule the next checkCreatures call
    size_t nextIndex = (index + 1) % EVENT_CREATURECOUNT;
    g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, [this, nextIndex]() {
        checkCreatures(nextIndex);
    }));
}
@Addams
Change this:
C++:
g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, [this, nextIndex]() {
    checkCreatures(nextIndex);
}));

to:
C++:
g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, nextIndex)));
Try and tell us if it changed anything from performance point
Hey Addams, have you solved the problem these guys mentioned above? How's the performance? If the problem is resolved, I will also update my source. Awaiting your feedback!
CPU is still going crazy and also feels like much more than before the change.
I have been told that it's some bad loop on doAttacking
Code:
[21/08/2023 15:21:02]
Thread: 1 Cpu usage: 70.5095% Idle: 31.5613% Other: -2.07077% Players online: 39
 Time (ms)     Calls     Rel usage %    Real usage % Description
     11218       145       53.03759%       37.39652% std::bind(&Game::checkCreatures, this, nextIndex)
      5790      6508       27.37424%       19.30143% std::bind(&Game::checkCreatureAttack, &g_game, getID())
       932      2218        4.40935%        3.10901% std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment, lastTimerEventId)
       876        77        4.14227%        2.92069% std::bind(&Game::checkDecay, this)
       612      1207        2.89625%        2.04213% [&]() { sendAll(bufferedProtocols); }
       577      2462        2.72928%        1.92440% &Game::playerSay
       540      5049        2.55407%        1.80086% std::bind(&Game::checkCreatureWalk, &g_game, getID())
       220       284        1.04415%        0.73623% std::bind(&Game::executeDeath, &g_game, getID())
        59       720        0.27915%        0.19682% &Game::playerUseWithCreature
        41       240        0.19390%        0.13672% std::bind(&Game::updateCreatureWalk, &g_game, getID())
        40       199        0.19257%        0.13578% std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, spriteId)
        35     87270        0.16757%        0.11815% std::bind(&ProtocolGame::writeToSpectatorsOutputBuffer, this, msg)
        34       333        0.16495%        0.11631% [=]() { spawnMonster(spawnId, sb, false); }
        33       892        0.15808%        0.11146% &Game::playerReceivePingBack
        32       351        0.15189%        0.10710% &Game::playerMove
        31       422        0.14824%        0.10452% std::bind(&Spawn::checkSpawn, this)
        28       656        0.13398%        0.09447% &Game::playerSetAttackedCreature
 
CPU is still going crazy and also feels like much more than before the change.
I have been told that it's some bad loop on doAttacking
Code:
[21/08/2023 15:21:02]
Thread: 1 Cpu usage: 70.5095% Idle: 31.5613% Other: -2.07077% Players online: 39
 Time (ms)     Calls     Rel usage %    Real usage % Description
     11218       145       53.03759%       37.39652% std::bind(&Game::checkCreatures, this, nextIndex)
      5790      6508       27.37424%       19.30143% std::bind(&Game::checkCreatureAttack, &g_game, getID())
       932      2218        4.40935%        3.10901% std::bind(&LuaEnvironment::executeTimerEvent, &g_luaEnvironment, lastTimerEventId)
       876        77        4.14227%        2.92069% std::bind(&Game::checkDecay, this)
       612      1207        2.89625%        2.04213% [&]() { sendAll(bufferedProtocols); }
       577      2462        2.72928%        1.92440% &Game::playerSay
       540      5049        2.55407%        1.80086% std::bind(&Game::checkCreatureWalk, &g_game, getID())
       220       284        1.04415%        0.73623% std::bind(&Game::executeDeath, &g_game, getID())
        59       720        0.27915%        0.19682% &Game::playerUseWithCreature
        41       240        0.19390%        0.13672% std::bind(&Game::updateCreatureWalk, &g_game, getID())
        40       199        0.19257%        0.13578% std::bind(&Game::playerUseWithCreature, this, playerId, fromPos, fromStackPos, creatureId, spriteId)
        35     87270        0.16757%        0.11815% std::bind(&ProtocolGame::writeToSpectatorsOutputBuffer, this, msg)
        34       333        0.16495%        0.11631% [=]() { spawnMonster(spawnId, sb, false); }
        33       892        0.15808%        0.11146% &Game::playerReceivePingBack
        32       351        0.15189%        0.10710% &Game::playerMove
        31       422        0.14824%        0.10452% std::bind(&Spawn::checkSpawn, this)
        28       656        0.13398%        0.09447% &Game::playerSetAttackedCreature
Hmmm. About doAttacking i see only few optimization points, OTSYS_TIME() is called multiple times within the function, you can cache the value in a variable to avoid repeated function calls. Weapon lookup is done twice for the same condition, you can perform the lookup once and reuse the weapon pointer. The creation of the scheduler task can be moved outside the if condition and since youre using the currentTime variable earlier, you can also use it for the SchedulerTask creation instead of calculating std::max<uint32_t>(SCHEDULER_MINTICKS, delay). But again question is, is really gonna help tho?
 
I am not sure, but one function should never consume that much CPU. I will just keep trying the solutions coming in the thread and see if that will reduce CPU anyhow.
So in case anyone has a not 100% sure solution, I can just put it on tests.
 
I am not sure, but one function should never consume that much CPU. I will just keep trying the solutions coming in the thread and see if that will reduce CPU anyhow.
So in case anyone has a not 100% sure solution, I can just put it on tests.
Will be following this thread since it affects all of us probably
 
Back
Top