楼主: lcfllx

[巫妖王之怒] PT优化补丁

[复制链接]

1096

时沙

0

精华

0

主题

声望: 1025   虚弱: 0

Lv.4(锻造者)

祥龙贺岁(尚美制作)金兔迎春(尚美制作)

发表于 2025-10-5 12:24:42 | 显示全部楼层
修修修分享
回复

使用道具 举报

1449

时沙

0

精华

0

主题

声望: 4075   虚弱: 0

Lv.6(觉醒者)

炉石伊利丹·怒风时沙之瓶信仰战假死猎人今天也只能打恢复的增强萨潜行者卖糖术神金鼠(ytfirefox制作) 犇向牛年(尚美制作)

发表于 2025-10-5 19:59:53 | 显示全部楼层

感谢大佬分享
[发帖际遇]: h8787148787在血色修道院逗狗被挣脱链子的大狼狗疯狂攻击,送去急救花了15 金币. 幸运榜 / 衰神榜
回复

使用道具 举报

 楼主| 发表于 2025-10-5 20:07:28 | 显示全部楼层
你的小可爱喵 发表于 2025-10-4 19:33
只能前排提示,不建议在学习初期对线程动刀,尤其是群服状态下的,处理各种竞态那真的是非常痛苦的回忆{:10 ...

- PlayerbotsDatabase.h/cpp - 包含了对Playerbots相关数据库表的操作语句,如PLAYERBOTS_SEL_RANDOM_BOTS_VALUE等
- PlayerbotsScript.cpp - 包含了一些Playerbot脚本事件的接口,如OnPlayerbotUpdate等
---
- ``PlayerbotsDatabase.h`` 和``PlayerbotsDatabase.cpp`` - 这两个文件包含了Playerbots模块的数据库操作,包括随机机器人相关的SQL语句。从这些文件可以看出,有专门的表和操作来管理随机机器人,包括创建、删除、更新等操作。
- ``PlayerbotsScript.cpp`` - 这个文件定义了Playerbots模块的一些脚本接口,包括LFG队列检查、任务处理、会话更新等功能。特别是``OnPlayerbotUpdate(uint32 diff)`` 函数,这可能是机器人更新的关键入口点。
---
1. ``PlayerbotAI.cpp`` - 机器人AI逻辑实现
2. ``PlayerbotMgr.cpp`` - 机器人管理器实现
3. ``RandomPlayerbotMgr.cpp`` - 随机机器人管理器实现
4. ``PlayerbotAIConfig.cpp`` - AI配置实现
---
- ``PlayerbotHolder`` - 基础的机器人持有者类,管理机器人的登录、登出等
- ``PlayerbotMgr`` - 每个主玩家(master)对应的机器人管理器
- ``PlayerbotsMgr`` - 全局的机器人管理器单例
---
1. PlayerbotAI.cpp(约6604行):

   - 这是机器人AI的核心实现文件
   - 包含了AI对象上下文创建、不同状态(战斗/非战斗/死亡)的引擎初始化
   - 有大量的包处理程序(masterIncomingPacketHandlers和botOutgoingPacketHandlers)
   - 包含了与游戏对象交互、任务、组队、LFG等系统的集成
2. RandomPlayerbotMgr.cpp(约3578行):

   - 这是随机机器人管理器的实现
   - 包含了城市和银行家的映射数据
   - 定义了多个线程函数:PrintStatsThread、CheckBgQueueThread、CheckLfgQueueThread、CheckPlayersThread
   - 实现了一个PID控制器(botPID),可能用于控制机器人数量的平衡
这两个文件中与更新循环相关的部分,特别是PlayerbotAI中的更新方法和RandomPlayerbotMgr中的线程管理部分,因为这些很可能是导致性能堵塞的关键区域。
---

1. 在``PlayerbotAI.cpp`` 中,重要的``UpdateAI`` 方法(209行开始),这是机器人AI的核心更新循环。这个方法包含了:

   - 检查机器人状态的逻辑
   - 处理作弊选项的逻辑(设置生命值和能量)
   - 处理当前法术的逻辑
   - 处理移动的逻辑
   - 更新AI内部状态的``UpdateAIInternal`` 调用
   - 最重要的是,在每个更新结束时,调用了``YieldThread(GetReactDelay())`` (329行),这可能是控制线程执行的关键部分。
2. 在``RandomPlayerbotMgr.cpp`` 中,查看``UpdateAIInternal`` 方法的开始部分,这个方法是管理所有随机机器人的核心逻辑。它有性能监控``totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");`` 和一些机器人数量管理的逻辑。
---
1. PlayerbotAI::UpdateAI方法 :

   - 这是机器人AI更新的主循环
   - 每次更新结束时调用``YieldThread(GetReactDelay())``
   - 这个方法在每次世界更新时都会为每个机器人调用,可能是性能瓶颈
2. RandomPlayerbotMgr::UpdateAIInternal方法 :

   - 负责管理所有随机机器人的更新和登录/登出
   - 包含复杂的机器人创建、登录、更新逻辑
   - 有一些性能监控代码``PerformanceMonitorOperation`` ,这表明开发者已经意识到性能问题
3. YieldThread方法 :

   - 在PlayerbotAI.cpp中被调用,但我还没有看到它的具体实现
   - 它可能是导致线程阻塞的关键,因为它看起来是在控制线程执行和延迟
4. ProcessBot方法 :

   - 在RandomPlayerbotMgr.cpp中被调用,但我还没有看到它的具体实现
   - 它似乎负责处理单个机器人的更新逻辑
---

- 在``PlayerbotAIConfig.cpp`` 第165行:``iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);``

- 这表明默认值是100,但在配置文件中被覆盖为10
- 在``PlayerbotAIConfig.h`` 第295行:``uint32 iterationsPerTick;``

- 这是配置类中对该参数的定义
- 在``strategy\Engine.cpp`` 第156-158行:

- 这是该参数实际被使用的地方,特别是在AI引擎的循环中
- 这里使用的是``queue.Size() * sPlayerbotAIConfig->iterationsPerTick`` 作为循环次数上限
- 这意味着队列越大,循环次数越多,可能导致性能问题
---

1. iterationsPerTick的计算方式是:``queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)``
2. 然后有一个while循环,会执行iterationsPerTick次循环迭代
这里非常重要,因为:

- 当有500个机器人时,假设每个机器人的动作队列都有一定大小
- 目前配置的AiPlayerbot.IterationsPerTick=10,这会导致每个机器人每次更新都执行大量循环
- 当系统中有大量机器人时,这会造成严重的性能压力

---
1. 在Engine.cpp中,``iterationsPerTick`` 被计算为``queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)``

   - 当不是minimal模式时,它会将动作队列的大小乘以配置文件中的``AiPlayerbot.IterationsPerTick`` 值
   - 这个值用于限制每次AI更新循环中的最大迭代次数
2. 当前配置文件中的``AiPlayerbot.IterationsPerTick`` 设置为10,这意味着:

   - 对于包含N个动作的队列,每个tick最多会处理N*10次迭代
   - 当机器人数量增加时(如配置中设置的最大20个),这个计算会导致迭代次数成比例增加
   - 如果机器人数量较多且动作队列较大,这可能会导致性能问题
3. 当我们考虑到用户提到的服务器在机器人数量达到20左右时会出现卡顿问题,这个参数很可能是导致性能下降的原因之一。

---.

Engine.cpp中的关键部分:

1. 这个循环是PlayerbotAI执行动作的核心部分。
2. 关键的性能问题点:

   - ``iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)`` - 这里将迭代次数与队列大小相乘,当队列很大时,这会导致迭代次数变得非常大。
   - 没有看到任何的线程yield或sleep机制,这意味着循环会尽可能快地执行所有迭代,可能导致CPU占用过高。
   - 有一个性能监控检查:``if (time(nullptr) - currentTime > 1)`` ,当执行时间超过1秒时会记录日志,但这只是监控而不是限制。


---
1. YieldThread函数的实现在PlayerbotAIBase.cpp中,它实际上只是设置了nextAICheckDelay参数,而不是真正的线程让出。
2. 这个函数的目的是通过设置检查延迟来控制AI更新的频率,而不是直接让出CPU时间片。
3. 从PlayerbotAIBase.cpp的UpdateAI函数可以看到,在每次AI更新后会调用YieldThread(),这会设置默认的检查延迟。

1. Engine.cpp中的循环会执行iterationsPerTick次迭代,这个值默认为10
2. 更重要的是,这个循环没有任何线程yield机制,导致CPU占用率高。
3. YieldThread函数只是设置延迟,并不会真正让出CPU时间片。
优化方案应该是:

1. 在Engine.cpp的循环中添加真正的线程yield机制,例如使用std::this_thread::yield()或std::this_thread::sleep_for()。
2. 或者修改YieldThread函数,使其不仅设置延迟,还实际让出CPU时间片。

---
1. iterationsPerTick 参数在 PlayerbotAIConfig.cpp 中被设置为配置文件中的值``AiPlayerbot.IterationsPerTick`` ,默认值是 100。
2. 在 Engine.cpp 中,iterationsPerTick 被用来控制每个 tick 中执行的操作数量:

3. 这是一个关键的性能问题 - iterationsPerTick 是一个乘数,乘以队列大小,这会导致大量的迭代操作。
4. YieldThread 函数并不是真正的线程让出,它只是设置下一次检查的延迟时间。
优化方案:

1. 减小 AiPlayerbot.IterationsPerTick 的值,从配置文件中的 10 改为更小的值,比如 2。
2. 在 Engine.cpp 的循环中添加真正的线程让出机制,使用 boost::this_thread::yield() 或其他适当的线程让出函数。

---
1. 修改了配置文件,将``AiPlayerbot.IterationsPerTick`` 从默认的 10 减小到了 2,这会显著减少每个AI更新周期中的迭代次数,从而减轻服务器负担。
2. 修改了``PlayerbotAIBase.cpp`` 中的``YieldThread`` 函数,添加了``boost::this_thread::yield()`` 调用,使其真正实现线程让出机制,而不仅仅是设置延迟。这样可以让Playerbot的AI线程更好地与其他线程共享CPU资源,避免长时间占用CPU导致其他线程饥饿。
优化的效果:

1. 减小``IterationsPerTick`` 会直接减少每个AI更新周期中的操作次数,从而减少CPU占用。当前配置为 2,相比默认的 10,理论上可以减少约 80% 的迭代操作。
2. 添加``boost::this_thread::yield()`` 调用会让AI线程在执行完一定操作后主动让出CPU时间片,允许其他线程有机会执行,从而改善整体系统的响应性和稳定性。

---


回复

使用道具 举报

136

时沙

0

精华

0

主题

声望: 3009   虚弱: 0

Lv.5(无冕者)

炉石信仰战

发表于 2025-10-6 23:34:28 | 显示全部楼层
感谢分享
回复

使用道具 举报

267

时沙

0

精华

1

主题

声望: 195   虚弱: 0

Lv.3(忠诚者)

发表于 2025-10-30 21:48:31 | 显示全部楼层
解决了PT线程的痛点,大佬威武
回复

使用道具 举报

快速回复 返回顶部 返回列表