服务端 CPU 夜里莫名冲到九成,日志看不出卡在哪个函数。火焰图采样给 CPU 拍片:按固定间隔记录调用栈,聚合后哪个函数占的宽度大一目了然,排查从猜谜变成看图定位。火焰图的价值在于把模糊的“感觉卡”变成具体的函数名与耗时占比,讨论对象从情绪变成数据。
关键路径分段打点,用 os.clock 记录每段耗时并按标签聚合,采样窗口结束后输出耗时排行,最宽的横条就是最热函数。
local samples = {}
local function profile(label, fn)
local t0 = os.clock()
fn()
local cost = os.clock() - t0
samples[label] = (samples[label] or 0) + cost
end
setontimerex(198, 300)
function ontimer198()
local worst, worstCost = "", 0
for label, cost in pairs(samples) do
if cost > worstCost then
worst, worstCost = label, cost
end
end
if worst ~= "" then
sendcentermsg("CPU 热点:" .. worst .. ",5 分钟累计 " .. string.format("%.2f", worstCost) .. " 秒")
end
samples = {}
end
定位到排行榜实时重建是最大热点后,改成脏标记加五秒批量重建,复测同一窗口的耗时下降九成,治理效果用同一把尺子量化。
local rankDirty = false
local function markRankDirty()
rankDirty = true
end
setontimerex(199, 5)
function ontimer199()
if rankDirty then
profile("rebuild_rank", rebuildRankCache)
rankDirty = false
end
end
构造一个已知耗时的慢函数打点,采样值与真实耗时偏差低于 5%;治理前后同一负载下的窗口耗时对比,下降幅度即优化收益;采样期间业务耗时波动不超过一成,确认探针本身足够轻。慢函数的构造用例除了校验采样精度,也用于回归确认探针没有引入新的热点。监控每日热点排行,同一函数连续三天上榜就立项优化。采样探针的自身开销也纳入统计单独列出,确保测量不喧宾夺主。采样窗口的五分钟粒度对突发尖峰不敏感,尖峰排查时临时把窗口缩到 60 秒。
采样打点自身有开销,最初给每个函数都包一层 profile,全服耗时凭空多了一成,改成只探可疑路径,探针数量控制在一打以内。os.clock 在部分引擎实现里统计的是进程时间而非墙钟时间,跨进程对比没有意义,火焰图数据只在单进程内解读。聚合标签用调用点拼接的动态字符串,标签数量爆炸撑爆统计表,标签收敛为白名单枚举。热点排序只看累计耗时,忽略调用次数,低耗高频的函数漏网,排行加了两列耗时与次数。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 装备回收商 NPC 每天按评分收购玩家的装备,裁决之杖、骨玉权杖按攻击、持久加权打分,榜上只要前五名。数…
实战应用:用在哪里 沙巴克攻城是全服行会战的最高舞台:攻方砸开城门、抢占皇宫,守方依托城墙与复活点反打。规则设计的关键是节奏…
实战应用:用在哪里 影分身玩法给法师职业补上生存短板:危急时刻召唤四个影分身分担仇恨,敌人需要在六格范围内分辨真身,猜错就浪…
实战应用:用在哪里 组队邀请、行会宣战这类关键信令丢了没有兜底,玩家只能反复手点,高峰期体验很差。重试机制给信令加确认回执:…
实战应用:用在哪里 新资料片上线首日预约量常是日常十倍,一口气全开会让数据库与登录网关同时过载。滚动开区把开区动作排成队列:…
实战应用:用在哪里 百人同屏的沙巴克外围战场,每个玩家每秒跑动会触发数次位置更新,全部即时下发时广播量随人数平方增长,网关先…