You need to enable JavaScript to run this app. 为何循环内有无sleep/usleep时同一函数调用耗时差异大、perf指令数暴涨?
优惠活动
大模型
产品
解决方案
定价
更多

为何循环内有无sleep/usleep时同一函数调用耗时差异大、perf指令数暴涨?

异常性能差异的底层原因拆解

先明确两个核心认知偏差,这是所有观测现象的来源:

  • 你用std::chrono::high_resolution_clock测得的单次窗口耗时,并非纯函数执行耗时,而是包含计时窗口内的系统调度抖动、CPU微架构状态冷热差异的总时间。
  • 你用perf stat统计的instructions、cache-misses等指标,并非只统计测试函数的执行开销,而是统计被测进程从启动到退出全生命周期内,被分配到的CPU核心上执行的所有指令(包括内核态指令、同核其他进程/线程的指令、idle线程指令)的总和。

不同sleep配置下单次函数耗时差异的来源

观测到的耗时差本质来自两方面影响,和cache冲刷相关,但和跨核迁移无关:

  1. CPU微架构状态的冷热差异
    • 无sleep场景下,循环是紧挨着执行的:上一次std::set::lower_bound跑完后,红黑树遍历路径上的节点内存、分支预测器记住的遍历跳转模式,都还保留在L1/L2 cache和CPU分支预测缓存里,下一次执行几乎没有冷启动开销,所以单次耗时稳定在1-2us。
    • usleep(100)场景下,每次休眠唤醒过程中,哪怕固定到同一个CPU核心,内核也要处理定时器软中断、进程调度、信号检查等逻辑,这些内核代码的执行会把之前留在L1 cache里的set节点数据挤出去,分支预测器也会因为切到内核代码执行几轮,冲掉之前记住的红黑树遍历分支模式。重新切回用户态执行函数时,程序已经回到冷启动状态,所以耗时稳定在5-8us,比无sleep的热状态高3-4倍。
    • sleep(1)场景下,除了冷启动开销,还存在明显的调度抖动:1秒尺度的休眠唤醒本身就有几十微秒的调度延迟,而且唤醒后进程不一定能立刻拿到CPU执行权——如果刚切回用户态跑了两条指令就被调度器抢占处理其他任务,这部分等待时间会被完整算进计时窗口,这就是观测到65us这种极高耗时值的原因:这不是函数本身跑了这么久,是调度等待的时间被纳入了统计。
  2. 绑核无法消除差异的原因
    之前假设跨核迁移是cache miss的来源,但实际上同一个CPU核心上,用户态程序和内核态代码共享L1/L2 cache——只要从用户态切到内核态(不管是进入sleep还是被中断抢占),内核执行的代码和数据本身就会冲掉用户态留在cache里的热数据,和进程跨不跨核没有关系。绑核只能消除跨核迁移的开销,解决不了用户态/内核态切换带来的cache冲刷问题。

sleep场景下perf统计instructions暴涨的原因

直接对比三个场景的总运行时间和指令数即可发现规律:

  • 无sleep和usleep(100)的总运行时间都在1.03s左右,instructions分别为1.3亿和1.18亿,数值基本持平——这两个场景中进程大部分时间在用户态跑测试循环,几乎没有多余开销,指令数和实际测试逻辑的执行规模匹配。
  • sleep(1)场景总运行时间为6.03s,指令数达到245亿,换算下来平均每秒执行40亿条指令。但测试代码满打满算,5次lower_bound加上1亿次set插入的初始化逻辑,总共也就跑几千万条指令,剩下99%的指令都不是测试代码产生的:
    • 每次nanosleep进入内核态,内核要完成定时器注册、进程状态挂起、唤醒触发、重新调度的全流程,这部分内核态执行的指令会被perf统计到进程名下(perf默认开启内核态计数)。
    • 休眠的1秒时间里,进程根本不在CPU上执行,此时CPU核心上跑的idle线程、其他系统进程的任务、内核定时器的软中断处理,这些产生的指令也会被perf绑定的CPU计数器统计到,最终计入进程的总指令数。
      所谓的instructions暴涨,本质是进程休眠期间的同核其他任务、内核态切换产生的额外指令,并非测试函数本身执行产生的开销。

参考的正确测试方式

  • 单次打时间点测微秒级函数耗时的方法本身误差极大,容易被系统调度、中断抖动带偏。如果要测纯函数执行耗时,应该把目标函数连续循环调用几万次,用总耗时除以调用次数得到平均开销,消除单次测量的随机抖动。
  • 如果要测微架构层面的真实开销,直接用perf stat在函数入口和出口打点统计这段代码的cycles、cache-misses、instructions,不要在函数前后夹杂sleep这类系统调用,避免内核切换带来的统计干扰。

补充:观测到的cache-miss率变化,本质也是统计口径问题——sleep(1)场景下因为统计了大量同核其他任务的cache访问行为,总cache参考数涨了10倍,所以cache miss的比例看起来偏低,不代表测试函数本身的cache miss率改善了。

内容的提问来源于stack exchange,提问作者Peng Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.06 15:54:07