Arganzheng's Blog

stay hungry, stay foolish

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...

Python 在 AI-Infra(02 上):类型系统——类型表达与 typing 工具箱

Type System I: Type Expression and the typing Toolbox

Python 是动态类型语言,但这不意味着”无类型”。自 Python 3.5 引入 typing 模块以来,类型注解已经从”可选装饰”演变为大型项目的工程标配。PyTorch、vLLM、FastAPI 等 AI Infra 项目大量依赖类型系统的高级特性。 与 Java 把类型声明、编译检查、.class 文件携带类型信息、运行时反射合为一体不同,Python 的类型系统由两个协作层构成——类型信息提供层和类型信息消费层。而在这两层之上,还有一层工程落地:用它们构建数据契约。 本系列这一讲围绕的核心问题是: Python 的类型信息从哪里来、被谁消费,又如何在系统边界上落成可执行的数据契约?1 这条链路太长,一篇放不下,所以拆成三篇:上篇讲类型表达...

Python 在 AI-Infra(01 下):对象如何工作——对象模型、协议、装饰器与生成器

How Objects Work — Object Model, Protocols, Decorators and Generators

上篇讲的是代码怎么跑:源码编译成 code object,调用创建帧,名称在帧里按 LEGB 解析,import 找到并执行模块,异常沿帧链传播。本篇讲对象怎么工作——上篇开头那七行里剩下的五行: model(x):走的是 call,中间可能插入 hooks for batch in loader:迭代协议 + 生成器的暂停与恢复 with torch.inference_mode():上下文管理协议:进入时改状态、退出时恢复 @register(“cuda”):装饰器在模块导入时执行,注册表能否填上取决于谁导入了它 self.linear = nn.Linear(4, 4):setattr 拦截赋值,把子模块登记到 _modules 这...

×