不能。dis.dis()显示的是cpython字节码(如load_fast、binary_add),非cpu汇编;它用于识别python层低效模式,如冗余装箱、重复计算或未内联调用,而非替代perf/objdump。

dis.dis() 能看到真正的 CPU 汇编指令吗?
不能。dis.dis() 显示的是 CPython 的字节码(bytecode),不是 x86 或 ARM 汇编。它反映的是 Python 解释器内部的指令,比如 LOAD_FAST、BINARY_ADD、RETURN_VALUE —— 这些是解释器虚拟机执行的单元,和 CPU 指令隔了至少两层抽象。
想靠它“优化底层执行逻辑”,目标要调准:它适合发现 Python 层面的低效模式,比如隐式装箱、重复计算、未内联的函数调用,而不是替代 perf 或 objdump。
哪些函数/代码块值得用 dis.dis() 检查?
优先检查高频执行、已知慢、或语义简单但实测不快的代码。例如:
- 被
@lru_cache包裹但命中率低的函数 —— 看是否真在缓存入口处就做了冗余字节码(如参数 unpacking) - 列表推导式 vs
map()vs 手动 for 循环 —— 对比LIST_APPEND出现次数和位置 - 带默认参数的函数定义 —— 检查是否在每次调用时都重新构造默认可变对象(如
def f(x=[])会生成BUILD_LIST字节码) - 使用
is和==的条件分支 —— 看是否因对象类型不同导致多出COMPARE_OP+CALL_METHOD(后者触发__eq__)
看懂关键字节码的实用信号
不必背全表,盯住这几个:
-
LOAD_GLOBAL比LOAD_FAST慢得多 —— 提示变量没声明为局部(比如循环里反复读 module-level 常量) - 连续多个
LOAD_CONST+BUILD_TUPLE—— 可能是元组字面量构造,若在循环内,考虑提前定义常量 - 出现
CALL_FUNCTION或CALL_METHOD—— 尤其参数 > 2 时开销明显;若调用的是纯计算函数(如math.sqrt),确认没被意外替换成同名但更重的方法 - 有
POP_JUMP_IF_FALSE后紧跟大量字节码 —— 说明 if 分支未被剪枝,且 else 块可能被频繁执行
示例:对比 if x is None: 和 if x == None:,前者只产生 IS_OP,后者会多出 COMPARE_OP 和潜在的 CALL_METHOD(如果 x 是自定义类)。
dis 本身不支持跨函数内联分析,怎么办?
dis.dis() 默认不递归展开嵌套函数或 lambda。容易误判“热点”位置 —— 表面看外层函数字节码少,实际耗时全在被调用的闭包里。
- 手动对每个可疑子函数调用
dis.dis(sub_func) - 用
dis.get_instructions(func)获取指令流,再按opcode过滤,比如统计所有CALL_FUNCTION的目标名:list(filter(lambda i: i.opname == 'CALL_FUNCTION', dis.get_instructions(my_func)))
- 配合
sys.settrace()或line_profiler定位真实热点后,再针对性dis—— 避免在 I/O 或锁等待上浪费字节码分析精力
真正影响性能的,往往不是单条字节码,而是它们组合暴露的 Python 运行时行为:对象创建频次、属性查找路径、方法解析缓存失效。dis 是显微镜,不是蓝图。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











