↓ Skip to main content

Posts

2026

从 CS336 到 ML Infra:理解 Arithmetic Intensity、Accelerator Intensity,以及 Compute Bound / Memory Bound

起因 # 这是 CS336 系列的又一篇。 之前那篇讨论了 FLOPs 到 Latency 之间的鸿沟:FLOPs 相同的两个 GEMM,latency 可能差几倍甚至十几倍。那篇文章的核心结论是 FLOPs 是 workload metric,不是 performance metric,并且引入了 Arithmetic Intensity 和 Roofline Model 作为分析工具。

从线上模型切换问题理解 PyTorch CUDA Caching Allocator:为什么显存管理需要理解生命周期

起因 # 我们的线上推理系统中存在大量模型——MLP、GNN、MoE,以及它们的各种组合。这个 workload 有几个重要特点: 单张 GPU 上可能同时存在多个模型; 很多模型的 architecture 相同或者高度相似; 模型会随着业务时间轴不断上线、下线和切换; 模型切换过程对 latency 比较敏感。 随着模型数量增加以及动态上下线越来越频繁,我们逐渐观察到两个问题:GPU memory fragmentation,以及 model loading / switching latency。

从 Bytes 到 Tokens:我在 CS336 中实现 BPE Tokenizer

·8149 words·17 mins
最近在做 Stanford CS336(Language Modeling from Scratch)的 Assignment 1,实现了一个 BPE Tokenizer。最初的 trainer 跑 TinyStories 要约 8 分钟,改完后降到约 36 秒。 耗时主要花在 merge 循环里。要判断哪些工作可以省,得先弄清每次 merge 改变了哪些统计量。下面先交代 BPE 的表示和合并规则,再回到这段实现。

从 CS336 的 xfail 看大型工程中的测试设计

·3497 words·7 mins
起因 # 最近在学 Stanford CS336(Language Modeling from Scratch),做 tokenizer 作业时,第一次在测试代码里见到了 pytest.mark.xfail。 具体是这段: 1@pytest.mark.xfail( 2 reason="Tokenizer.encode is expected to take more memory than allotted (1MB)." 3) 4def test_encode_memory_usage(): 5 """ 6 We expect this test to fail, since Tokenizer.encode is not expected to be memory efficient. 7 """ 8 tokenizer = get_tokenizer_from_vocab_merges_path( 9 vocab_path=VOCAB_PATH, 10 merges_path=MERGES_PATH, 11 ) 12 with open(FIXTURES_PATH / "tinystories_sample_5M.txt") as f: 13 contents = f.read() 14 _ = _encode(tokenizer, contents) 我当时第一反应是困惑:既然作者明确知道 encode 会超过 1MB,为什么不直接写成 assert memory > 1MB?或者干脆删掉?为什么要提交一个「预期会失败」的测试?

写在毕业八年之后

·3343 words·7 mins
好久没在博客上写东西了。确实是因为比较忙,但还是要写一写、做一些输出,不然人的表达能力是会退化的。 人生目标 # 上周五研发部开 all-hands meeting,有人问老板他的人生目标是什么。问这个问题的背景是,和他一样达到财务自由的人,有一些已经躺平享受生活了。

2023

2022

浅谈 Cpp Value Categories

·925 words·2 mins
背景 # 每一个cpp expression都有一个type 和 value category 属性 前者大家都比较了解,但是后者却常常被忽视 Value Categories in the c language # cpp是由c语言发展而来,因此这里先介绍c 语言中的value category

redis学习笔记

·1304 words·3 mins
最近在做一个智能算力的项目,其中需要用到redis维护某个全局的时间窗口 资料 # https://redis.io/docs/about/ 极客时间Redis核心技术与实战 复杂数据结构的表示方式 # 学习过程中唯一让我迷惑的是一个嵌套的数据结构要如何表示。比如我想表示一个key为string, value为List的Hash 按照c++的语法,也就是一个

[施工中] levelDB 代码阅读笔记 06 iterator

·1164 words·3 mins
接口 # 一个ABC,后面会继承这个类做各种实现 clean up function # 代码比较好懂,唯一让我感到疑惑的是 clean up function 这部分 1 2 // FIXME: 不太理解为什么需要很多个clean up function 3 // Cleanup functions are stored in a single-linked list. 4 // The list's head node is inlined in the iterator. 5 struct CleanupNode { 6 // True if the node is not used. Only head nodes might be unused. 7 bool IsEmpty() const { return function == nullptr; } 8 // Invokes the cleanup function. 9 void Run() { 10 assert(function != nullptr); 11 (*function)(arg1, arg2); 12 } 13 14 // The head node is used if the function pointer is not null. 15 CleanupFunction function; 16 void* arg1; 17 void* arg2; 18 CleanupNode* next; 19 }; 20 CleanupNode cleanup_head_; 21}; 从 table/iterator.cc 的析构函数中,可以看到在iterator析构的时候会依次调用多个clean up function