Arganzheng's Blog

stay hungry, stay foolish

C++ 在 AI-Infra(01 上):编译模型——从一个 .cpp 到可加载的 .so

The Compilation Model: From a .cpp to a Loadable .so

import torch 背后,Python 解释器真正加载的第一个 C 语言文件只有 15 行。先说清楚一件事:import 导入的不一定是 .py 文件。目录里如果放的是一个编译好的共享库 _C.cpython-312-x86_64-linux-gnu.so,同一条 import 语句会 dlopen 它并调用其中一个名为 PyInit__C 的 C 函数拿到模块对象——这是 Python 系列第一篇第四章讲的 ExtensionFileLoader。本篇从 C++ 这一侧接着讲:那个 PyInit__C 在哪、它编成了什么、又拉起了什么。它在 torch/csrc/stub.c,全文如下: #include <Python.h> extern ...

C++ 在 AI-Infra:从对象模型到算子扩展(总纲)

C++ for AI-Infra, from the Object Model to Operator Extensions

内容简介 《C++ 在 AI-Infra:从对象模型到算子扩展》是一组共八讲、九篇的系列文章(第一讲拆成上下两篇),面向有 Java 或其他托管语言背景、准备阅读和修改 PyTorch、vLLM 等 AI-Infra 项目 C++ 层的工程师。 它不是一本 C++ 教材。C++ 有几十个特性、几千页标准,而 AI-Infra 项目真正大量使用的只是一个子集。这个系列只讲这个子集,并且每一个特性都从一个具体问题引出: PyTorch 和 vLLM 的 C++ 源码里,这段代码为什么这样写?1 一个典型例子。PyTorch 的 at::Tensor 在 Python 侧看起来是一个普通对象,但它的 C++ 定义大致是: class TensorBase...

Python 在 AI-Infra(08):系列总结与通关自测

Python for AI-Infra: Series Recap and Final Self-Test

七篇正文回答了一个问题:在 AI-Infra 系统里,Python 不承担最重的计算,那它到底承担什么,为此需要掌握它的哪些机制。答案是组织、调度、扩展、观测和交付——Python 是控制平面和胶水层。第一篇讲代码是怎么运转的,第二篇讲类型如何被表达与消费,第三篇讲任务如何协作,第四篇讲系统如何扩展而不失控,第五篇讲内存如何被占用与回收,第六篇讲怎么确认它真的对、出了问题怎么查,第七篇讲怎么把它可复现地交付出去。前六篇解决”写对”,第七篇解决”交付”。 本文不讲新内容,做三件事:把七篇压成一张表与七段回顾,把贯穿七篇的几条线拎出来,然后给一套三段式的通关自测——判断与计算、跨篇综合、面试题。各篇末尾的自测检验的是”这一篇读懂了没有”,这里检验的是”七篇能不能连起来...

Python 在 AI-Infra(07):项目工程化与生产交付

Python Project Engineering and Production Delivery

前面几篇讨论的都是”代码本身”:语言机制、类型与数据契约、并发、元编程、内存、测试与调试。这一篇讨论一件不同的事——怎么把这些代码变成一个可以交付的东西。 对 Java 开发者来说,这套东西是现成的: 工程化工具链:Java 与 Python 的对应 关注点 Java Python 依赖管理与构建 Maven / Gradle pyproject.toml + pip / uv / Poetry 环境隔离 JVM + classpath 天然隔离 venv / conda(必须显式创建) ...

Python 在 AI-Infra(06):单元测试、问题定位与调试实践

Python Unit Testing, Troubleshooting, and Debugging

两个都发生过的场景。第一个:一次改动通过了 CI 里全部四百个测试,上线半小时后服务 OOM——测试里把模型客户端 mock 成了一个立刻返回的函数,真实客户端在超时后会把请求留在一个重试队列里,而那个队列没有上限;测试全绿,因为它测的根本不是上线跑的那条路。第二个:一个训练任务的进程”卡住”了——CPU 0%、GPU 0%、日志停在四十分钟前、进程没有崩,kill -9 之前没有人知道它停在哪一行;事后发现是一个集合通信在等一个已经退出的 rank。 AI-Infra 的 Python 代码——异步调度、动态 batch、多后端切换、插件加载、资源生命周期——正确性没法靠读出来:逻辑对但时序错、类型对但运行时的值不对、不报错但内存一直涨、不崩溃但卡死。这些在 c...

Python 在 AI-Infra(05):内存管理与优化

Python Memory Management and Optimization

一个推理服务上线后,进程的常驻内存(RSS)每小时稳定上涨 300 MB,三天后被 OOM killer 杀掉。值班的人打开 tracemalloc,它说 Python 对象总共只涨了 20 MB。剩下的 280 MB 在哪?这类问题在 AI-Infra 服务里几乎是必修课,答案通常不在任何一行”算得慢”的 Python 代码里,而在几个看不见的地方:NumPy 数组的数据缓冲区不算 Python 对象,tracemalloc 默认看不到它;一个请求级的 dict 缓存没有上限,每个 key 挂着一段 tensor;一次 arr[mask] 以为是视图其实是拷贝;pymalloc 把释放的小对象内存留在自己的 arena 里不还给操作系统;CUDA 的缓存分配器同样...

Python 在 AI-Infra(04):Python的动态机制及工程实践

Python Dynamic Mechanisms and Practice

vllm.LLM(model="Qwen/Qwen3-8B") 这一行背后,vLLM 要在几十种模型架构、十几种量化格式、几种 attention 后端、几种调度策略里各选一个,还要允许别人在不改 vLLM 源码的情况下接入自己的模型或量化方法。如果用 if model_type == "qwen3": ... elif ... 来写,核心代码会变成一张几百行的 if-else 表,每接一个新模型都要改它。实际的做法是:模型类用一个装饰器把自己登记进一个注册表,量化方法按名字从表里取,后端在启动时按硬件条件动态 import——运行时用字符串找到类、用装饰器改写函数、用元类 / __init_subclass__ 在定义时介入。这些就是 Python 的动态机制。 ...

Python 在 AI-Infra(03):并发、异步与任务协作

Python Concurrency, Asynchrony, and Task Collaboration in AI Systems

先看一个真实形状的故障。一个用 FastAPI 写的推理网关,后面接着 GPU 上的推理引擎:压测到每秒 200 个请求时 p99 延迟涨到 8 秒,而 GPU 利用率只有 30%——算力明明空着,请求却在排队。第一反应是”多开几个线程”,没用:请求处理里有一段纯 Python 的 tokenize 与 JSON 拼装,CPython 的全局解释器锁让 32 个线程也只能一个一个地算。换成多进程,吞吐上去了,但每个进程各自加载了一份 5 GB 的模型,显存翻了几倍。改成 asyncio,网络那一段快了,可是有人在协程里调了一个同步的 requests.get,整条事件循环被卡住 3 秒,所有请求一起等。 这个故障里的每一步都不是”不会用线程 / 进程 / asyn...

Python 在 AI-Infra(02 下):类型系统——数据契约设计

Type System III: Data Contract Design

前两篇沿着”类型信息如何流动”展开:上篇讲怎么写(typing 工具箱),中篇讲怎么分发、谁消费(存根、mypy、运行时读注解)。本篇的组织轴切换——不再讨论类型信息本身,而是讨论用这些能力去构建什么:数据契约。 所谓数据契约,就是对”一组数据长什么样”的正式约定。在 AI Infra 里它无处不在:推理服务的请求体与响应体、训练任务的配置文件、模型仓库里的元数据、Worker 之间传递的消息。Java 工程师熟悉的 Bean Validation、Jackson、@ConfigurationProperties 在这里对应的是 @dataclass、Pydantic BaseModel、BaseSettings——但它们不是一一对应的翻译,而是同一份类型注解的三...

Python 在 AI-Infra(02 中):类型系统——类型信息的分发与消费

Type System II: Distributing and Consuming Type Information

上篇讲了怎么写类型:typing 工具箱里每个工具表达哪一种类型意图。但写下来的注解在 Python 里默认什么都不发生——解释器只是把它存进 __annotations__,不检查、不转换。类型信息要产生作用,得有人读它。本篇讲的就是这条链路的中间两段: 分发:我写的注解怎么随包发给别人?没有源码注解的库(torch._C 这类 C 扩展)怎么提供类型信息?——.pyi 存根、typeshed、types-*、py.typed / PEP 561; 消费:谁在读注解、什么时候读、读到之后做什么?——静态一侧是 mypy / pyright 在开发时推理与检查;动态一侧是 get_type_hints()、@dataclass 的代码生成、Pydanti...

×