[{"data":1,"prerenderedAt":1947},["ShallowReactive",2],{"post-\u002Fposts\u002Fmini-llm-engine-from-scratch":3,"all-posts-nav":1674},{"id":4,"title":5,"body":6,"categories":1657,"date":1659,"description":79,"draft":1660,"extension":1661,"hidden":1660,"meta":1662,"navigation":278,"path":1663,"published":1660,"seo":1664,"stem":1665,"tags":1666,"__hash__":1673},"posts\u002Fposts\u002Fmini-llm-engine-from-scratch.md","从零实现 LLM 推理引擎：深挖 vLLM 的六大核心优化",{"type":7,"value":8,"toc":1633},"minimark",[9,27,30,35,43,46,49,51,55,58,63,70,80,87,93,147,151,157,160,162,166,170,181,184,190,195,395,402,482,488,490,494,503,506,512,517,707,712,718,720,724,733,739,745,759,923,926,928,932,941,944,950,963,1124,1129,1135,1137,1141,1148,1154,1160,1171,1173,1177,1186,1189,1195,1201,1212,1219,1221,1225,1231,1233,1237,1241,1280,1283,1289,1336,1346,1350,1353,1355,1359,1362,1370,1392,1400,1408,1416,1418,1422,1511,1517,1519,1523,1526,1554,1563,1629],[10,11,12],"blockquote",{},[13,14,15,16,23,26],"p",{},"本文对应 GitHub 项目：",[17,18,22],"a",{"href":19,"rel":20},"https:\u002F\u002Fgithub.com\u002Fliangqianxing\u002Fmini-llm-engine",[21],"nofollow","mini-llm-engine",[24,25],"br",{},"\n代码量：~3500 行 Python · 67 个单元测试 · 全部通过 ✅",[28,29],"hr",{},[31,32,34],"h2",{"id":33},"一为什么要自己实现","一、为什么要自己实现？",[13,36,37,38,42],{},"用 vLLM 很容易，一行 ",[39,40,41],"code",{},"from vllm import LLM"," 就能跑起来。但面试时被问到\"vLLM 为什么比 HuggingFace 快 20 倍\"，能说清楚的人并不多。",[13,44,45],{},"我花了两周时间，从零实现了 vLLM 的核心调度和内存管理逻辑，把六个关键优化全部用 Python 写了一遍，并且每个都配了 benchmark。",[13,47,48],{},"这篇文章记录整个过程——不只是\"是什么\"，而是**\"为什么这么设计\"**。",[28,50],{},[31,52,54],{"id":53},"二传统推理有什么问题","二、传统推理有什么问题？",[13,56,57],{},"先理解痛点，才能理解优化的价值。",[59,60,62],"h3",{"id":61},"问题一显存碎片内存利用率只有-17","问题一：显存碎片（内存利用率只有 17%）",[13,64,65,66,69],{},"传统服务框架在请求开始时就预留 ",[39,67,68],{},"max_seq_len"," 个 token 的 KV cache：",[71,72,77],"pre",{"className":73,"code":75,"language":76},[74],"language-text","请求 A（实际生成 20 tokens）：\n[prompt KV | generated KV | pad | pad | pad | pad | ...]\n ← 20 tokens 实际使用 →  ← 108 tokens 浪费 →\n","text",[39,78,75],{"__ignoreMap":79},"",[13,81,82,83,86],{},"如果 ",[39,84,85],{},"max_seq_len=128","，实际生成 20 个 token，显存利用率只有 15%。不同长度的请求导致显存碎片严重，多余内存无法被其他请求复用。",[13,88,89],{},[90,91,92],"strong",{},"实测数据（200 个请求，max_tokens=128，eos_prob=5%）：",[94,95,96,115],"table",{},[97,98,99],"thead",{},[100,101,102,106,109,112],"tr",{},[103,104,105],"th",{},"策略",[103,107,108],{},"利用率",[103,110,111],{},"碎片率",[103,113,114],{},"最大并发",[116,117,118,133],"tbody",{},[100,119,120,124,127,130],{},[121,122,123],"td",{},"静态预分配",[121,125,126],{},"17–28%",[121,128,129],{},"72–83%",[121,131,132],{},"~8 条",[100,134,135,138,141,144],{},[121,136,137],{},"Paged KV Cache",[121,139,140],{},"45–65%",[121,142,143],{},"35–55%",[121,145,146],{},"~38 条",[59,148,150],{"id":149},"问题二静态批处理的-gpu-空闲","问题二：静态批处理的 GPU 空闲",[71,152,155],{"className":153,"code":154,"language":76},[74],"Batch [A, B, C, D]（batch_size=4）：\n\nStep 1  : A B C D  ← 全部 decode\nStep 10 : A B _ _  ← C、D 已完成，但 A、B 还在跑，两个槽位空着浪费！\nStep 30 : A _ _ _  ← 只剩 A，75% 的算力在等待\nStep 40 : 完成，才能开始下一批\n",[39,156,154],{"__ignoreMap":79},[13,158,159],{},"短序列完成后，GPU 槽位空着等最长的序列，算力白白浪费。",[28,161],{},[31,163,165],{"id":164},"三六大优化的实现原理","三、六大优化的实现原理",[59,167,169],{"id":168},"_31-paged-kv-cache虚拟内存分页","3.1 Paged KV Cache（虚拟内存分页）",[13,171,172,175,176],{},[90,173,174],{},"论文","：",[17,177,180],{"href":178,"rel":179},"https:\u002F\u002Farxiv.org\u002Fabs\u002F2309.06180",[21],"PagedAttention, SOSP 2023",[13,182,183],{},"核心思路类比 OS 的虚拟内存：",[71,185,188],{"className":186,"code":187,"language":76},[74],"传统：  [连续大块预分配，max_seq_len 对齐]\n\nPaged：[block₀][block₁][block₂]...  ← 每块 16 个 token\n       ↑ 用完一块才分配下一块，结束立即回收\n",[39,189,187],{"__ignoreMap":79},[13,191,192],{},[90,193,194],{},"关键数据结构：",[71,196,200],{"className":197,"code":198,"language":199,"meta":79,"style":79},"language-python shiki shiki-themes github-dark-dimmed github-light","@dataclass\nclass PhysicalBlock:\n    block_id: int        # GPU 显存中的索引\n    ref_count: int = 0   # 引用计数（>1 时触发 CoW）\n    content_hash: int = None  # 块内容哈希（用于 Prefix Cache）\n\nclass BlockAllocator:\n    # O(1) 分配\u002F回收：deque 作为空闲链表\n    _free_blocks: Deque[PhysicalBlock]\n    \n    def allocate(self) -> PhysicalBlock:\n        return self._free_blocks.popleft()   # O(1)\n    \n    def free(self, block: PhysicalBlock) -> None:\n        block.ref_count -= 1\n        if block.ref_count == 0:\n            self._free_blocks.append(block)  # O(1)\n","python",[39,201,202,211,226,240,257,273,280,290,296,302,308,320,335,340,356,368,384],{"__ignoreMap":79},[203,204,207],"span",{"class":205,"line":206},"line",1,[203,208,210],{"class":209},"saVmf","@dataclass\n",[203,212,214,218,222],{"class":205,"line":213},2,[203,215,217],{"class":216},"s6PUj","class",[203,219,221],{"class":220},"sqRhv"," PhysicalBlock",[203,223,225],{"class":224},"ssh_m",":\n",[203,227,229,232,236],{"class":205,"line":228},3,[203,230,231],{"class":224},"    block_id: ",[203,233,235],{"class":234},"swcJU","int",[203,237,239],{"class":238},"sgHix","        # GPU 显存中的索引\n",[203,241,243,246,248,251,254],{"class":205,"line":242},4,[203,244,245],{"class":224},"    ref_count: ",[203,247,235],{"class":234},[203,249,250],{"class":216}," =",[203,252,253],{"class":234}," 0",[203,255,256],{"class":238},"   # 引用计数（>1 时触发 CoW）\n",[203,258,260,263,265,267,270],{"class":205,"line":259},5,[203,261,262],{"class":224},"    content_hash: ",[203,264,235],{"class":234},[203,266,250],{"class":216},[203,268,269],{"class":234}," None",[203,271,272],{"class":238},"  # 块内容哈希（用于 Prefix Cache）\n",[203,274,276],{"class":205,"line":275},6,[203,277,279],{"emptyLinePlaceholder":278},true,"\n",[203,281,283,285,288],{"class":205,"line":282},7,[203,284,217],{"class":216},[203,286,287],{"class":220}," BlockAllocator",[203,289,225],{"class":224},[203,291,293],{"class":205,"line":292},8,[203,294,295],{"class":238},"    # O(1) 分配\u002F回收：deque 作为空闲链表\n",[203,297,299],{"class":205,"line":298},9,[203,300,301],{"class":224},"    _free_blocks: Deque[PhysicalBlock]\n",[203,303,305],{"class":205,"line":304},10,[203,306,307],{"class":224},"    \n",[203,309,311,314,317],{"class":205,"line":310},11,[203,312,313],{"class":216},"    def",[203,315,316],{"class":209}," allocate",[203,318,319],{"class":224},"(self) -> PhysicalBlock:\n",[203,321,323,326,329,332],{"class":205,"line":322},12,[203,324,325],{"class":216},"        return",[203,327,328],{"class":234}," self",[203,330,331],{"class":224},"._free_blocks.popleft()   ",[203,333,334],{"class":238},"# O(1)\n",[203,336,338],{"class":205,"line":337},13,[203,339,307],{"class":224},[203,341,343,345,348,351,354],{"class":205,"line":342},14,[203,344,313],{"class":216},[203,346,347],{"class":209}," free",[203,349,350],{"class":224},"(self, block: PhysicalBlock) -> ",[203,352,353],{"class":234},"None",[203,355,225],{"class":224},[203,357,359,362,365],{"class":205,"line":358},15,[203,360,361],{"class":224},"        block.ref_count ",[203,363,364],{"class":216},"-=",[203,366,367],{"class":234}," 1\n",[203,369,371,374,377,380,382],{"class":205,"line":370},16,[203,372,373],{"class":216},"        if",[203,375,376],{"class":224}," block.ref_count ",[203,378,379],{"class":216},"==",[203,381,253],{"class":234},[203,383,225],{"class":224},[203,385,387,390,393],{"class":205,"line":386},17,[203,388,389],{"class":234},"            self",[203,391,392],{"class":224},"._free_blocks.append(block)  ",[203,394,334],{"class":238},[13,396,397,398,401],{},"每个序列维护一张\"页表\"（",[39,399,400],{},"block_table","），把逻辑块编号映射到物理块：",[71,403,405],{"className":197,"code":404,"language":199,"meta":79,"style":79},"class Sequence:\n    block_table: Dict[int, PhysicalBlock]  # logical_idx → PhysicalBlock\n    logical_blocks: List[LogicalTokenBlock]\n    \n    def needs_new_block(self) -> bool:\n        return not self.logical_blocks or self.logical_blocks[-1].is_full\n",[39,406,407,416,429,434,438,453],{"__ignoreMap":79},[203,408,409,411,414],{"class":205,"line":206},[203,410,217],{"class":216},[203,412,413],{"class":220}," Sequence",[203,415,225],{"class":224},[203,417,418,421,423,426],{"class":205,"line":213},[203,419,420],{"class":224},"    block_table: Dict[",[203,422,235],{"class":234},[203,424,425],{"class":224},", PhysicalBlock]  ",[203,427,428],{"class":238},"# logical_idx → PhysicalBlock\n",[203,430,431],{"class":205,"line":228},[203,432,433],{"class":224},"    logical_blocks: List[LogicalTokenBlock]\n",[203,435,436],{"class":205,"line":242},[203,437,307],{"class":224},[203,439,440,442,445,448,451],{"class":205,"line":259},[203,441,313],{"class":216},[203,443,444],{"class":209}," needs_new_block",[203,446,447],{"class":224},"(self) -> ",[203,449,450],{"class":234},"bool",[203,452,225],{"class":224},[203,454,455,457,460,462,465,468,470,473,476,479],{"class":205,"line":275},[203,456,325],{"class":216},[203,458,459],{"class":216}," not",[203,461,328],{"class":234},[203,463,464],{"class":224},".logical_blocks ",[203,466,467],{"class":216},"or",[203,469,328],{"class":234},[203,471,472],{"class":224},".logical_blocks[",[203,474,475],{"class":216},"-",[203,477,478],{"class":234},"1",[203,480,481],{"class":224},"].is_full\n",[13,483,484,487],{},[90,485,486],{},"生命周期","：请求开始时按需分配块 → decode 时若当前块满则申请新块 → 请求完成时批量回收所有块。",[28,489],{},[59,491,493],{"id":492},"_32-continuous-batching连续批处理","3.2 Continuous Batching（连续批处理）",[13,495,496,175,498],{},[90,497,174],{},[17,499,502],{"href":500,"rel":501},"https:\u002F\u002Fwww.usenix.org\u002Fconference\u002Fosdi22\u002Fpresentation\u002Fyu",[21],"Orca, OSDI 2022",[13,504,505],{},"每步结束后立即检查完成的序列，有空槽就从等待队列拉入新请求：",[71,507,510],{"className":508,"code":509,"language":76},[74],"Step N:   [A:decode] [B:decode] [C:decode]\nStep N+1: C 完成 → 立即拉入 D\n          [A:decode] [B:decode] [D:prefill]\nStep N+2: [A:decode] [B:decode] [D:decode]\n",[39,511,509],{"__ignoreMap":79},[13,513,514],{},[90,515,516],{},"调度器核心逻辑（简化版）：",[71,518,520],{"className":197,"code":519,"language":199,"meta":79,"style":79},"def schedule(self) -> SchedulerOutput:\n    # 1. 为 running 序列分配 decode slot（内存不够就抢占）\n    for seq_group in self.running:\n        for seq in seq_group.get_seqs(RUNNING):\n            if not self.kv_cache.can_append_slot(seq):\n                self._preempt(seq_group, output)  # OOM → 抢占\n                continue\n            self.kv_cache.append_slot(seq)\n            output.decode_seqs.append(seq)\n    \n    # 2. 从 waiting 拉入新请求（prefill）\n    while self.waiting:\n        sg = self.waiting[0]\n        if not self.kv_cache.can_allocate(sg.seqs[0]):\n            break  # 显存不够，停止\n        self.kv_cache.allocate(sg.seqs[0])\n        output.prefill_chunks.append(...)\n        self.running.append(sg)\n",[39,521,522,533,538,554,573,585,596,601,608,613,617,622,632,651,667,675,688,699],{"__ignoreMap":79},[203,523,524,527,530],{"class":205,"line":206},[203,525,526],{"class":216},"def",[203,528,529],{"class":209}," schedule",[203,531,532],{"class":224},"(self) -> SchedulerOutput:\n",[203,534,535],{"class":205,"line":213},[203,536,537],{"class":238},"    # 1. 为 running 序列分配 decode slot（内存不够就抢占）\n",[203,539,540,543,546,549,551],{"class":205,"line":228},[203,541,542],{"class":216},"    for",[203,544,545],{"class":224}," seq_group ",[203,547,548],{"class":216},"in",[203,550,328],{"class":234},[203,552,553],{"class":224},".running:\n",[203,555,556,559,562,564,567,570],{"class":205,"line":242},[203,557,558],{"class":216},"        for",[203,560,561],{"class":224}," seq ",[203,563,548],{"class":216},[203,565,566],{"class":224}," seq_group.get_seqs(",[203,568,569],{"class":234},"RUNNING",[203,571,572],{"class":224},"):\n",[203,574,575,578,580,582],{"class":205,"line":259},[203,576,577],{"class":216},"            if",[203,579,459],{"class":216},[203,581,328],{"class":234},[203,583,584],{"class":224},".kv_cache.can_append_slot(seq):\n",[203,586,587,590,593],{"class":205,"line":275},[203,588,589],{"class":234},"                self",[203,591,592],{"class":224},"._preempt(seq_group, output)  ",[203,594,595],{"class":238},"# OOM → 抢占\n",[203,597,598],{"class":205,"line":282},[203,599,600],{"class":216},"                continue\n",[203,602,603,605],{"class":205,"line":292},[203,604,389],{"class":234},[203,606,607],{"class":224},".kv_cache.append_slot(seq)\n",[203,609,610],{"class":205,"line":298},[203,611,612],{"class":224},"            output.decode_seqs.append(seq)\n",[203,614,615],{"class":205,"line":304},[203,616,307],{"class":224},[203,618,619],{"class":205,"line":310},[203,620,621],{"class":238},"    # 2. 从 waiting 拉入新请求（prefill）\n",[203,623,624,627,629],{"class":205,"line":322},[203,625,626],{"class":216},"    while",[203,628,328],{"class":234},[203,630,631],{"class":224},".waiting:\n",[203,633,634,637,640,642,645,648],{"class":205,"line":337},[203,635,636],{"class":224},"        sg ",[203,638,639],{"class":216},"=",[203,641,328],{"class":234},[203,643,644],{"class":224},".waiting[",[203,646,647],{"class":234},"0",[203,649,650],{"class":224},"]\n",[203,652,653,655,657,659,662,664],{"class":205,"line":342},[203,654,373],{"class":216},[203,656,459],{"class":216},[203,658,328],{"class":234},[203,660,661],{"class":224},".kv_cache.can_allocate(sg.seqs[",[203,663,647],{"class":234},[203,665,666],{"class":224},"]):\n",[203,668,669,672],{"class":205,"line":358},[203,670,671],{"class":216},"            break",[203,673,674],{"class":238},"  # 显存不够，停止\n",[203,676,677,680,683,685],{"class":205,"line":370},[203,678,679],{"class":234},"        self",[203,681,682],{"class":224},".kv_cache.allocate(sg.seqs[",[203,684,647],{"class":234},[203,686,687],{"class":224},"])\n",[203,689,690,693,696],{"class":205,"line":386},[203,691,692],{"class":224},"        output.prefill_chunks.append(",[203,694,695],{"class":234},"...",[203,697,698],{"class":224},")\n",[203,700,702,704],{"class":205,"line":701},18,[203,703,679],{"class":234},[203,705,706],{"class":224},".running.append(sg)\n",[13,708,709],{},[90,710,711],{},"实测吞吐量提升（50 个请求，max_tokens=64，2ms\u002Fstep 模拟延迟）：",[71,713,716],{"className":714,"code":715,"language":76},[74],"Naive Batching    ：152 tok\u002Fs\nContinuous Batch  ：360 tok\u002Fs   🚀 2.4x\n",[39,717,715],{"__ignoreMap":79},[28,719],{},[59,721,723],{"id":722},"_33-chunked-prefill分块预填充","3.3 Chunked Prefill（分块预填充）",[13,725,726,175,728],{},[90,727,174],{},[17,729,732],{"href":730,"rel":731},"https:\u002F\u002Farxiv.org\u002Fabs\u002F2308.16369",[21],"Sarathi-Serve, OSDI 2024",[13,734,735,738],{},[90,736,737],{},"问题","：512 token 的长 prompt prefill 占据整个步骤，decode 序列被阻塞：",[71,740,743],{"className":741,"code":742,"language":76},[74],"Without：│──── prefill(512 tok) ────│decode│decode│...\n   With：│chunk(32)│decode│chunk(32)│decode│...│\n",[39,744,742],{"__ignoreMap":79},[13,746,747,750,751,754,755,758],{},[90,748,749],{},"实现","：序列新增 ",[39,752,753],{},"PREFILLING"," 状态和 ",[39,756,757],{},"num_prefilled_tokens"," 进度字段：",[71,760,762],{"className":197,"code":761,"language":199,"meta":79,"style":79},"class SequenceStatus(Enum):\n    WAITING    = auto()\n    PREFILLING = auto()  # ← 新增：长 prompt 分批处理中\n    RUNNING    = auto()\n    SWAPPED    = auto()  # ← 新增：KV cache 已卸载到 CPU\n    FINISHED   = auto()\n\nclass Sequence:\n    num_prefilled_tokens: int = 0   # 已处理的 prompt token 数\n    \n    def get_next_prefill_range(self, chunk_size: int):\n        start = self.num_prefilled_tokens\n        end = min(start + chunk_size, self.prompt_len)\n        return start, end\n",[39,763,764,780,791,804,813,825,835,839,847,861,865,879,891,916],{"__ignoreMap":79},[203,765,766,768,771,774,778],{"class":205,"line":206},[203,767,217],{"class":216},[203,769,770],{"class":220}," SequenceStatus",[203,772,773],{"class":224},"(",[203,775,777],{"class":776},"sr-dd","Enum",[203,779,572],{"class":224},[203,781,782,785,788],{"class":205,"line":213},[203,783,784],{"class":234},"    WAITING",[203,786,787],{"class":216},"    =",[203,789,790],{"class":224}," auto()\n",[203,792,793,796,798,801],{"class":205,"line":228},[203,794,795],{"class":234},"    PREFILLING",[203,797,250],{"class":216},[203,799,800],{"class":224}," auto()  ",[203,802,803],{"class":238},"# ← 新增：长 prompt 分批处理中\n",[203,805,806,809,811],{"class":205,"line":242},[203,807,808],{"class":234},"    RUNNING",[203,810,787],{"class":216},[203,812,790],{"class":224},[203,814,815,818,820,822],{"class":205,"line":259},[203,816,817],{"class":234},"    SWAPPED",[203,819,787],{"class":216},[203,821,800],{"class":224},[203,823,824],{"class":238},"# ← 新增：KV cache 已卸载到 CPU\n",[203,826,827,830,833],{"class":205,"line":275},[203,828,829],{"class":234},"    FINISHED",[203,831,832],{"class":216},"   =",[203,834,790],{"class":224},[203,836,837],{"class":205,"line":282},[203,838,279],{"emptyLinePlaceholder":278},[203,840,841,843,845],{"class":205,"line":292},[203,842,217],{"class":216},[203,844,413],{"class":220},[203,846,225],{"class":224},[203,848,849,852,854,856,858],{"class":205,"line":298},[203,850,851],{"class":224},"    num_prefilled_tokens: ",[203,853,235],{"class":234},[203,855,250],{"class":216},[203,857,253],{"class":234},[203,859,860],{"class":238},"   # 已处理的 prompt token 数\n",[203,862,863],{"class":205,"line":304},[203,864,307],{"class":224},[203,866,867,869,872,875,877],{"class":205,"line":310},[203,868,313],{"class":216},[203,870,871],{"class":209}," get_next_prefill_range",[203,873,874],{"class":224},"(self, chunk_size: ",[203,876,235],{"class":234},[203,878,572],{"class":224},[203,880,881,884,886,888],{"class":205,"line":322},[203,882,883],{"class":224},"        start ",[203,885,639],{"class":216},[203,887,328],{"class":234},[203,889,890],{"class":224},".num_prefilled_tokens\n",[203,892,893,896,898,901,904,907,910,913],{"class":205,"line":337},[203,894,895],{"class":224},"        end ",[203,897,639],{"class":216},[203,899,900],{"class":234}," min",[203,902,903],{"class":224},"(start ",[203,905,906],{"class":216},"+",[203,908,909],{"class":224}," chunk_size, ",[203,911,912],{"class":234},"self",[203,914,915],{"class":224},".prompt_len)\n",[203,917,918,920],{"class":205,"line":342},[203,919,325],{"class":216},[203,921,922],{"class":224}," start, end\n",[13,924,925],{},"效果：短请求的 TTFT 不再受长 prompt 拖累。",[28,927],{},[59,929,931],{"id":930},"_34-prefix-caching前缀缓存-copy-on-write","3.4 Prefix Caching（前缀缓存 \u002F Copy-on-Write）",[13,933,934,175,936],{},[90,935,174],{},[17,937,940],{"href":938,"rel":939},"https:\u002F\u002Farxiv.org\u002Fabs\u002F2312.07104",[21],"SGLang RadixAttention",[13,942,943],{},"当多个请求共享相同 system prompt，KV cache 只需计算一次：",[71,945,948],{"className":946,"code":947,"language":76},[74],"Req 1：[system prompt KV 256 tok] [user query 1 KV]  ← 全部计算\nReq 2：[system prompt KV 256 tok] [user query 2 KV]  ← 前 256 tok 直接复用！\n       ^────── ref_count=2，CoW 保护 ──────^\n",[39,949,947],{"__ignoreMap":79},[13,951,952,954,955,958,959,962],{},[90,953,749],{},"：以满块的 ",[39,956,957],{},"content_hash"," 为 key，命中则 ",[39,960,961],{},"ref_count+1"," 直接引用：",[71,964,966],{"className":197,"code":965,"language":199,"meta":79,"style":79},"class PrefixCache:\n    _cache: Dict[int, PhysicalBlock]  # hash → PhysicalBlock\n    \n    def lookup(self, content_hash: int) -> Optional[PhysicalBlock]:\n        block = self._cache.get(content_hash)\n        if block:\n            block.ref_count += 1   # 共享引用\n        return block\n    \n    def cow_if_needed(self, block: PhysicalBlock):\n        if block.ref_count \u003C= 1:\n            return block, False  # 独占，安全写入\n        # 触发 CoW：分配新块，旧块 ref_count-1\n        new_block = self.allocator.allocate()\n        block.ref_count -= 1\n        return new_block, True\n",[39,967,968,977,989,993,1008,1020,1027,1041,1048,1052,1062,1075,1089,1094,1106,1114],{"__ignoreMap":79},[203,969,970,972,975],{"class":205,"line":206},[203,971,217],{"class":216},[203,973,974],{"class":220}," PrefixCache",[203,976,225],{"class":224},[203,978,979,982,984,986],{"class":205,"line":213},[203,980,981],{"class":224},"    _cache: Dict[",[203,983,235],{"class":234},[203,985,425],{"class":224},[203,987,988],{"class":238},"# hash → PhysicalBlock\n",[203,990,991],{"class":205,"line":228},[203,992,307],{"class":224},[203,994,995,997,1000,1003,1005],{"class":205,"line":242},[203,996,313],{"class":216},[203,998,999],{"class":209}," lookup",[203,1001,1002],{"class":224},"(self, content_hash: ",[203,1004,235],{"class":234},[203,1006,1007],{"class":224},") -> Optional[PhysicalBlock]:\n",[203,1009,1010,1013,1015,1017],{"class":205,"line":259},[203,1011,1012],{"class":224},"        block ",[203,1014,639],{"class":216},[203,1016,328],{"class":234},[203,1018,1019],{"class":224},"._cache.get(content_hash)\n",[203,1021,1022,1024],{"class":205,"line":275},[203,1023,373],{"class":216},[203,1025,1026],{"class":224}," block:\n",[203,1028,1029,1032,1035,1038],{"class":205,"line":282},[203,1030,1031],{"class":224},"            block.ref_count ",[203,1033,1034],{"class":216},"+=",[203,1036,1037],{"class":234}," 1",[203,1039,1040],{"class":238},"   # 共享引用\n",[203,1042,1043,1045],{"class":205,"line":292},[203,1044,325],{"class":216},[203,1046,1047],{"class":224}," block\n",[203,1049,1050],{"class":205,"line":298},[203,1051,307],{"class":224},[203,1053,1054,1056,1059],{"class":205,"line":304},[203,1055,313],{"class":216},[203,1057,1058],{"class":209}," cow_if_needed",[203,1060,1061],{"class":224},"(self, block: PhysicalBlock):\n",[203,1063,1064,1066,1068,1071,1073],{"class":205,"line":310},[203,1065,373],{"class":216},[203,1067,376],{"class":224},[203,1069,1070],{"class":216},"\u003C=",[203,1072,1037],{"class":234},[203,1074,225],{"class":224},[203,1076,1077,1080,1083,1086],{"class":205,"line":322},[203,1078,1079],{"class":216},"            return",[203,1081,1082],{"class":224}," block, ",[203,1084,1085],{"class":234},"False",[203,1087,1088],{"class":238},"  # 独占，安全写入\n",[203,1090,1091],{"class":205,"line":337},[203,1092,1093],{"class":238},"        # 触发 CoW：分配新块，旧块 ref_count-1\n",[203,1095,1096,1099,1101,1103],{"class":205,"line":342},[203,1097,1098],{"class":224},"        new_block ",[203,1100,639],{"class":216},[203,1102,328],{"class":234},[203,1104,1105],{"class":224},".allocator.allocate()\n",[203,1107,1108,1110,1112],{"class":205,"line":358},[203,1109,361],{"class":224},[203,1111,364],{"class":216},[203,1113,367],{"class":234},[203,1115,1116,1118,1121],{"class":205,"line":370},[203,1117,325],{"class":216},[203,1119,1120],{"class":224}," new_block, ",[203,1122,1123],{"class":234},"True\n",[13,1125,1126],{},[90,1127,1128],{},"实测（30 个请求，48 token 共享前缀 + 16 token 独有后缀）：",[71,1130,1133],{"className":1131,"code":1132,"language":76},[74],"缓存命中率：71.3%\n显存节省：~75% 的前缀 KV cache 块被共享，无需重复分配\n",[39,1134,1132],{"__ignoreMap":79},[28,1136],{},[59,1138,1140],{"id":1139},"_35-cpu-swap显存卸载","3.5 CPU Swap（显存卸载）",[13,1142,1143,1144,1147],{},"当 GPU 显存不足，传统做法是丢弃被抢占序列的 KV cache，等轮到它时重新 prefill（代价高）。",[90,1145,1146],{},"CPU Swap"," 将 KV cache 临时卸载到 CPU RAM：",[71,1149,1152],{"className":1150,"code":1151,"language":76},[74],"GPU OOM：swap_out(seq) → GPU 块释放，映射记录在 CPU 字典\n         swap_in(seq)  → 分配新 GPU 块，从 CPU 字典恢复映射\n                       ← 避免昂贵的重新 prefill ←\n",[39,1153,1151],{"__ignoreMap":79},[13,1155,1156,1159],{},[90,1157,1158],{},"权衡","：PCIe 带宽（GPU↔CPU ~16 GB\u002Fs）远低于 GPU 显存带宽（~2 TB\u002Fs），swap 延迟约为 GPU 内部操作的 100 倍。但对长序列（512+ tokens）来说，swap 延迟 \u003C\u003C 重新 prefill 延迟。",[13,1161,1162,1163,1166,1167,1170],{},"这与 vLLM 的 ",[39,1164,1165],{},"preemption_mode=\"swap\""," 完全对应（另一模式是 ",[39,1168,1169],{},"\"recompute\"","）。",[28,1172],{},[59,1174,1176],{"id":1175},"_36-speculative-decoding投机解码","3.6 Speculative Decoding（投机解码）",[13,1178,1179,175,1181],{},[90,1180,174],{},[17,1182,1185],{"href":1183,"rel":1184},"https:\u002F\u002Farxiv.org\u002Fabs\u002F2211.17192",[21],"Leviathan et al., ICML 2023",[13,1187,1188],{},"标准解码每步只生成 1 个 token（memory-bound，GPU 计算利用率低）。投机解码利用并行验证加速：",[71,1190,1193],{"className":1191,"code":1192,"language":76},[74],"Step 1 (Draft)  ：小模型连续生成 K=4 个候选 token [t₁, t₂, t₃, t₄]\nStep 2 (Verify) ：大模型并行验证所有 K 个（等价于 1 次 prefill）\nStep 3 (Accept) ：t₁✓ t₂✓ t₃✗ → 接受 [t₁, t₂]，用大模型的纠正 token\n",[39,1194,1192],{"__ignoreMap":79},[13,1196,1197,1198],{},"期望每步接受的 token 数：",[39,1199,1200],{},"E[accepted] = (1 - α^(K+1)) \u002F (1 - α)",[13,1202,1203,1204,1207,1208,1211],{},"当 ",[39,1205,1206],{},"α=0.7, K=4","：期望 ≈ ",[90,1209,1210],{},"3.3 tokens\u002F步","（vs 标准的 1 token\u002F步）",[13,1213,1214,1215,1218],{},"实际部署中，draft model 通常比 target model 小 10-20 倍（如 GPT-2 small vs GPT-2 large），验证步骤的成本接近 1 个标准 decode step，但产出 3.3 个 token，理论加速 ",[90,1216,1217],{},"3.3x","。",[28,1220],{},[31,1222,1224],{"id":1223},"四系统整体数据流","四、系统整体数据流",[71,1226,1229],{"className":1227,"code":1228,"language":76},[74],"用户请求\n  │\n  ▼\nLLMEngine.add_request()\n  │ 创建 Sequence（状态 WAITING），加入 Scheduler.waiting 队列\n  ▼\nScheduler.schedule()  ── 每步调用 ──▶\n  │\n  ├── 1. 为 RUNNING 序列 append_slot（可能触发 CoW）\n  ├── 2. 为 PREFILLING 序列推进 chunk 进度\n  ├── 3. 尝试 swap_in SWAPPED 序列\n  └── 4. 从 waiting 拉入新请求（分配物理块）\n  │\n  ▼ SchedulerOutput（prefill_chunks + decode_seqs）\n  │\nModelRunner.step()  →  Dict[seq_id → new_token_id]\n  │\n  ▼\nScheduler.on_step_done()  →  追加 token，检查完成条件\n  │\nMetricsCollector  →  p50\u002Fp95\u002Fp99 latency、TTFT、KV utilization\n  │\n  ▼\nRequestOutput（latency, ttft, output_text）\n",[39,1230,1228],{"__ignoreMap":79},[28,1232],{},[31,1234,1236],{"id":1235},"五工程亮点","五、工程亮点",[59,1238,1240],{"id":1239},"测试驱动67-个单元测试","测试驱动，67 个单元测试",[71,1242,1246],{"className":1243,"code":1244,"language":1245,"meta":79,"style":79},"language-bash shiki shiki-themes github-dark-dimmed github-light","tests\u002Ftest_block_allocator.py   # 9 tests：OOM、CoW、批量释放\ntests\u002Ftest_kv_cache.py          # 10 tests：分配、append_slot、CoW\ntests\u002Ftest_scheduler.py         # 8 tests：连续批处理核心逻辑\ntests\u002Ftest_new_features.py      # 40 tests：6 个新功能的完整测试\n","bash",[39,1247,1248,1256,1264,1272],{"__ignoreMap":79},[203,1249,1250,1253],{"class":205,"line":206},[203,1251,1252],{"class":220},"tests\u002Ftest_block_allocator.py",[203,1254,1255],{"class":238},"   # 9 tests：OOM、CoW、批量释放\n",[203,1257,1258,1261],{"class":205,"line":213},[203,1259,1260],{"class":220},"tests\u002Ftest_kv_cache.py",[203,1262,1263],{"class":238},"          # 10 tests：分配、append_slot、CoW\n",[203,1265,1266,1269],{"class":205,"line":228},[203,1267,1268],{"class":220},"tests\u002Ftest_scheduler.py",[203,1270,1271],{"class":238},"         # 8 tests：连续批处理核心逻辑\n",[203,1273,1274,1277],{"class":205,"line":242},[203,1275,1276],{"class":220},"tests\u002Ftest_new_features.py",[203,1278,1279],{"class":238},"      # 40 tests：6 个新功能的完整测试\n",[59,1281,1282],{"id":1282},"干净的接口分离",[13,1284,1285,1288],{},[39,1286,1287],{},"ModelRunner"," 是可替换的后端接口，调度器完全不感知后端差异：",[71,1290,1292],{"className":197,"code":1291,"language":199,"meta":79,"style":79},"class BaseModelRunner(ABC):\n    @abstractmethod\n    def step(self, prefill_seqs, decode_seqs) -> Dict[int, int]: ...\n",[39,1293,1294,1308,1313],{"__ignoreMap":79},[203,1295,1296,1298,1301,1303,1306],{"class":205,"line":206},[203,1297,217],{"class":216},[203,1299,1300],{"class":220}," BaseModelRunner",[203,1302,773],{"class":224},[203,1304,1305],{"class":234},"ABC",[203,1307,572],{"class":224},[203,1309,1310],{"class":205,"line":213},[203,1311,1312],{"class":209},"    @abstractmethod\n",[203,1314,1315,1317,1320,1323,1325,1328,1330,1333],{"class":205,"line":228},[203,1316,313],{"class":216},[203,1318,1319],{"class":209}," step",[203,1321,1322],{"class":224},"(self, prefill_seqs, decode_seqs) -> Dict[",[203,1324,235],{"class":234},[203,1326,1327],{"class":224},", ",[203,1329,235],{"class":234},[203,1331,1332],{"class":224},"]: ",[203,1334,1335],{"class":234},"...\n",[13,1337,1338,1341,1342,1345],{},[39,1339,1340],{},"MockModelRunner","（无 GPU，用于 CI）和 ",[39,1343,1344],{},"GPT2ModelRunner","（真实推理）实现同一接口。",[59,1347,1349],{"id":1348},"github-actions-ci","GitHub Actions CI",[13,1351,1352],{},"矩阵测试（Python 3.9\u002F3.10\u002F3.11）+ benchmark smoke test，每次 push 自动验证。",[28,1354],{},[31,1356,1358],{"id":1357},"六面试中的高频考点","六、面试中的高频考点",[13,1360,1361],{},"通过实现这个项目，能清楚回答以下问题：",[13,1363,1364,1367,1369],{},[90,1365,1366],{},"Q：vLLM 为什么比 HuggingFace 快？",[24,1368],{},"\n→ Continuous Batching（避免 GPU 空闲）+ PagedAttention（消除显存碎片），GPU 利用率从 ~30% 提升到 ~90%+。",[13,1371,1372,1375,1377,1378,1381,1382,1384,1385,1388,1389,1218],{},[90,1373,1374],{},"Q：Prefix Cache 的 CoW 怎么实现的？",[24,1376],{},"\n→ 每个满块计算 ",[39,1379,1380],{},"content_hash = hash(tuple(token_ids))","，命中时 ",[39,1383,961],{}," 直接引用；写入前检查 ",[39,1386,1387],{},"ref_count > 1"," 则分配新块、旧块 ",[39,1390,1391],{},"ref_count-1",[13,1393,1394,1397,1399],{},[90,1395,1396],{},"Q：显存不够时怎么处理？",[24,1398],{},"\n→ 三种策略：① Swap out 到 CPU（保留 KV cache）；② Recompute（丢弃，适合短序列）；③ 请求排队（等内存释放）。",[13,1401,1402,1405,1407],{},[90,1403,1404],{},"Q：Chunked Prefill 和 Continuous Batching 的关系？",[24,1406],{},"\n→ CB 解决\"谁来跑\"的问题（批次动态变化）；Chunked Prefill 解决\"prefill 步骤阻塞 decode\"的问题（把长 prefill 拆碎，穿插到 decode 步骤之间）。二者配合使用。",[13,1409,1410,1413,1415],{},[90,1411,1412],{},"Q：Speculative Decoding 什么时候失效？",[24,1414],{},"\n→ draft 和 target 分布差异大时（α 低），每步 draft 时间 > 节省的 token 时间。通常 α \u003C 0.5 时不如标准解码。实践中选择同家族小模型（如 Llama-3.2-1B 作为 Llama-3-70B 的 draft）。",[28,1417],{},[31,1419,1421],{"id":1420},"七与-vllm-的对比","七、与 vLLM 的对比",[94,1423,1424,1437],{},[97,1425,1426],{},[100,1427,1428,1431,1434],{},[103,1429,1430],{},"功能",[103,1432,1433],{},"本项目",[103,1435,1436],{},"vLLM",[116,1438,1439,1449,1459,1469,1480,1490,1500],{},[100,1440,1441,1443,1446],{},[121,1442,137],{},[121,1444,1445],{},"✅ Python 模拟",[121,1447,1448],{},"✅ CUDA kernel（PagedAttention）",[100,1450,1451,1454,1457],{},[121,1452,1453],{},"Continuous Batching",[121,1455,1456],{},"✅",[121,1458,1456],{},[100,1460,1461,1464,1466],{},[121,1462,1463],{},"Chunked Prefill",[121,1465,1456],{},[121,1467,1468],{},"✅（Sarathi-Serve 集成）",[100,1470,1471,1474,1477],{},[121,1472,1473],{},"Prefix Caching",[121,1475,1476],{},"✅ 哈希平铺",[121,1478,1479],{},"✅ Radix Tree（最长前缀匹配）",[100,1481,1482,1484,1487],{},[121,1483,1146],{},[121,1485,1486],{},"✅ 模拟",[121,1488,1489],{},"✅ 真实 CUDA memcpy",[100,1491,1492,1495,1497],{},[121,1493,1494],{},"Speculative Decoding",[121,1496,1456],{},[121,1498,1499],{},"✅（Eagle、Medusa 等变体）",[100,1501,1502,1505,1508],{},[121,1503,1504],{},"多 GPU",[121,1506,1507],{},"❌",[121,1509,1510],{},"✅（Tensor + Pipeline Parallelism）",[13,1512,1513,1514,1218],{},"本项目的价值在于",[90,1515,1516],{},"让调度策略层可读、可测试、可解释",[28,1518],{},[31,1520,1522],{"id":1521},"八总结","八、总结",[13,1524,1525],{},"推荐路线（如果你也想做）：",[1527,1528,1529,1536,1542,1548],"ol",{},[1530,1531,1532,1535],"li",{},[90,1533,1534],{},"先读论文摘要","：PagedAttention（SOSP'23）、Orca（OSDI'22）、Sarathi（OSDI'24）——各 10 分钟，理解直觉",[1530,1537,1538,1541],{},[90,1539,1540],{},"先实现 BlockAllocator","：最纯粹的数据结构，没有依赖，容易测试",[1530,1543,1544,1547],{},[90,1545,1546],{},"再实现 Scheduler","：连续批处理是核心，其他功能都是在它上面叠加",[1530,1549,1550,1553],{},[90,1551,1552],{},"最后加高级功能","：Prefix Cache → CPU Swap → Speculative Decoding",[13,1555,1556,1557],{},"代码在 GitHub：",[90,1558,1559],{},[17,1560,1562],{"href":19,"rel":1561},[21],"liangqianxing\u002Fmini-llm-engine",[71,1564,1566],{"className":1243,"code":1565,"language":1245,"meta":79,"style":79},"git clone https:\u002F\u002Fgithub.com\u002Fliangqianxing\u002Fmini-llm-engine\ncd mini-llm-engine\npip install pytest\npytest tests\u002F -v                              # 67 tests, all pass\npython run_all_benchmarks.py --fast --no-plot # 5 个 benchmark\n",[39,1567,1568,1580,1588,1599,1613],{"__ignoreMap":79},[203,1569,1570,1573,1577],{"class":205,"line":206},[203,1571,1572],{"class":220},"git",[203,1574,1576],{"class":1575},"sXfbr"," clone",[203,1578,1579],{"class":1575}," https:\u002F\u002Fgithub.com\u002Fliangqianxing\u002Fmini-llm-engine\n",[203,1581,1582,1585],{"class":205,"line":213},[203,1583,1584],{"class":234},"cd",[203,1586,1587],{"class":1575}," mini-llm-engine\n",[203,1589,1590,1593,1596],{"class":205,"line":228},[203,1591,1592],{"class":220},"pip",[203,1594,1595],{"class":1575}," install",[203,1597,1598],{"class":1575}," pytest\n",[203,1600,1601,1604,1607,1610],{"class":205,"line":242},[203,1602,1603],{"class":220},"pytest",[203,1605,1606],{"class":1575}," tests\u002F",[203,1608,1609],{"class":234}," -v",[203,1611,1612],{"class":238},"                              # 67 tests, all pass\n",[203,1614,1615,1617,1620,1623,1626],{"class":205,"line":259},[203,1616,199],{"class":220},[203,1618,1619],{"class":1575}," run_all_benchmarks.py",[203,1621,1622],{"class":234}," --fast",[203,1624,1625],{"class":234}," --no-plot",[203,1627,1628],{"class":238}," # 5 个 benchmark\n",[1630,1631,1632],"style",{},"html pre.shiki code .saVmf, html code.shiki .saVmf{--shiki-default:#DCBDFB;--shiki-light:#6F42C1}html pre.shiki code .s6PUj, html code.shiki .s6PUj{--shiki-default:#F47067;--shiki-light:#D73A49}html pre.shiki code .sqRhv, html code.shiki .sqRhv{--shiki-default:#F69D50;--shiki-light:#6F42C1}html pre.shiki code .ssh_m, html code.shiki .ssh_m{--shiki-default:#ADBAC7;--shiki-light:#24292E}html pre.shiki code .swcJU, html code.shiki .swcJU{--shiki-default:#6CB6FF;--shiki-light:#005CC5}html pre.shiki code .sgHix, html code.shiki .sgHix{--shiki-default:#768390;--shiki-light:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sr-dd, html code.shiki .sr-dd{--shiki-default:#6CB6FF;--shiki-light:#6F42C1}html pre.shiki code .sXfbr, html code.shiki .sXfbr{--shiki-default:#96D0FF;--shiki-light:#032F62}",{"title":79,"searchDepth":213,"depth":228,"links":1634},[1635,1636,1640,1648,1649,1654,1655,1656],{"id":33,"depth":213,"text":34},{"id":53,"depth":213,"text":54,"children":1637},[1638,1639],{"id":61,"depth":228,"text":62},{"id":149,"depth":228,"text":150},{"id":164,"depth":213,"text":165,"children":1641},[1642,1643,1644,1645,1646,1647],{"id":168,"depth":228,"text":169},{"id":492,"depth":228,"text":493},{"id":722,"depth":228,"text":723},{"id":930,"depth":228,"text":931},{"id":1139,"depth":228,"text":1140},{"id":1175,"depth":228,"text":1176},{"id":1223,"depth":213,"text":1224},{"id":1235,"depth":213,"text":1236,"children":1650},[1651,1652,1653],{"id":1239,"depth":228,"text":1240},{"id":1282,"depth":228,"text":1282},{"id":1348,"depth":228,"text":1349},{"id":1357,"depth":213,"text":1358},{"id":1420,"depth":213,"text":1421},{"id":1521,"depth":213,"text":1522},[1658],"AI","2026-06-30 10:00:00",false,"md",{},"\u002Fposts\u002Fmini-llm-engine-from-scratch",{"title":5,"description":79},"posts\u002Fmini-llm-engine-from-scratch",[1667,1668,1436,1669,1670,1671,1672],"LLM","AI Infra","PagedAttention","推理优化","投机解码","实习求职","3Wt0KbjRxuMV29OqtH2VS_thVGI7AFle59J7wwMtRPE",[1675,1686,1693,1696,1708,1717,1726,1736,1746,1755,1765,1777,1790,1802,1811,1821,1833,1844,1854,1862,1873,1879,1885,1891,1899,1908,1916,1922,1930,1938],{"slug":1676,"path":1677,"title":1678,"date":1679,"tags":1680,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":298},"multimodal-rag-from-scratch","\u002Fposts\u002Fmultimodal-rag-from-scratch","从零实现多模态 RAG：BM25、Dense 检索、RRF 融合、MMR 重排全部手写","2026-06-30 18:00:00",[1681,1682,1668,1683,1684,1685,1672],"RAG","多模态","BM25","向量检索","混合检索",{"slug":1687,"path":1688,"title":1689,"date":1690,"tags":1691,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":310},"mini-llm-engine-deep-dive","\u002Fposts\u002Fmini-llm-engine-deep-dive","讲透 mini-llm-engine：从显存碎片到六大推理优化","2026-06-30 14:00:00",[1667,1668,1436,1669,1692,1670,1671,1672],"KV Cache",{"slug":1694,"path":1663,"title":5,"date":1659,"tags":1695,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":304},"mini-llm-engine-from-scratch",[1667,1668,1436,1669,1670,1671,1672],{"slug":1697,"path":1698,"title":1699,"date":1700,"tags":1701,"description":1707,"draft":1660,"hidden":1660,"published":278,"readingTime":701},"bytedance-recommendation-architecture-intern-interview","\u002Fposts\u002Fbytedance-recommendation-architecture-intern-interview","字节推荐架构实习生 Data 面试准备：推荐系统、实时特征与高并发八股","2026-06-09",[1702,1703,1704,1705,1706],"面试","推荐系统","后端架构","实时计算","字节跳动","面向字节跳动推荐架构团队实习岗位的八股准备清单，覆盖推荐系统链路、实时数据、特征服务、高并发后端、分布式系统与项目包装。",{"slug":1709,"path":1710,"title":1711,"date":1700,"tags":1712,"description":1716,"draft":1660,"hidden":1660,"published":278,"readingTime":322},"high-concurrency-rate-limiting-algorithms","\u002Fposts\u002Fhigh-concurrency-rate-limiting-algorithms","高并发限流算法：固定窗口、滑动窗口、漏桶与令牌桶",[1713,1714,1704,1715,1702],"高并发","限流","系统设计","面试和工程都常见的限流算法总结，讲清楚固定窗口、滑动窗口、漏桶、令牌桶、并发数限制以及分布式限流如何落地。",{"slug":1718,"path":1719,"title":1720,"date":1700,"tags":1721,"description":1725,"draft":1660,"hidden":1660,"published":278,"readingTime":292},"kafka-producer-broker-consumer","\u002Fposts\u002Fkafka-producer-broker-consumer","Kafka 入门：生产者、Broker、消费者和“消费”到底是什么意思",[1722,1723,1724,1702,1703],"Kafka","消息队列","后端","用推荐系统里的用户行为日志为例，讲清楚 Kafka 的作用、Producer、Broker、Consumer、Topic、Partition、Offset 和消费语义。",{"slug":1727,"path":1728,"title":1729,"date":1700,"tags":1730,"description":1735,"draft":1660,"hidden":1660,"published":278,"readingTime":275},"leetcode-lru-merge-k-reverse-list","\u002Fposts\u002Fleetcode-lru-merge-k-reverse-list","链表与缓存高频题：LRU Cache、合并 K 个有序链表、反转链表",[1731,1732,1733,1734,1702],"算法","链表","LRU","LeetCode","面试高频算法题速记，整理 LRU Cache、合并 K 个有序链表、反转链表的核心思路、复杂度和 C++ 代码。",{"slug":1737,"path":1738,"title":1739,"date":1740,"tags":1741,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":370},"meddococr-interpreter-source-analysis","\u002Fposts\u002Fmeddococr-interpreter-source-analysis","MedDocOCR-Interpreter 源码导读：医疗文档 OCR、结构化抽取与报告解读原型","2026-05-22 10:30:00",[1742,1682,1743,1681,1744,1745],"OCR","医疗 AI","Python","源码分析",{"slug":1747,"path":1748,"title":1749,"date":1750,"tags":1751,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":304},"cet6-writing-model-essays","\u002Fposts\u002Fcet6-writing-model-essays","六级写作范文背诵包：10 个高频话题","2026-05-20 09:00:00",[1752,1753,1754],"English","CET6","Writing",{"slug":1756,"path":1757,"title":1758,"date":1759,"tags":1760,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":1764},"claude-code-context-management","\u002Fposts\u002Fclaude-code-context-management","Claude Code 上下文管理机制：从 Microcompact 到 Auto Compact","2026-05-19 10:00:00",[1761,1762,1667,1668,1763],"Claude Code","Agent","上下文工程",27,{"slug":1766,"path":1767,"title":1768,"date":1769,"tags":1770,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":1776},"nanotron-llm-pretraining-framework-analysis","\u002Fposts\u002Fnanotron-llm-pretraining-framework-analysis","Nanotron 项目详解：Hugging Face 的大模型预训练框架怎么做分布式训练","2026-05-10 12:10:00",[1667,1771,1772,1773,1774,1668,1775],"大模型训练","分布式训练","Nanotron","Hugging Face","PyTorch",19,{"slug":1778,"path":1779,"title":1780,"date":1781,"tags":1782,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":1789},"gofoundry-go-backend-foundation-framework","\u002Fposts\u002Fgofoundry-go-backend-foundation-framework","GoFoundry 项目详解：基于 Go 的后端基础框架套件设计","2026-05-10 11:20:00",[1783,1784,1785,1786,1787,1723,1788],"Go","后端框架","ORM","分布式缓存","分布式锁","项目架构",25,{"slug":1791,"path":1792,"title":1793,"date":1794,"tags":1795,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":1764},"cloudvault-go-cloud-storage-system","\u002Fposts\u002Fcloudvault-go-cloud-storage-system","CloudVault 项目详解：基于 Go 的云端存储与网盘系统架构设计","2026-05-10 10:30:00",[1783,1796,1797,1798,1788,1799,1800,1801],"云存储","网盘系统","分布式系统","Redis","RabbitMQ","Elasticsearch",{"slug":1803,"path":1804,"title":1805,"date":1806,"tags":1807,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":1810},"openclaw-source-code-analysis","\u002Fposts\u002Fopenclaw-source-code-analysis","OpenClaw 源码导读：个人 AI 助手的网关、通道、插件与运行时架构","2026-05-08 16:30:00",[1808,1762,1668,1809,1745],"OpenClaw","TypeScript",20,{"slug":1812,"path":1813,"title":1814,"date":1815,"tags":1816,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":304},"flow-matching-generative-models","\u002Fposts\u002Fflow-matching-generative-models","Flow Matching：从噪声到数据的连续流生成模型","2026-05-07 00:00:00",[1817,1818,1819,1820],"生成模型","Diffusion","Flow Matching","深度学习",{"slug":1822,"path":1823,"title":1824,"date":1825,"tags":1826,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":358},"database-ai-week4","\u002Fposts\u002Fdatabase-ai-week4","Week 4：数据库速成——从 Storage、Index、Query Optimization 到 Vector DB 与 RAG","2026-05-05 12:00:00",[1827,1828,1829,1681,1830,1831,1832],"数据库","CMU 15-445","Vector DB","LLM Memory","Query Optimization","Caching",{"slug":1834,"path":1835,"title":1836,"date":1837,"tags":1838,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":370},"distributed-systems-week3","\u002Fposts\u002Fdistributed-systems-week3","Week 3：分布式系统速成——MapReduce、Raft、容错与 Distributed KV Store","2026-05-05 11:00:00",[1798,1839,1840,1841,1842,1843,1762],"MIT 6.824","MapReduce","Raft","KV Store","Ray",{"slug":1845,"path":1846,"title":1847,"date":1848,"tags":1849,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":358},"gpu-inference-acceleration-week2","\u002Fposts\u002Fgpu-inference-acceleration-week2","Week 2：GPU 与推理加速——从 Kernel、算子融合到 LLM Serving","2026-05-05 10:00:00",[1820,1850,1851,1667,1852,1436,1853],"GPU","推理加速","CMU 10-414","TensorRT",{"slug":1855,"path":1856,"title":1857,"date":1858,"tags":1859,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":701},"dl-framework-autograd-mini","\u002Fposts\u002Fdl-framework-autograd-mini","Week 1：DL 框架与 Autograd——从计算图、反向传播到 Mini Autograd 实现","2026-05-05 09:00:00",[1820,1860,1775,1852,1861],"Autograd","Mini Framework",{"slug":1863,"path":1864,"title":1865,"date":1866,"tags":1867,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":322},"lock-free-concurrency-notes","\u002Fposts\u002Flock-free-concurrency-notes","无锁并发入门：从 CAS 到 Atomic Ring Buffer","2026-04-25",[1868,1869,1870,1871,1872],"C++","并发","无锁编程","性能优化","量化开发",{"slug":1874,"path":1875,"title":1876,"date":1877,"tags":1878,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":259},"agent-memory","\u002Fposts\u002Fagent-memory","Agent 对话记忆化：从原理到实现","2026-04-24",[1667,1762,1681,1702],{"slug":1880,"path":1881,"title":1882,"date":1877,"tags":1883,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":275},"llm-context-compression","\u002Fposts\u002Fllm-context-compression","LLM 上下文五层压缩机制详解",[1667,1762,1884,1702],"上下文压缩",{"slug":1886,"path":1887,"title":1888,"date":1889,"tags":1890,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":298},"cpp-concurrency-basics","\u002Fposts\u002Fcpp-concurrency-basics","C++ 并发编程入门：从数据竞争到线程池","2026-04-15",[1868,1869,1702,1872],{"slug":1892,"path":1893,"title":1894,"date":1895,"tags":1896,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":206},"travel-in-shenzhen","\u002Fposts\u002Ftravel-in-shenzhen","XCPC 深圳游记","2026-04-13",[1897,1868,1898],"XCPC","比赛",{"slug":1900,"path":1901,"title":1902,"date":1903,"tags":1904,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":282},"backend-stack-deep-dive","\u002Fposts\u002Fbackend-stack-deep-dive","后端五件套：FastAPI \u002F Node.js \u002F SQLAlchemy async \u002F PostgreSQL \u002F Docker 面试速通","2026-04-07",[1724,1905,1906,1907,1702],"FastAPI","PostgreSQL","Docker",{"slug":1909,"path":1910,"title":1911,"date":1912,"tags":1913,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":275},"deepscientist-tech-stack","\u002Fposts\u002Fdeepscientist-tech-stack","DeepScientist 技术栈全解析：一个 AI 科研平台的架构设计","2026-04-06",[1914,1905,1915,1906,1702],"全栈","Next.js",{"slug":1917,"path":1918,"title":1919,"date":1912,"tags":1920,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":292},"minicode-source-analysis","\u002Fposts\u002Fminicode-source-analysis","MiniCode 源码解析：用 5000 行 TypeScript 实现一个 AI 编程助手",[1809,1921,1667,1745,1702],"CLI",{"slug":1923,"path":1924,"title":1925,"date":1912,"tags":1926,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":259},"nova-theme-implementation","\u002Fposts\u002Fnova-theme-implementation","我是怎么从零实现 Nova 主题的",[1927,1928,1929],"Hexo","前端","开源",{"slug":1931,"path":1932,"title":1933,"date":1934,"tags":1935,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":213},"git-cheatsheet","\u002Fposts\u002Fgit-cheatsheet","Git 常用操作备忘","2026-04-05 14:00:00",[1936,1937],"Git","工具",{"slug":1939,"path":1940,"title":1941,"date":1942,"tags":1943,"description":79,"draft":1660,"hidden":1660,"published":278,"readingTime":228},"github-actions-intro","\u002Fposts\u002Fgithub-actions-intro","GitHub Actions 入门：自动化你的工作流","2026-04-04 09:00:00",[1944,1945,1946],"GitHub Actions","CI\u002FCD","自动化",1782796012472]