【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系

文章目录


C++ 与 Python:类型、编译器/解释器与 CPU 的关系

一、问题从哪来:为什么 trellis2 要 Pyhon 和 C++ 结合用?

在工程里常见一种分工:性能关键路径用 C++ (例如网格扫描、体素哈希、矩阵求解),胶水、调参、训练用 Python

同一套"密集循环 + 大量内存访问"的逻辑,用 Python 写往往会慢一个数量级以上。

原因可以归结为:类型在何时、以何种方式被确定,决定了代码最终如何变成 CPU 能执行的形式。


二、CPU 只认什么

CPU 不认"C++"或"Python",也不认"整数""浮点数"这些概念。

它只执行机器码 :一串二进制指令,例如"从某地址读 8 字节""做一次浮点加""把结果写回某地址""根据条件跳转"。

任何高级语言要跑起来,最终都必须变成这样一串指令;差别在于:谁、在什么时候、以什么方式完成这种转换。


三、类型扮演的角色

类型是语言层面的约定:规定"一块数据是什么、占多少空间、能参与哪些运算"。

  • C++ 里,变量在写代码时就有明确类型(intfloatEigen::Vector3f 等),且一般不会在运行时变成别的类型。
  • Python 里,同一个名字可以先指向一个整数,再指向一个列表,再指向任意对象;类型是运行时附着在对象上的属性。

因此:

  • 类型越早、越固定 ,就越容易在编译时把"人类写的运算"直接对应到某几条 CPU 指令。
  • 类型越晚、越不固定 ,就只能在运行时再查"当前是什么类型、该调用哪段实现",无法在编译时生成"一步到位"的机器码。

四、C++:编译时定类型,一次生成机器码

  • 源码里每个变量都有静态类型(显式写出或由编译器推导,但编译后固定)。
  • 编译器在编译时就知道:某次加法是"两个 float 相加",某次访问是"从某结构体偏移 8 字节读 4 个 int"。
  • 于是编译器可以直接生成对应的机器指令序列(用哪条 CPU 指令、数据放在哪、如何寻址),写入可执行文件。
  • 运行时 :CPU 加载这段机器码,按指令执行即可,不再需要查类型、选实现
  • 类型在 C++ 里的作用,可以理解为:给编译器足够的信息,让它一次性把"带类型的运算"翻译成 CPU 指令

五、Python:运行时定类型,解释器每次"查表"

  • 源码里不写变量类型,同一个名字在不同时刻可以指向不同类型的对象。
  • 因此编译时 无法确定"这个 + 是整数加、浮点加还是列表拼接",编译器(严格说是 Python 的"编译"前端)只能生成字节码(一种中间表示),而不是最终给 CPU 的机器码。
  • 运行时解释器 (如 CPython)执行字节码。每次遇到一次运算(例如 a + b),解释器会:
    • 查看当前 ab 指向的对象的类型;
    • 根据类型查找对应的实现(例如 int__add__);
    • 调用那段实现(通常是用 C 写的,最终会变成 CPU 指令)。
  • 所以:类型在 Python 里是运行时的属性,每次运算都可能涉及"查类型、选实现";CPU 直接跑的是解释器本身的机器码,而"Python 的加法"要经过解释器这一层间接才能变成 CPU 上的计算。

六、编译器 vs 解释器:谁在"对接"CPU?

角色 在 C++ 中 在 Python 中(以 CPython 为例)
类型 编译时确定、固定 运行时确定、可变
谁做翻译 编译器 解释器(+ 少量 JIT 可选)
何时翻译 编译时,一次性 运行时,按条解释 / 按需 JIT
产出 可直接执行的机器码 字节码;真正执行的是解释器的机器码
与 CPU 的关系 编译器产出 → CPU 直接执行 解释器(机器码)在 CPU 上跑,解释器再驱动"Python 运算"

可以简单记:

  • C++:类型 → 编译器 → 机器码 → CPU。
  • Python:类型(运行时)→ 解释器查类型、调实现 → 内部 C 等实现转为机器码 → CPU。

七、为什么"类型少且确定"就容易快?

  • C++:类型少且确定,编译器"准备充分"------在编译时就知道该用哪条指令、数据布局如何,因此可以生成紧凑、无额外分支的机器码;循环里没有"这次是 int 还是 float"的判断,CPU 只是重复执行同一段指令。
  • Python:类型"随便"、多变,编译时准备不了唯一的一种机器码,只能每次运算时再查类型、再选实现;在密集循环里,这种"查表 + 间接调用"的开销会随着迭代次数线性放大,所以同样逻辑往往比 C++ 慢一个数量级以上。

这不是说"Python 不能快",而是说:在通用、动态语义下,标准实现选择的是"解释 + 运行时查类型",而不是"编译成一种固定的机器码";若要对某段 Python 做极致优化,要么用 C/C++/Cython 写扩展,要么依赖 JIT(如 PyPy)在运行时根据观测到的类型再生成机器码,本质上都是在"减少每次运算时的类型查找与间接"。


八、小结:一句话串起来

  • CPU 只认机器码;类型是语言和编译器/解释器之间的约定,用来决定"如何把人类写的运算变成 CPU 能做的操作"。
  • C++ 用静态类型在编译时就定死布局与指令,编译器一次性生成机器码,CPU 直接执行。
  • Python 用动态类型在运行时才确定类型,解释器每次运算都要查类型、选实现,再通过底层 C 等代码间接变成 CPU 指令。
  • 因此:类型何时、如何确定,决定了是谁(编译器 vs 解释器)、在何时(编译时 vs 运行时)把代码变成 CPU 指令,也决定了密集计算能多接近"裸机器码"的执行效率。
相关推荐
Jerry2 小时前
LeetCode 1047. 删除字符串中的所有相邻重复项
算法
程序员脚趾4 小时前
GPT-5.6 上线:当 GPT 与 Codex 走向融合,AI 正从“回答问题”走向“完成工作”
人工智能
thubier(段新建)4 小时前
owtb 3pl 面向城市配送物流企业需求V0.2
大数据·人工智能
可编程芯片开发4 小时前
基于霍尔传感器和PID控制器的有功功率检测控制系统simulink建模与仿真
算法
To_OC4 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
Token炼金师5 小时前
模型的防线:Prompt 注入防御、越狱攻击与对齐、红队测试、价值观对齐、对抗样本鲁棒性、安全评测与边界 —— 模型安全六防
人工智能·红队测试·prompt 注入防御·越狱攻击与对齐·价值观对齐·对抗样本鲁棒性·安全评测与边界
blueman88885 小时前
Qt5通过vcpkg中调用时,在debug模式下调试时总是调用release的plugins文件夹中的dll
c++·qt·cmake
嘘神秘用5 小时前
布:AI 驱动的 Redis 客户端,更快、更直观
数据库·人工智能·redis
黒亱中旳5 小时前
Java AI 框架三国杀:Solon AI vs Spring AI vs LangChain4j 深度对比
java·人工智能·spring
小和尚同志5 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc