[{"categories":["Notes"],"collections":null,"content":"这是什么 这个博客的知识索引。所有文章按 6 条轨道归类，每条轨道标出读到哪里、断在哪。以后再写新的，挂到对应轨道下面即可。 总览：61 篇，跨 2024-01 → 2025-12。最大的两块是 OS/Kernel（22） 和 Database（11）——论文读得很多，但动手只到 Mini-LSM 第一周。这是目前最大的缺口。 图例：● 在读 / 未收尾 ✓ 已完成一个阶段 （无标记）单篇论文笔记 ","date":"2026-09-26","objectID":"/reading-map/:1:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#这是什么"},{"categories":["Notes"],"collections":null,"content":"一、OS \u0026amp; Kernel（22 篇） 数据中心系统、虚拟化、内核旁路、远端内存。这是你读得最厚的一块。 虚拟化 / 隔离 Xen and the Art of Virtualization — Xen 半虚拟化 Firecracker — 轻量级 serverless 虚拟化 From Laptop to Lambda — 瞬时函数容器（也见轨道五） OS 架构 Exokernel — 应用级资源管理 The Multikernel — 多核可扩展内核 The Demikernel — 微秒级数据面 OS SigmaOS — 统一 serverless 与微服务 内核旁路 / 数据面 Arrakis — OS 即控制面 Junction — 让内核旁路在云上可用 IX: A Protected Dataplane OS — 高吞吐低尾延迟 Shenango — 延迟敏感负载的 CPU 效率 Caladan — 微秒级干扰隔离 Enso — NIC-应用通信的流式接口 Dune — 用户态访问特权 CPU 特性 调度 / eBPF ghOSt — 用户态接管 Linux 调度 XDP — 可编程包处理 XRP — 内核内 eBPF 存储函数 内存 / 远端内存 Software-Defined Far Memory — 仓库级计算 Fastswap — 远端内存能提吞吐吗 AIFM — 应用集成的远端内存 Temeraire — hugepage 感知的分配器 内存知识笔记 01 — 为什么可用内存远超物理内存 RDMA PRISM — 重新思考 RDMA 接口 ","date":"2026-09-26","objectID":"/reading-map/:2:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#一os--kernel22-篇"},{"categories":["Notes"],"collections":null,"content":"二、Database（11 篇） 存储引擎（动手） ● Mini-LSM Week 1 Day1 · Day2 · Day3 — 断点：只做完 Week 1（Week 2 compaction、Week 3 MVCC 未做） 书 DDIA 序章 + 第一章 DDIA 第二章：数据模型与查询语言 系统论文 DuckDB — 可嵌入分析型数据库 Delta Lake — 对象存储上的 ACID 表 Milvus — 向量数据管理系统 AnalyticDB-V — 查询融合的混合分析引擎 VBase — 统一向量检索与关系查询 Apiary — DBMS 集成的 FaaS ","date":"2026-09-26","objectID":"/reading-map/:3:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#二database11-篇"},{"categories":["Notes"],"collections":null,"content":"三、Learned Data Systems（9 篇） 机器学习 / LLM 进入数据系统：学习型索引、代价模型、自动调优、NL2SQL。 Bao — 让学习型查询优化变得实用（SIGMOD 21） The Case for a Learned Sorting Algorithm — 学习型排序（SIGMOD 20） Zero-Shot Cost Models — 开箱即用的学习型代价预测（VLDB 22） MB2 — 自驱动 DBMS 的行为建模 ML-based Automatic Configuration Tuning DB-BERT — 读手册的调优工具 SEED — 用 LLM 做领域数据整理 SPADE — 为 LLM 流水线合成断言 CatSQL — 面向真实世界的 NL2SQL（VLDB 23） ","date":"2026-09-26","objectID":"/reading-map/:4:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#三learned-data-systems9-篇"},{"categories":["Notes"],"collections":null,"content":"四、ML Systems（8 篇） CMU 10-414/714 Deep Learning Systems（2020） —— 断点在这里 ● 01 Softmax · 02-03 Neural Networks · 04 Automatic Differentiation · Autograd 实现 · hw0 从零写深度学习库 Needle；做到 autograd，hw1（自动微分框架）之后停。这是整个博客最新的学习痕迹（2025-03）。 训练并行论文 Megatron-LM — 模型并行训练 Megatron-LM v2 — GPU 集群上高效大规模训练 PipeDream — 流水线并行（SOSP 19） ","date":"2026-09-26","objectID":"/reading-map/:5:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#四ml-systems8-篇"},{"categories":["Notes"],"collections":null,"content":"五、Distributed \u0026amp; Cloud（5 篇） Sky Computing — 从云计算到天空计算 ServiceRouter — Meta 的超大规模低本服务网格 Scalability! But at what COST — 单机也能赢？ From Laptop to Lambda — 瞬时函数容器 Ownership — 细粒度任务的分布式 future ","date":"2026-09-26","objectID":"/reading-map/:6:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#五distributed--cloud5-篇"},{"categories":["Notes"],"collections":null,"content":"六、Notes（6 篇） ● Agentic Design Patterns 第一章 — 读了 86 字就停，通篇在质疑 prompt chaining 是否真有用 2024 的一些计划 微服务架构 Go 并发编程实战 1-4 Ryan Dahl: NodeJS 人月神话 ","date":"2026-09-26","objectID":"/reading-map/:7:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#六notes6-篇"},{"categories":["Notes"],"collections":null,"content":"断点与下一步 三个断点： Mini-LSM：Week 1 之后（2024-07 最后一次记录） CMU 10-714 DLSys：hw0 / autograd 之后（2025-03） Agentic Design Patterns：第一章质疑后搁置（2025-12） 核心问题：读了 44 篇论文，但动手只有 Mini-LSM 第一周。缺的不是输入，是把论文变成能跑的东西。 建议路线（选一条，别贪多；每条都自带判分 / 测试，学完写一篇博客）： 路线 内容 为什么 A（推荐） CMU 15-445 BusTub 把 Mini-LSM 的存储层补成完整 RDBMS：buffer pool → B+Tree → 查询执行 → 并发控制 → 恢复。C++20，与日常工作（RisingWave）直接相关 B MIT 6.1810 xv6 亲手写页表 / COW / 陷阱 / 文件系统 / mmap，对应轨道一的 22 篇 OS 论文。make grade 自带判分 C MIT 6.5840 或 TinyKV 补分布式侧（Raft、分片 KV），自带测试 / GitHub Classroom 判分 ","date":"2026-09-26","objectID":"/reading-map/:8:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#断点与下一步"},{"categories":["Notes"],"collections":null,"content":"参考资源（筛过的） 动手实验 skyzh/mini-lsm — 已有 3 周 + 给 coding agent 的引导轨 CMU 15-445 Fall 2025 录像（非 CMU 看这版） MIT 6.1810 xv6 labs · MIT 6.5840 labs aalhour/beachdb · arjunsood2025/raft-kv-engine-project 真干货博客（有署名、读源码、可验证） Jack Vanlightly — Kafka/Pulsar/BookKeeper/Iceberg 内核，配 TLA+ 验证 Marc Brooker — 分布式系统 / AWS Murat Demirbas — 分布式系统研究博客，写了 15 年 Andrey Satarin · Testing Distributed Systems — 策展清单，里面有 RisingWave、TigerBeetle、Jepsen、Elle、TLA+ TigerBeetle Blog · boringSQL 避雷：reddb.io / scalix.world / semicolony.dev 这类匿名、疑似 AI 批量生成的 SEO 站，不要采信。 ","date":"2026-09-26","objectID":"/reading-map/:9:0","tags":["Index"],"title":"Reading Map","uri":"/reading-map/#参考资源筛过的"},{"categories":["Notes"],"collections":null,"content":"Agentic Design Patterns AI Agent 这几个月被吹得神乎其神，让我看看到底怎么个事！ 书是 Antonio Gulli 的 Agentic Design Patterns: A Hands-On Guide to Building Intelligent Systems，21 章 + 7 个附录。配套代码在 evoiz/Agentic-Design-Patterns。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:0","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#agentic-design-patterns"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#prompt-chaining"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#单-prompt-为什么不行"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#拆成链"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#结构化输出"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#书里点出但容易被忽略的一点"},{"categories":["Notes"],"collections":null,"content":"Prompt Chaining 又名 Pipeline pattern。核心一句话：把复杂任务拆成顺序步骤，每步一个聚焦的 prompt，上一步的输出喂给下一步。 单 prompt 为什么不行 书里列了五种失效模式： 失效模式 含义 Instruction neglect 指令被漏执行 Contextual drift 丢掉最初上下文，跑偏 Error propagation 早期错误在后续被放大 上下文窗口不够 信息塞不下 Hallucination 认知负载升高，编造概率上升 例子：一个 prompt 里同时要求「分析市场报告 + 总结 + 提取趋势和数据点 + 起草邮件」，模型可能总结得不错，但数据没提、邮件写砸。 拆成链 ① 总结关键发现 → ② 基于摘要找前三趋势 + 支撑数据点 → ③ 基于趋势起草邮件。每步可赋予不同角色（Market Analyst / Trade Analyst / Writer）。 每步更简单、更少歧义，模型认知负载下降 —— 这是书的说法。 其实我不理解 prompt 里主动要求任务分解 + 拆解，和不要求有什么区别，如果都是通过 LM 注意力机制，有没有又有什么区别呢，prompt 的质量确实能影响结果的好坏，但到底是怎么影响的呢？ 区别不在「prompt 里写不写拆解」—— 在同一个 prompt 里让模型自己分步，和干脆不写，效果差别不大。真正的区别是拆成多次独立的调用：每次调用的上下文更短、约束更单一，模型不必同时兼顾五件事。书的例子其实是后者，但它没有把这个区别讲透。 结构化输出 步骤之间用 JSON/XML 传递，保证下游能精确解析。 注意书的原话是 \u0026ldquo;minimizes errors that arise from interpreting natural language\u0026rdquo; —— 它管的是传输格式，不是内容真假。 让 LLM 生成结构化输出，确实对构建自动化/Agent 系统是有帮助的，但能保障结果的正确性吗？Agent 系统会因为幻觉而失败吗？MCP 的提出能保证完全避免幻觉导致非结构化的输出吗？ 不能。JSON 格式合法 ≠ 内容正确，字段里照样可以是编的。MCP 是工具调用协议，跟幻觉没有关系。 书里点出、但容易被忽略的一点 链真正的价值之一，是能在模型调用之间插入确定性逻辑 —— 校验输出、条件分支、调外部工具。比如精确算术交给计算器，而不是让 LLM 自己算。 应用场景 信息处理工作流：抓取 → 总结 → 抽实体 → 查知识库 → 出报告 复杂问答：拆子问题 → 分别检索 → 综合 数据抽取转换：发票字段抽取，缺字段就条件性再补一轮 内容生成：选题 → 大纲 → 逐节起草 → 润色 有状态对话：把累积的实体/状态构造进新 prompt 代码生成精修：伪码 → 草稿 → 检错 → 重写 → 文档/测试 多模态多步推理：图里文字 → 关联标签 → 查表得结论 书里的自动化研究 Agent 是混合的：检索 + 抽取并发跑，整合 → 综合 → 审查串行跑。 好像都是计划和执行的结合，抛开 LLM Agent，传统编程/人去做这些任务也几乎是这样做的，有什么区别呢？LM 是速度更快还是处理能力更强？目前的 Agent 系统真的发挥了其智能的优势吗？ 这一章没有回答。拆步骤、传结构化数据、插确定性逻辑 —— 这些传统工程早就在做。LLM 带来的增量是每步可以做模糊的语义操作（总结、抽取、判断），而不是「会规划」。书把这些包装成「智能」，但本章讲的其实是工程约束，不是智能。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:1","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#应用场景"},{"categories":["Notes"],"collections":null,"content":"上下文工程和提示工程 （本章最后一节。） Context Engineering 的定义：在生成 token 之前，系统性地设计、构造、投递一个完整的信息环境给模型。它断言：输出质量更少取决于模型架构本身，更多取决于所提供上下文的丰富度。 系统提示词：定义 AI 操作参数的基础指令（如「你是一名技术作家，语气需正式精确」） 外部数据集成 检索文档：AI 主动从知识库获取信息（如提取项目技术规格） 工具输出：通过 API 获取实时数据（如查询日历确定用户空闲时间） 隐式数据融合：结合用户身份、交互历史和环境状态等关键信息 核心原则：即使高级模型，在有限或不良构建的操作环境下也会表现不佳。 表现不佳，LM 参数够大，训练的数据够好，表现会差到哪里去呢？在我看来 参数大 ≠ 知道你的上下文。模型再强，也看不到你的代码库、你的历史决策、你此刻的环境。上下文工程解决的正是这个：不是让模型变聪明，而是把模型不知道的东西喂给它。「提示工程」管怎么说，「上下文工程」管给什么 —— 书里说后者的工程部分在于「运行时抓取、转换数据的健壮管道 + 持续改善上下文质量的反馈回路」。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:2","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#上下文工程和提示工程"},{"categories":["Notes"],"collections":null,"content":"小结 是什么：单 prompt 处理复杂任务会压垮模型（漏指令、丢上下文、编造） 为什么：拆成互相连接的小步骤，每步聚焦一个操作，更可靠、更好调试 什么时候用：任务对一个 prompt 太复杂 / 有多个独立处理阶段 / 步骤间要调外部工具 / 要多步推理并维护状态 LangChain/LangGraph、Google ADK 提供现成的链式编排工具 一句话：第一、三章之前的这个模式，本质是「分而治之 + 步骤间接确定性逻辑」。它不神秘，但确实是把 LLM 塞进可靠系统的基本手段。 ","date":"2025-12-31","objectID":"/posts/adp-01/:1:3","tags":["Agent","笔记"],"title":"Agentic Design Patterns 第一章笔记","uri":"/posts/adp-01/#小结"},{"categories":["ML Systems"],"collections":null,"content":"10-714: Homework 0 build a basic softmax regression algorithm, plus a simple two-layer neural network github hw0 ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#10-714-homework-0"},{"categories":["ML Systems"],"collections":null,"content":"Question 1: A basic add function, and testing/autograding basics plement simple_ml.add() function in src/simple_ml.py testing: !python3 -m pytest -k \u0026quot;add\u0026quot; 根据测试，由于 add(x, y) 传入的参数可以是任意类型，则直接返回 return x + y 没有注册 mugrade 等 autograde system，所以就基本跑本地的测试了 ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-1-a-basic-add-function-and-testingautograding-basics"},{"categories":["ML Systems"],"collections":null,"content":"Question 2: Loading MNIST data parse_mnist_data() function def parse_mnist(image_filename, label_filename): \u0026#34;\u0026#34;\u0026#34; Read an images and labels file in MNIST format. See this page: http://yann.lecun.com/exdb/mnist/ for a description of the file format. Args: image_filename (str): name of gzipped images file in MNIST format label_filename (str): name of gzipped labels file in MNIST format Returns: Tuple (X,y): X (numpy.ndarray[np.float32]): 2D numpy array containing the loaded data. The dimensionality of the data should be (num_examples x input_dim) where \u0026#39;input_dim\u0026#39; is the full dimension of the data, e.g., since MNIST images are 28x28, it will be 784. Values should be of type np.float32, and the data should be normalized to have a minimum value of 0.0 and a maximum value of 1.0 (i.e., scale original values of 0 to 0.0 and 255 to 1.0). y (numpy.ndarray[dtype=np.uint8]): 1D numpy array containing the labels of the examples. Values should be of type np.uint8 and for MNIST will contain the values 0-9. \u0026#34;\u0026#34;\u0026#34; 注意返回的是 Tuple 使用 gzip 打开文件 gzip.open(image_filename, 'rb') 和 gzip.open(label_filename, 'rb') MNIST image_filename 的格式为 [magic number 32bits] [number of images 32bits] [number of rows 32bits] [number of columns 32bits] [... pixel] MNIST label_filename 的格式为 [magic number 32bits] [number of items 32bits] [...label] 利用 struct.unpack('\u0026gt;IIII', f.read(16)) 读取 image_file 表示 big-endian 大端读 16 bytes 获取 magic, num_images, rows, cols 然后使用 img_data = np.frombuffer(f.read(), dtype=np.uint8) 获取剩下的图像数据（一维），进行 img_data.reshape(num_images, rows * cols) / 255.0 转成二维 利用 struct.unpack('\u0026gt;II', f.read(8)) 读取 image_file 表示 big-endian 大端读 8 bytes 获取 magic, num_items 然后用 y = np.frombuffer(f.read(), dtype=np.uint8) 获取剩下的标签数据 ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:2","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-2-loading-mnist-data"},{"categories":["ML Systems"],"collections":null,"content":"Question 3: Softmax loss Implement the softmax (a.k.a. cross-entropy) loss as defined in softmax_loss() function in src/simple_ml.py. ℓsoftmax(z,y)=log⁡∑i=1kexp⁡zi−zy. \\begin{equation} \\ell_{\\mathrm{softmax}}(z, y) = \\log\\sum_{i=1}^k \\exp z_i - z_y. \\end{equation} ℓsoftmax​(z,y)=logi=1∑k​expzi​−zy​.​​softmax_loss() takes a 2D array of logits (i.e., the kkk dimensional logits for a batch of different samples), plus a corresponding 1D array of true labels, and should output the average softmax loss over the entire batch. Note that to do this correctly, you should not use any loops, but do all the computation natively with numpy vectorized operations (to set expectations here, we should note for instance that our reference solution consists of a single line of code). def softmax_loss(Z, y): \u0026#34;\u0026#34;\u0026#34; Return softmax loss. Note that for the purposes of this assignment, you don\u0026#39;t need to worry about \u0026#34;nicely\u0026#34; scaling the numerical properties of the log-sum-exp computation, but can just compute this directly. Args: Z (np.ndarray[np.float32]): 2D numpy array of shape (batch_size, num_classes), containing the logit predictions for each class. y (np.ndarray[np.uint8]): 1D numpy array of shape (batch_size, ) containing the true label of each example. Returns: Average softmax loss over the sample. \u0026#34;\u0026#34;\u0026#34;按照公式完成即可，注意输入是 Z (np.ndarray[np.float32]) (batch_size, num_classes) 每一行代表一个样本的 logits（预测分数），y (np.ndarray[np.uint8]) 是对应的 label 拿出 batch_size = Z.shape[0]，根据公式写出 np.log(np.sum(np.exp(Z), axis=1)) - Z[np.arange(batch_size), y] 最后返回 np.mean(loss) np.arange(batch_size) 生成一个从 0 到 batch_size-1 的数组，这样可以和 y 配合从 Z 中提取每个样本真实类别对应的 logit。 ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:3","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-3-softmax-loss"},{"categories":["ML Systems"],"collections":null,"content":"Question 4: Stochastic gradient descent for softmax regression implement stochastic gradient descent (SGD) for (linear) softmax regression. consider a hypothesis function that makes nnn-dimensional inputs to kkk-dimensional logits via the function h(x)=ΘTx \\begin{equation} h(x) = \\Theta^T x \\end{equation} h(x)=ΘTx​​where x∈Rnx \\in \\mathbb{R}^nx∈Rn is the input, and Θ∈Rn×k\\Theta \\in \\mathbb{R}^{n \\times k}Θ∈Rn×k are the model parameters the gradient of the linear softmax objective is given by $$ \\nabla_\\Theta \\ell_{\\mathrm{softmax}}(\\Theta^T x, y) = x (z - e_y)^T \\\\ z = \\frac{\\exp(\\Theta^T x)}{1^T \\exp(\\Theta^T x)} \\equiv normalize(\\exp(\\Theta^T x)) $$ Using these gradients, implement the softmax_regression_epoch() function, which runs a single epoch of SGD (one pass over a data set) using the specified learning rate / step size lr and minibatch size batch. As described in the docstring, your function should modify the Theta array in-place. def softmax_regression_epoch(X, y, theta, lr = 0.1, batch=100): \u0026#34;\u0026#34;\u0026#34; Run a single epoch of SGD for softmax regression on the data, using the step size lr and specified batch size. This function should modify the theta matrix in place, and you should iterate through batches in X _without_ randomizing the order. Args: X (np.ndarray[np.float32]): 2D input array of size (num_examples x input_dim). y (np.ndarray[np.uint8]): 1D class label array of size (num_examples,) theta (np.ndarrray[np.float32]): 2D array of softmax regression parameters, of shape (input_dim, num_classes) lr (float): step size (learning rate) for SGD batch (int): size of SGD minibatch Returns: None \u0026#34;\u0026#34;\u0026#34;按照 batch 大小遍历数据集，其中 X.shape[0] 是 num_examples 每次获得 X[i : i+batch] 输入和 y[i : i+batch] 相应的 label theta 是参数矩阵，在 numpy 里用 @ 进行矩阵乘法 X_batch @ theta 计算 minibatch logits 也就是预测分数 根据公式计算 np.exp(logits) 求和并且进行 normalize 归一化 One-hot 编码：归一化结果 one_hot = [np.arange(X_batch.shape[0]), y_batch] = 1.0 就得到了标签的的编码。 这里是 python 的语法，np.arange(X_batch.shape[0]) 创建一个大小为参数的数组，并且从 0 开始，也就是行索引，而 y_batch 则是标签的索引，列索引。 梯度计算：grad = (X_batch.T @ (probs - one_hot)) / X_batch.shape[0] 梯度公式 1mXT(Z−Iy)\\frac{1}{m} X^T (Z - I_y)m1​XT(Z−Iy​) modify the Theta array in-place: theta -= lr * grad 其中 lr 就是学习率 θnew=θ−η⋅grad\\theta_{\\text{new}} = \\theta - \\eta \\cdot \\text{grad}θnew​=θ−η⋅grad Training MNIST with softmax regression: For reference, as seen below, our implementation runs in ~3 seconds on Colab, and achieves 7.97% error. ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:4","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-4-stochastic-gradient-descent-for-softmax-regression"},{"categories":["ML Systems"],"collections":null,"content":"Question 5: SGD for a two-layer neural network def nn_epoch(X, y, W1, W2, lr = 0.1, batch=100): \u0026#34;\u0026#34;\u0026#34; Run a single epoch of SGD for a two-layer neural network defined by the weights W1 and W2 (with no bias terms): logits = ReLU(X * W1) * W2 The function should use the step size lr, and the specified batch size (and again, without randomizing the order of X). It should modify the W1 and W2 matrices in place. Args: X (np.ndarray[np.float32]): 2D input array of size (num_examples x input_dim). y (np.ndarray[np.uint8]): 1D class label array of size (num_examples,) W1 (np.ndarray[np.float32]): 2D array of first layer weights, of shape (input_dim, hidden_dim) W2 (np.ndarray[np.float32]): 2D array of second layer weights, of shape (hidden_dim, num_classes) lr (float): step size (learning rate) for SGD batch (int): size of SGD minibatch Returns: None \u0026#34;\u0026#34;\u0026#34;consider a two-layer neural network (without bias terms) of the form z=W2TReLU(W1Tx) z = W_2^T \\mathrm{ReLU}(W_1^T x) z=W2T​ReLU(W1T​x)where W1∈Rn×dW_1 \\in \\mathbb{R}^{n \\times d}W1​∈Rn×d and W2∈Rd×kW_2 \\in \\mathbb{R}^{d \\times k}W2​∈Rd×k represent the weights of the network (which has a ddd-dimensional hidden unit), and where z∈Rkz \\in \\mathbb{R}^kz∈Rk represents the logits output by the network. again use the softmax / cross-entropy loss, meaning that we want to solve the optimization problem minimizeW1,W2 1msumi=1mℓsoftmax(W2TReLU(W1Tx(i)),y(i)) minimize_{W_1, W_2} \\;\\; \\frac{1}{m} sum_{i=1}^m \\ell_{\\mathrm{softmax}}(W_2^T \\mathrm{ReLU}(W_1^T x^{(i)}), y^{(i)}) minimizeW1​,W2​​m1​sumi=1m​ℓsoftmax​(W2T​ReLU(W1T​x(i)),y(i))Using the chain rule, we can derive the backpropagation updates for this network Z1∈Rm×d=ReLU(XW1)G2∈Rm×k=normalize(exp⁡(Z1W2))−IyG1∈Rm×d=1{Z1\u0026gt;0}∘(G2W2T) \\begin{equation} \\begin{split} Z_1 \\in \\mathbb{R}^{m \\times d} \u0026amp; = \\mathrm{ReLU}(X W_1) \\\\ G_2 \\in \\mathbb{R}^{m \\times k} \u0026amp; = normalize(\\exp(Z_1 W_2)) - I_y \\\\ G_1 \\in \\mathbb{R}^{m \\times d} \u0026amp; = \\mathrm{1}\\{Z_1 \u0026gt; 0\\} \\circ (G_2 W_2^T) \\end{split} \\end{equation} Z1​∈Rm×dG2​∈Rm×kG1​∈Rm×d​=ReLU(XW1​)=normalize(exp(Z1​W2​))−Iy​=1{Z1​\u0026gt;0}∘(G2​W2T​)​​​ 相比 SGD，2 layer 的实现也很类似，但需要实现 forward 和 backprop，以及链式法则 softmax(z∗i)=exp⁡(zi)∑∗j=1kexp⁡(zj) \\text{softmax}(z*i) = \\frac{\\exp(z_i)}{\\sum*{j=1}^{k} \\exp(z_j)} softmax(z∗i)=∑∗j=1kexp(zj​)exp(zi​)​ReLU 的误差计算： ∇W1ℓsoftmax(ReLU(XW1)W2,y)=1mXTG1G1∈Rm×d=1{Z1\u0026gt;0}∘(G2W2T) \\nabla_{W_1} \\ell_{\\mathrm{softmax}}(\\mathrm{ReLU}(X W_1) W_2, y) = \\frac{1}{m} X^T G_1 \\\\\\\\ G_1 \\in \\mathbb{R}^{m \\times d} = \\mathrm{1}\\{Z_1 \u0026gt; 0\\} \\circ (G_2 W_2^T) ∇W1​​ℓsoftmax​(ReLU(XW1​)W2​,y)=m1​XTG1​G1​∈Rm×d=1{Z1​\u0026gt;0}∘(G2​W2T​)def nn_epoch(X, y, W1, W2, lr=0.1, batch=100): m = X.shape[0] for i in range(0, m, batch): X_batch = X[i : i + batch] y_batch = y[i : i + batch] m_batch = X_batch.shape[0] # forward, ReLU(XW1)W2, np.maximum(0, ...) 是 ReLU 的实现形式 Z1 = np.maximum(0, X_batch @ W1) logits = Z1 @ W2 # 计算 softmax 输出 exp_logits = np.exp(logits) sums = np.sum(exp_logits, axis=1, keepdims=True) probs = exp_logits / sums # 编码 one_hot = np.zeros_like(probs) one_hot[np.arange(m_batch), y_batch] = 1.0 # backprop # G2: 编码后的 softmax - label G2 = probs - one_hot # W2梯度: (Z1^T @ G2) / m_batch. gradW2 = (Z1.T @ G2) / m_batch # Propagate the gradients back through the ReLU nonlinearity. # (Z1 \u0026gt; 0) is the indicator of activated neurons. # ReLU 反向传播，Z1 大于 0 表示激活 G1 = (G2 @ W2.T) * (Z1 \u0026gt; 0) # Gradient for W1: (X_batch^T @ G1) / m_batch. gradW1 = (X_batch.T @ G1) / m_batch # inplace 原地更新 W1 -= lr * gradW1 W2 -= lr * gradW2Training a full neural network: trains a two-layer network with 400 hidden units. import sys # Reload the simple_ml module which has been cached from the earlier experiment import importlib import simple_ml importlib.reload(simple_ml) sys.path.append(\u0026#34;src/\u0026#34;) from simple_ml import train_nn, parse_mnist X_tr, y_tr = parse_mnist(\u0026#34;data/train-images-idx3-ubyte.gz\u0026#34;, \u0026#34;data/train-labels-idx1-ubyte.gz\u0026#34;) X_te, y_te = parse_mnist(\u0026#34;data","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:5","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-5-sgd-for-a-two-layer-neural-network"},{"categories":["ML Systems"],"collections":null,"content":"Question 6: Softmax regression in C++ pybind11, a header-only library, which allows you to implement the entire Python/C++ interface within a single C++ source library void softmax_regression_epoch_cpp(const float *X, const unsigned char *y, float *theta, size_t m, size_t n, size_t k, float lr, size_t batch) { }it requires passing some additional arguments because we are operating on raw pointers to the array data rather than any sort of higher-level \u0026ldquo;matrix\u0026rdquo; data structure. Specifically, X, y, and theta are pointers to the raw data of the corresponding numpy arrays from the previous section; for 2D arrays, these are stored in C-style (row-major) format, meaning that the first row of XXX is stored sequentially as the first bytes in X, then the second row, etc (this contrasts with column major ordering, which stores the first column of the matrirx sequentially, then the second column, etc). row major 行存储 As an illustration of how to access the data, note that because X represents a row-major, m×nm \\times nm×n matrix, if we want to access the (i,j)(i,j)(i,j) element of XXX (the element in the iiith row and the jjjth column), we would use the index X[i*n + j] pybind11 code that actually provides the Python interface, this code essentially just extracts the raw pointers from the provided inputs (using pybinds numpy interface), and then calls the corresponding softmax_regression_epoch_cpp function. PYBIND11_MODULE(simple_ml_ext, m) { m.def(\u0026#34;softmax_regression_epoch_cpp\u0026#34;, [](py::array_t\u0026lt;float, py::array::c_style\u0026gt; X, py::array_t\u0026lt;unsigned char, py::array::c_style\u0026gt; y, py::array_t\u0026lt;float, py::array::c_style\u0026gt; theta, float lr, int batch) { softmax_regression_epoch_cpp( static_cast\u0026lt;const float*\u0026gt;(X.request().ptr), static_cast\u0026lt;const unsigned char*\u0026gt;(y.request().ptr), static_cast\u0026lt;float*\u0026gt;(theta.request().ptr), X.request().shape[0], X.request().shape[1], theta.request().shape[1], lr, batch ); }, py::arg(\u0026#34;X\u0026#34;), py::arg(\u0026#34;y\u0026#34;), py::arg(\u0026#34;theta\u0026#34;), py::arg(\u0026#34;lr\u0026#34;), py::arg(\u0026#34;batch\u0026#34;)); }相比 python，用 cpp 实现 softmax regression 的难点在于矩阵大小和下标访问： void softmax_regression_epoch_cpp(const float *X, const unsigned char *y, float *theta, size_t m, size_t n, size_t k, float lr, size_t batch) { for (size_t i = 0; i \u0026lt; m; i += batch) { // m 个样本，每次 batch size_t currentBatch = std::min(batch, m - i); // 当前 batch 数量 // Allocate a gradient buffer for theta (size n*k) and initialize to 0. float *grad = new float[n * k]; // std::memset(grad, 0, sizeof(float) * n * k); // process the minibatch for (size_t s = 0; s \u0026lt; currentBatch; s++) { // forward: X_batch @ theta size_t exampleIdx = i + s; // logits = \\theta^T X, x \\in R^n, \\theta \\in R^{n x k} // logits \\in k dimension float *logits = new float[k]; // matrix multiplication // Compute logits = X[exampleIdx] dot theta, for each class j. for (size_t j = 0; j \u0026lt; k; j++) { float dot = 0.0f; for (size_t l = 0; l \u0026lt; n; l++) { // X is row-major: element (exampleIdx, l) // theta is row-major: element (l, j) dot += X[exampleIdx * n + l] * theta[l * k + j]; } logits[j] = dot; } // exp(logits) / np.sum(exp(logtis)) float sumExp = 0.0f; for (size_t j = 0; j \u0026lt; k; j++) { logits[j] = std::exp(logits[j]); sumExp += logits[j]; } // one-hot encoding // compute the probability and the difference (error) // compared to the one-hot label indicator. for (size_t j = 0; j \u0026lt; k; j++) { float prob = logits[j] / sumExp; // If j equals the true label then indicator is 1, else 0. float delta = prob - ( (j == static_cast\u0026lt;size_t\u0026gt;(y[exampleIdx])) ? 1.0f : 0.0f ); // Accumulate gradients for each feature. for (size_t l = 0; l \u0026lt; n; l++) { grad[l * k + j] += X[exampleIdx * n + l] * delta; } } delete[] logits; } // update theta in-place // mean gradients over the mini-batch and update theta // divide by float(currentBatch) to avoid integer division issues for (size_t l = 0; l \u0026lt; n; l++) { for (size_t j = 0; j \u0026lt; k; j++) { theta[l*k + j] -= lr ","date":"2025-03-04","objectID":"/posts/ml-sys-hw0/:1:6","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 hw0","uri":"/posts/ml-sys-hw0/#question-6-softmax-regression-in-c"},{"categories":["ML Systems"],"collections":null,"content":"Automatic Differentiation hypothesis class: x→hθ(x)x \\rightarrow h_\\theta(x)x→hθ​(x), MLP loss function(cross-entropy loss): ℓ(x,y)=−hy(x)+log⁡∑j=1nexp⁡(hj(x))\\ell(x, y) = -h_y(x) + \\log \\sum_{j=1}^n \\exp(h_j(x))ℓ(x,y)=−hy​(x)+log∑j=1n​exp(hj​(x)) optimization method: θ:=θ−α∇θℓ\\theta := \\theta - \\alpha \\nabla_\\theta \\ellθ:=θ−α∇θ​ℓ 机器学习/深度学习是否就是在学习参数集合 θ\\thetaθ? 除了 SGD 随机梯度下降，还有 Adam 等优化方法 计算 gradient 是一个很复杂的问题，但是现在有自动微分 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:1:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#automatic-differentiation"},{"categories":["ML Systems"],"collections":null,"content":"Numerical differentiation $$ \\frac{\\partial f(\\theta)}{\\partial \\theta_i} = \\lim_{\\epsilon \\to 0} \\frac{f(\\theta + \\epsilon e_i) - f(\\theta)}{\\epsilon} \\ \\frac{\\partial f(\\theta)}{\\partial \\theta_i} \\approx \\frac{f(\\theta + \\epsilon e_i) - f(\\theta - \\epsilon e_i)}{2\\epsilon} + o(\\epsilon^2) $$ numerical error, less efficient to compute 一般用来做 checking？ ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:2:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#numerical-differentiation"},{"categories":["ML Systems"],"collections":null,"content":"Numerical gradient checking ∂f(θ)∂θi≈f(θ+ϵδ)−f(θ−ϵδ)2ϵ \\frac{\\partial f(\\theta)}{\\partial \\theta_i} \\approx \\frac{f(\\theta + \\epsilon \\delta) - f(\\theta - \\epsilon \\delta)}{2\\epsilon} ∂θi​∂f(θ)​≈2ϵf(θ+ϵδ)−f(θ−ϵδ)​ 检查 auto differentiation algo 是否正确 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:3:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#numerical-gradient-checking"},{"categories":["ML Systems"],"collections":null,"content":"Symbolic differentiation ∂(f(θ)+g(θ))∂θ=∂f∂θ+∂g∂θ \\frac{\\partial (f(\\theta) + g(\\theta))}{\\partial \\theta} = \\frac{\\partial f}{\\partial \\theta} + \\frac{\\partial g}{\\partial \\theta} ∂θ∂(f(θ)+g(θ))​=∂θ∂f​+∂θ∂g​∂(f(θ)g(θ))∂θ=g(θ)∂f∂θ+f(θ)∂g∂θ \\frac{\\partial (f(\\theta) g(\\theta))}{\\partial \\theta} = g(\\theta) \\frac{\\partial f}{\\partial \\theta} + f(\\theta) \\frac{\\partial g}{\\partial \\theta} ∂θ∂(f(θ)g(θ))​=g(θ)∂θ∂f​+f(θ)∂θ∂g​∂f(g(θ))∂θ=∂f(g)∂g(θ)⋅∂g(θ)∂θ \\frac{\\partial f(g(\\theta))}{\\partial \\theta} = \\frac{\\partial f(g)}{\\partial g(\\theta)} \\cdot \\frac{\\partial g(\\theta)}{\\partial \\theta} ∂θ∂f(g(θ))​=∂g(θ)∂f(g)​⋅∂θ∂g(θ)​ 回顾偏微分法则 wasted computation: f(θ)=∏i=1nθi f(\\theta) = \\prod_{i=1}^n \\theta_i f(θ)=i=1∏n​θi​ 为了避免冗余，现代机器学习和计算工具使用了自动微分（Automatic Differentiation） 这个函数求偏导，会有很多重复的乘积，可以用自动微分和计算图缓存中间结果 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:4:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#symbolic-differentiation"},{"categories":["ML Systems"],"collections":null,"content":"Computational graph y=ln⁡(x1)+x1⋅x2−sin⁡(x2) y = \\ln(x_1) + x_1 \\cdot x_2 - \\sin(x_2) y=ln(x1​)+x1​⋅x2​−sin(x2​) 中间结果 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:5:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#computational-graph"},{"categories":["ML Systems"],"collections":null,"content":"Forward mode automatic differentiation (AD) it automatically propagates gradients through a computational graph based on the mathematical operations involved. 不需要手动计算梯度 计算了很多中间值 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:6:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#forward-mode-automatic-differentiation-ad"},{"categories":["ML Systems"],"collections":null,"content":"Limitation of forward mode AD 对于函数 f:Rn→Rkf: \\mathbb{R}^n \\to \\mathbb{R}^kf:Rn→Rk ，前向模式自动微分需要 n 次传递 来计算输出对所有 n 个输入变量的梯度。 也就是说，对于每个输入变量 (x1,x2,…,xn)(x_1, x_2, \\ldots, x_n )(x1​,x2​,…,xn​)，需要单独进行一次计算，以追踪输出随着该输入的变化。 在大多数情况下，当 k=1k = 1k=1 （标量输出，例如机器学习中的损失函数）且 nnn 很大时（如神经网络的权重数量庞大），前向模式会显得 计算成本较高且效率低下，因为它的计算成本随着输入数量 nnn 的增加线性增长。 We mostly care about the cases where k=1k = 1k=1 and large nnn . In order to resolve the problem efficiently, we need to use another kind of AD ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:7:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#limitation-of-forward-mode-ad"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode automatic differentiation(AD) Adjoint: vˉi=∂y∂vi\\bar{v}_i = \\frac{\\partial y}{\\partial v_i}vˉi​=∂vi​∂y​ 每个变量的导数，但是反向计算 Reverse Mode 一次反向传递即可计算出 标量输出对所有输入的梯度，适用于 f:Rn→R1f: \\mathbb{R}^n \\to \\mathbb{R}^1f:Rn→R1 的情况（例如机器学习中的损失函数）。 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:8:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-automatic-differentiationad"},{"categories":["ML Systems"],"collections":null,"content":"Derivation for the multiple pathway case multiple pathways $$ v_1 \\ y = f(v_2, v_3) \\ \\frac{\\partial y}{\\partial v_1} = \\frac{\\partial f(v_2, v_3)}{\\partial v_2} \\cdot \\frac{\\partial v_2}{\\partial v_1} + \\frac{\\partial f(v_2, v_3)}{\\partial v_3} \\cdot \\frac{\\partial v_3}{\\partial v_1} $$ partial adjoint: vi→vj=vˉj⋅∂vj∂viv_i \\rightarrow v_j = \\bar{v}_j \\cdot \\frac{\\partial v_j}{\\partial v_i}vi​→vj​=vˉj​⋅∂vi​∂vj​​ vˉi=∑j∈next(i)vi→vj \\bar{v}_i = \\sum_{j \\in \\text{next}(i)} v_i \\rightarrow v_j vˉi​=j∈next(i)∑​vi​→vj​We can compute partial adjoints separately then sum them together 自动微分（automatic differentiation）的背景下，针对多路径计算场景的偏导数的推导 用于表示 Reverse AD 算法 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:9:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#derivation-for-the-multiple-pathway-case"},{"categories":["ML Systems"],"collections":null,"content":"Reverse AD algorithm node_to_grad 记录 partial adjoint 用于缓存 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:10:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-ad-algorithm"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode AD by extending computational graph computational graph 拓展 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:11:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-ad-by-extending-computational-graph"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode AD vs Backprop ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:12:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-ad-vs-backprop"},{"categories":["ML Systems"],"collections":null,"content":"Backprop Run backward operations the same forward graph Used in first generation deep learning frameworks (caffe, cuda-convnet) ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:12:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#backprop"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode AD by extending computational graph Construct separate graph nodes for adjoints Used by modern deep learning frameworks 所以 reverse mode AD 自动微分就是缓存了一些其中的结果，用 adjoints 来表示 每次计算就不需要完全重新计算梯度、扩展的图 ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:12:2","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-ad-by-extending-computational-graph-1"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode AD on Tensors Define adjoint for tensor values Zˉ=∂y∂Z=[∂y∂Z1,1⋯∂y∂Z1,n⋮⋱⋮∂y∂Zm,1⋯∂y∂Zm,n] \\bar{Z} = \\frac{\\partial y}{\\partial Z} = \\begin{bmatrix} \\frac{\\partial y}{\\partial Z_{1,1}} \u0026amp; \\cdots \u0026amp; \\frac{\\partial y}{\\partial Z_{1,n}} \\\\ \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ \\frac{\\partial y}{\\partial Z_{m,1}} \u0026amp; \\cdots \u0026amp; \\frac{\\partial y}{\\partial Z_{m,n}} \\end{bmatrix} Zˉ=∂Z∂y​=​∂Z1,1​∂y​⋮∂Zm,1​∂y​​⋯⋱⋯​∂Z1,n​∂y​⋮∂Zm,n​∂y​​​ Tensor 计算，reverse mode AD 的向量表达 下一张将讨论实现 pros/cons of backprop and reverse mode AD: ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:13:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-ad-on-tensors"},{"categories":["ML Systems"],"collections":null,"content":"Handling gradient of gradient The result of reverse mode AD is still a computational graph We can extend that graph further by composing more operations and run reverse mode AD again on the gradient ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:14:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#handling-gradient-of-gradient"},{"categories":["ML Systems"],"collections":null,"content":"Reverse mode AD on data structures Key take away: Define “adjoint value” usually in the same data type as the forward value and adjoint propagation rule. Then the sample algorithm works ","date":"2025-03-03","objectID":"/posts/ml-sys-03/:15:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 04 Automatic Differentiation","uri":"/posts/ml-sys-03/#reverse-mode-ad-on-data-structures"},{"categories":["ML Systems"],"collections":null,"content":"Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM 姑且算作 Megatron LM v2，因为是晚一年发表的，粗略过一下，因为都是同样的 motivation 和 background 等等 知乎一篇不错的总结 作者 Jared 的视频介绍 其中和 ZeRO 的对比很有意思，吞吐量高了很多。周末看看 Gpipe 和 ZeRO ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:1:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#efficient-large-scale-language-model-training-on-gpu-clusters-using-megatron-lm"},{"categories":["ML Systems"],"collections":null,"content":"ABSTRACT 本文中，我们展示了如何将张量并行、流水线并行和数据并行结合起来，以扩展到数千个 GPU。 提出了一种新颖的交错流水线调度方法，可以在内存占用与现有方法相当的情况下，将吞吐量提高 10%以上。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:2:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#abstract"},{"categories":["ML Systems"],"collections":null,"content":"INTRODUCTION 数据并行扩展通常效果良好，但存在两个局限性： beyond a point, the per-GPU batch size becomes too small, reducing GPU utilization and increasing communication cost the maximum number of devices that can be used is the batch size, limiting the number of accelerators that can be used for training. 模型并行技术、张量（层内）模型并行化 张量并行所需的 all-reduce 通信需要通过服务器间链路，这些链路比多 GPU 服务器内可用的高带宽 NVLink[9]慢； 高度的模型并行化可能会产生小的矩阵乘法（GEMMs），可能会降低 GPU 利用率。 Pipeline model parallelism 流水线模型并行化，一个批次被拆分为较小的微批次，执行过程在这些微批次之间进行流水线处理。 将流水线并行、张量并行和数据并行结合起来，这种技术我们称之为 PTD-P 还与 ZeRO[36]进行了比较，发现由于跨节点通信较少，我们的方法在 1750 亿和 5300 亿参数的模型上比 ZeRO-3 高出 70%。这些模型太大，无法容纳在多 GPU 服务器上。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:3:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#introduction"},{"categories":["ML Systems"],"collections":null,"content":"MODES OF PARALLELISM ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:4:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#modes-of-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Data Parallelism 每个工作节点都拥有完整模型的副本，输入数据集被分片，工作节点定期聚合它们的梯度，以确保所有工作节点看到一致的权重版本 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:4:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#data-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Pipeline Model Parallelism 在流水线并行化中，模型的各层被分配到多个设备上。当用于具有重复相同 Transformer 块的模型时，每个设备可以被分配相同数量的 Transformer 层。 一个批次被拆分为较小的微批次；然后在微批次之间进行流水线执行。 其实流水线和 CPU 的流水线调度很相似？ 量化 GPipe 的流水线气泡大小（𝑡𝑝𝑏）。我们将批次中的微批次数量表示为 𝑚，流水线阶段的数量（用于流水线并行的设备数量）表示为 𝑝，每次迭代的理想时间表示为 𝑡𝑖𝑑（假设完美或理想的扩展），执行单个微批次的前向和后向传递的时间表示为 𝑡𝑓 和 𝑡𝑏。在此调度中，流水线气泡包括批次开始时的 𝑝−1 个前向传递和批次结束时的 𝑝−1 个后向传递。流水线气泡中花费的总时间为 𝑡𝑝𝑏 = (𝑝−1)·(𝑡𝑓 +𝑡𝑏)。批次的理想处理时间为 𝑡𝑖𝑑 = 𝑚·(𝑡𝑓 +𝑡𝑏)。因此，流水线气泡中花费的理想计算时间的比例为 Bubble time fraction(pipeline bubble size)=tpbtid=(p−1)⋅(tf+tb)m⋅(tf+tb)=p−1m Bubble\\ time\\ fraction (pipeline\\ bubble\\ size) = \\frac{t_{pb}}{t_{id}} = \\frac{(p−1)\\cdot (t_f + t_b)}{m \\cdot(t_f + t_b)} = \\frac{p−1}{m} Bubble time fraction(pipeline bubble size)=tid​tpb​​=m⋅(tf​+tb​)(p−1)⋅(tf​+tb​)​=mp−1​为了减少流水线气泡的影响，可以增加微批次的数量或减少流水线阶段的数量。 使流水线气泡时间占比（bubble time fraction）尽可能小，我们需要满足 m\u0026gt;\u0026gt;pm \u0026gt;\u0026gt; pm\u0026gt;\u0026gt;p Schedule with Interleaved Stages: 新调度将气泡时间减少了 v 倍，这种流水线气泡大小的减少并非没有代价：这种调度需要额外的通信。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:4:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#pipeline-model-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Tensor Model Parallelism MLP parallelism multi-head attention parallelism ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:4:3","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#tensor-model-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"PERFORMANCE ANALYSIS OF PARALLELIZATI ON CONFIGURATIONS ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:5:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#performance-analysis-of-parallelizati-on-configurations"},{"categories":["ML Systems"],"collections":null,"content":"Tensor and Pipeline Model Parallelism p−1m=n/t−1m \\frac{p - 1}{m} = \\frac{n / t - 1}{m} mp−1​=mn/t−1​因此，我们可以看到，张量模型并行化增加了设备之间的通信量。因此，当 𝑡 大于单个节点中的 GPU 数量时，跨较慢的节点间链路执行张量模型并行化的开销可能是不切实际的。 𝑡 表示张量模型并行的规模，𝑝 表示流水线模型并行的规模，𝑑 表示数据并行的规模。 𝑏：微批次大小 𝑚：每个流水线中一个批次内的微批次数量 𝑛：GPU 的数量 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:5:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#tensor-and-pipeline-model-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Data and Model Parallelism 在使用张量模型并行化时，每个微批次都需要执行 all-reduce 通信。这在跨多 GPU 服务器时可能会非常昂贵 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:5:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#data-and-model-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Microbatch Size 微批次大小 b 的选择也会影响模型训练的吞吐量。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:5:3","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#microbatch-size"},{"categories":["ML Systems"],"collections":null,"content":"Activation Recomputation 激活重计算（Activation Recomputation）[12, 18, 20, 21] 是一种可选技术，通过在后向传递之前再次运行前向传递（并仅保存给定流水线阶段的输入激活值，而不是整个中间激活值集，后者占用内存更大），以增加计算操作的数量为代价来减少内存占用。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:5:4","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#activation-recomputation"},{"categories":["ML Systems"],"collections":null,"content":"IMPLEMENTATION 略 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:6:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#implementation"},{"categories":["ML Systems"],"collections":null,"content":"EVALUATION PTD-P 的性能如何？它是否能实现实际的端到端训练时间？ 流水线并行化在给定模型和批量大小下的扩展性如何？交错调度对性能有多大影响？ 不同的并行化维度之间如何交互？微批次大小等超参数的影响是什么？ scatter-gather 通信优化的影响是什么？在大规模运行训练迭代时，我们对硬件施加了哪些限制？ ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#evaluation"},{"categories":["ML Systems"],"collections":null,"content":"End-to-End Performance 评估了系统在参数量从 10 亿到 1 万亿的 GPT 模型上的端到端性能 启用了 scatter/gather 优化 的交错流水线调度 如何计算 FLOPS？ ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#end-to-end-performance"},{"categories":["ML Systems"],"collections":null,"content":"Comparison to ZeRO-3 PTD-P 在这两个模型上的性能比 ZeRO-3 高出 70%，这得益于更少的跨节点通信。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#comparison-to-zero-3"},{"categories":["ML Systems"],"collections":null,"content":"Pipeline Parallelism Weak Scaling. 较高的批量大小具有更好的扩展性，因为流水线气泡被分摊到更多的微批次上 Interleaved versus Non-Interleaved Schedule. 启用 scatter/gather 通信优化 的交错调度比非交错（默认）调度具有更高的计算性能。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:3","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#pipeline-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Comparison of Parallel Configurations ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:4","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#comparison-of-parallel-configurations"},{"categories":["ML Systems"],"collections":null,"content":"Microbatch Size ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:5","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#microbatch-size-1"},{"categories":["ML Systems"],"collections":null,"content":"Activation Recomputation ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:6","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#activation-recomputation-1"},{"categories":["ML Systems"],"collections":null,"content":"Scatter-Gather Optimization ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:7","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#scatter-gather-optimization"},{"categories":["ML Systems"],"collections":null,"content":"Fused Operators ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:8","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#fused-operators"},{"categories":["ML Systems"],"collections":null,"content":"Inter-Node Communication Bandwidth ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:9","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#inter-node-communication-bandwidth"},{"categories":["ML Systems"],"collections":null,"content":"Checkpoint Loading and Saving 各种配置 evaluation 略 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:7:10","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#checkpoint-loading-and-saving"},{"categories":["ML Systems"],"collections":null,"content":"RELATED WORK ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm-v2/:8:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM","uri":"/posts/paper-megatron-lm-v2/#related-work"},{"categories":["ML Systems"],"collections":null,"content":"Megatron-LM Nvidia 开源的 Megatron-LM 大模型训练框架 结合 Model Parallelism 和 Pipeline Parallelism 实现了 Tensor Model Parallelism 基于 Transformer 和 Attention 进行切分，同样是经典的一篇分布式语言模型训练的文章 论文比较短，细节很少，需要结合李沐大神的精读视频阅读，关于 AI 的东西还是得多看多动手，不然一知半解 柳浩“罗西的思考” 大神的 MegatronLM 源码分析 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:1:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#megatron-lm"},{"categories":["ML Systems"],"collections":null,"content":"Abstract 训练大型 Transformer 模型，由于内存限制，训练非常大的模型可能会非常困难 本文介绍了训练超大型 Transformer 模型的技术，并实现了一种简单高效的层内模型并行方法，使得训练具有数十亿参数的 Transformer 模型成为可能 orthogonal and complimentary to pipeline model parallelism, 可以通过在原生 PyTorch 中插入少量通信操作来完全实现 通过使用 512 个 GPU 收敛了高达 83 亿参数的基于 Transformer 的模型来展示这种方法 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:2:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#abstract"},{"categories":["ML Systems"],"collections":null,"content":"Introduction 随着这些模型变得越来越大，它们超出了现代处理器的内存限制，需要额外的内存管理技术，例如激活检查点 activation checkpointing 减小内存占用，丢弃大部分中间激活值 可扩展性：单 GPU 到 512 GPU 准确性：SOTA ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:3:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#introduction"},{"categories":["ML Systems"],"collections":null,"content":"Background and Challenges ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:4:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#background-and-challenges"},{"categories":["ML Systems"],"collections":null,"content":"Neural Language Model Pretraining ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:4:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#neural-language-model-pretraining"},{"categories":["ML Systems"],"collections":null,"content":"Transformer Language Models and Multi-Head Attention 当前 NLP 的研究趋势倾向于使用 Transformer 模型，最初的 Transformer 设计是一种机器翻译架构，通过编码器（Encoder）和解码器（Decoder）两部分将输入序列转换为输出序列。然而，最近利用 Transformer 进行语言建模的工作，如 BERT（Devlin 等，2018）和 GPT-2（Radford 等，2019），根据需求仅使用编码器或解码器。 值得注意的是，GPT-2 和 BERT 都使用了 GeLU（Hendrycks \u0026amp; Gimpel，2016）非线性和层归一化（Ba 等，2016）应用于多头注意力和前馈层的输入，而原始 Transformer（Vaswani 等，2017）使用 ReLU 非线性并将层归一化应用于输出。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:4:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#transformer-language-models-and-multi-head-attention"},{"categories":["ML Systems"],"collections":null,"content":"Data and Model Parallelism in Deep Learning 将深度神经网络训练扩展到多个硬件加速器时，有两种核心范式： data parallelism(Valiant, 1990): 将训练小批量数据拆分到多个工作节点上； model parallelism: 将模型的内存使用和计算分布到多个工作节点上。 然而，这些技术在可处理的问题规模上存在一个根本性限制：模型必须完全适合单个工作节点。随着 BERT 和 GPT-2 等语言模型的规模和复杂性不断增加，神经网络已接近现代硬件加速器的内存容量。 Within model parallelism, there are two further paradigms: layer-wise pipeline parallelism: TensorFlow GPipe 然而，这种方法需要额外的逻辑来高效处理通信和计算操作的流水线，并且存在管道气泡（pipeline bubbles）问题，降低了效率，或者需要修改优化器本身，从而影响准确性。 distributed tensor computation: 正交且更通用的方法，FlexFlow，Mesh-TensorFlow 等等 我们利用了与 Mesh-TensorFlow 类似的思路，并通过并行计算 Transformer 的注意力头来实现 Transformer 模型的并行化，仅对现有的 PyTorch Transformer 实现进行了一些有针对性的修改。不需要任何新的编译器或代码重写，只需插入一些简单的原语即可完全实现。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:4:3","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#data-and-model-parallelism-in-deep-learning"},{"categories":["ML Systems"],"collections":null,"content":"Model Parallel Transformers 利用 Transformer 网络的结构，通过添加少量同步原语，实现了一种简单的模型并行方法。 Transformer 层由一个自注意力块和一个两层的多层感知机（MLP） ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:5:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#model-parallel-transformers"},{"categories":["ML Systems"],"collections":null,"content":"MLP 块的并行化 MLP 块的第一部分是一个 GEMM（通用矩阵乘法）操作，后接一个 GeLU 非线性激活函数 Y=GeLU(XA) Y=GeLU(XA) Y=GeLU(XA)并行化 GEMM 的一种选择是将权重矩阵 A 沿其行 rows 分割输入 X 沿其列 columns 分割 X=[X1 X2],A=[A1A2] X = [X_1 \\ X_2], \\quad A = \\begin{bmatrix} A_1 \\\\ A_2 \\end{bmatrix} X=[X1​ X2​],A=[A1​A2​​]这种分区会导致 Y=GeLU(X1A1+X2A2)Y = \\text{GeLU}(X_1A_1 + X_2A_2)Y=GeLU(X1​A1​+X2​A2​)，由于 GeLU 是一个非线性函数（GeLU(X1A1+X2A2)≠GeLU(X1A1)+GeLU(X2A2)\\text{GeLU}(X_1A_1 + X_2A_2) \\neq \\text{GeLU}(X_1A_1) + \\text{GeLU}(X_2A_2)GeLU(X1​A1​+X2​A2​)=GeLU(X1​A1​)+GeLU(X2​A2​) ，需要在 GeLU 函数之前添加一个同步点。 另一种选择是将 AAA 沿其列 column 分割 A=[A1 A2]A = [A_1 \\ A_2]A=[A1​ A2​]。这种分区允许 GeLU 非线性独立应用于每个分区 GEMM 的输出： [Y1 Y2]=[GeLU(XA1) GeLU(XA2)] [Y_1 \\ Y_2] = [\\text{GeLU}(XA_1) \\ \\text{GeLU}(XA_2)] [Y1​ Y2​]=[GeLU(XA1​) GeLU(XA2​)]这种方法的优势在于它移除了一个同步点。因此，我们以这种列并行 column parallel 的方式对第一个 GEMM 进行分区，并将第二个 GEMM 沿其行 rows 分割，使其直接接收 GeLU 层的输出，而无需任何通信， GEMM（General Matrix Multiply） 在前向传播中只需要一次 all-reduce 操作（g 操作符），在反向传播中也只需要一次 all-reduce 操作（f 操作符）。这两个操作符互为共轭，可以在 PyTorch 中用几行代码实现。 class f(torch.autograd.Function): def forward(ctx, x): return x def backward(ctx, gradient): all_reduce(gradient) return gradient","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:5:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#mlp-块的并行化"},{"categories":["ML Systems"],"collections":null,"content":"自注意力块的并行化 对于自注意力块，我们利用了多头注意力操作中的固有并行性，将键（K）、查询（Q）和值（V）相关的 GEMM 以列 column parallel 并行的方式分区，使得每个注意力头对应的矩阵乘法在单个 GPU 上本地完成 The transformer language model has an output embedding with the dimension of hidden-size (H) times vocabulary size (v) 由于现代语言模型的词汇表大小通常在数万个词（例如，GPT-2 使用了 50,257 的词汇表大小），因此并行化输出嵌入的 GEMM 操作是有益的。然而，在 Transformer 语言模型中，输出嵌入层与输入嵌入共享权重，因此需要对两者进行修改。 输入嵌入权重矩阵 EH×vE_{H\\times v}EH×v​ 沿词汇表维度并行化，按列分割 这里省略 embedding 没太看懂 我们的模型并行方法主要通过减少通信并保持 GPU 的计算负载来实现优化。与其让一个 GPU 计算部分 dropout、层归一化或残差连接并将结果广播到其他 GPU，我们选择在 GPU 之间复制计算。 附录 B 中提供了关于混合模型和数据并行以及随机数生成处理的更多细节以供参考。总之，我们上述的方法实现简单，只需在前向和反向传播中添加少量额外的 all-reduce 操作。它不需要编译器，并且与管道模型并行是正交且互补的。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:5:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#自注意力块的并行化"},{"categories":["ML Systems"],"collections":null,"content":"Setup GPT-2, BERT ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:6:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#setup"},{"categories":["ML Systems"],"collections":null,"content":"Training Dataset 聚合了几个最大的语言建模数据集 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:6:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#training-dataset"},{"categories":["ML Systems"],"collections":null,"content":"Training Optimization and Hyperparameters 混合精度训练和动态损失缩放 好奇为什么论文没提到 FP16 FP32 等等 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:6:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#training-optimization-and-hyperparameters"},{"categories":["ML Systems"],"collections":null,"content":"Experiments 32 台 DGX-2H 服务器（共 512 个 Tesla V100 SXM3 32GB GPU） ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:7:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#experiments"},{"categories":["ML Systems"],"collections":null,"content":"Scaling Analysis GPT-2 models with four sets of parameters detailed in Table1 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:7:1","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#scaling-analysis"},{"categories":["ML Systems"],"collections":null,"content":"MODEL AND DATA PARALLELISM 展示模型并行和模型+数据并行情况下关于模型参数的弱扩展性 弱扩展性 weak scaling 代表了什么？怎么计算？ 是否应该稳定或者轻微下降才表示扩展良好 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:7:2","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#model-and-data-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Language Modeling Results Using GPT-2 表 2 还列出了完成一个 epoch 所需的时间，相当于 68,507 次迭代。例如，对于在 512 个 GPU 上训练的 8.3B 模型，每个 epoch 大约需要两天时间。与表 1 中用于扩展性研究的配置相比，2.5B 模型相同，8.3B 模型有 24 个注意力头而不是 32 个，而 355M 模型比之前看到的任何模型都小得多，但仍然使用 64 个 GPU 进行训练，因此每个 epoch 的时间大大减少。 验证困惑度（perplexity）随迭代次数的变化，随着模型规模的增加，验证困惑度下降，8.3B 模型的验证困惑度达到 9.27 微软的研究人员与 NVIDIA 合作，使用 Megatron 训练了一个 170 亿参数的 GPT-2 模型，称为 Turing-NLG（Microsoft，2020），并展示了随着模型规模的扩大，准确率进一步提高，凸显了大型模型的价值。 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:7:3","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#language-modeling-results-using-gpt-2"},{"categories":["ML Systems"],"collections":null,"content":"Bi-directional Transformer Results Using BERT 略 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:7:4","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#bi-directional-transformer-results-using-bert"},{"categories":["ML Systems"],"collections":null,"content":"Conclusion and Future Work 比较短的一篇论文，方法很粗暴简单，但也很有效 对于 DL/LLM Training 我其实很难想象这些并行/分布式训练是几年前才热门，还需要阅读更多 尤其是 Megatron 的源码 ","date":"2025-02-26","objectID":"/posts/paper-megatron-lm/:8:0","tags":["Paper Reading","Deep Learning","Language Models","Training"],"title":"Paper Reading: Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism","uri":"/posts/paper-megatron-lm/#conclusion-and-future-work"},{"categories":["ML Systems"],"collections":null,"content":"PipeDream: Generalized Pipeline Parallelism for DNN Training 第一次看 ML/DL (distributed) training 框架相关的论文，有很多地方不理解。 对 Evaluation 与数学证明更是浅尝辄止，许多指标和算法难以理解，关于大模型、分布式训练等等有许多总结得非常好的博客和分享，参照着多看看加深记忆。 流水线并行分布式训练一篇经典的文章，结合 DP, MP 以及 Pipeline 这篇和 Gpipe 非常相似： GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism 类似还有 MegatronLM 团队的论文 Megatron-LM Gpipe 知乎讲解 关于 Gpipe 和 PipeDream 源码详解可以参考柳浩“罗西的思考”大神的博客 Gpipe 源码解析 PipeDream 一个很有意思的 笔记 by jasperzhong 详细解释了 pipedream 做 partition 的时候为什么可以用动态规划 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:1:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#pipedream-generalized-pipeline-parallelism-for-dnn-training"},{"categories":["ML Systems"],"collections":null,"content":"ABSTRACT 深度神经网络（DNN）的训练极为耗时，需高效的多加速器并行化。 当前的训练并行化方法主要使用 intra-batch parallelization, 但在较高工人数目时会遇到边际收益递减 PipeDream: adds inter-batch pipelining to intra-batch parallelism to further improve parallel training throughput 与传统流水线不同，DNN 训练是双向的，计算图的前向传播之后是反向传播，后者使用前向传播期间计算的状态和中间数据 PipeDream 对模型参数进行版本控制以确保梯度计算的数值正确性，并在不同工作节点上同时调度不同小批次的前向和反向传播，以最小化流水线停顿。 intra-batch parallelism techniques 比如传统的数据并行 DP 和模型并行 MP https://leimao.github.io/blog/Data-Parallelism-vs-Model-Paralelism/ 毛磊大神的博客带有 Data Parallelism 的证明 他的观点也很有意思，Model Parallelism 并不是真的并行，而是 Model Serialization ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:2:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#abstract"},{"categories":["ML Systems"],"collections":null,"content":"INTRODUCTION Deep Neural Networks (DNNs) 训练所需的计算成本显著增加，需要在多个 accelerators（e.g., GPUs）上进行并行执行。 DNN 训练通过前向传播和反向传播的迭代计算进行，在每次迭代中，训练循环处理一个小批次（minibatch）的输入数据，并更新模型参数。当前的方法主要集中在将优化算法的每次迭代在一组工作节点上并行化。 数据并行将输入数据分配到不同工作节点上 模型并行将算子分配到不同工作节点上 而混合方案则同时分配数据和模型 然而，批内并行化在大规模训练中可能会面临高通信开销的问题 在 32 个 GPU 上，某些模型的通信开销（以通信停顿时间占总时间的百分比计算）高达 90%，这是由于跨服务器的 all_reduce 通信成本高昂。即使在使用专用互连（如 NVLink [4]）的服务器上，通信开销仍然很高。此外，GPU 计算能力的快速提升将进一步使训练的瓶颈转向通信。 PipeDream: combining intra-batch parallelism with inter-batch parallelization 怎么减少通信开销？看之前 Gpipe 的实验大部分时间都花费在通信上，较小的批次会带来更多开销吗 PipeDream 将模型分配到可用工作节点上，为每个工作节点分配一组连续的算子 consecutive operators（在 DNN 术语中称为层 layers），然后以流水线方式重叠不同输入的计算和通信。这一过程可以显著减少工作节点间的通信，因为它将通信限制在分配到不同工作节点的连续层之间的层输入和输出（前向传播中的激活值和反向传播中的梯度），对于许多模型来说，这些通信量远小于整个模型的大小。此外，这种通信是点对点的，而不是全对全的。 PipeDream 采用了一种更精细的流水线方法：给定一个由分配到不同工作节点的连续层组（称为阶段 stage）组成的流水线，PipeDream 使用一种称为 1F1B 的调度算法来保持硬件的高利用率，同时实现与数据并行相似的语义。 在 1 Forward 1 Backward 的稳态下，每个工作节点严格交替执行其阶段的前向传播和反向传播，确保高资源利用率（negligible pipeline stalls, no pipeline flushes） PipeDream 在多 GPU 上效果很好 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:3:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#introduction"},{"categories":["ML Systems"],"collections":null,"content":"BACKGROUND AND RELATED WORK 深度神经网络（DNN）模型由许多算子组成，这些算子被组织成层。 没正经学过 ML DL，这里的算子可以理解为各种操作吗？比如卷积、矩阵乘法或者激活函数？然后组织成一个层，卷积层等等 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:4:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#background-and-related-work"},{"categories":["ML Systems"],"collections":null,"content":"Intra-batch Parallelism 在并行化 DNN 训练时，这些层可以以不同的方式分配到可用工作节点上。 Data Parallelism: 输入数据被分配到不同工作节点上，同时定期通过集体通信原语（如 all_reduce [24]）或参数服务器 [38] 与其他工作节点同步权重。最常用的数据并行形式称为 bulk synchronous parallel or BSP Model Parallelism: intra-batch 将 DNN 模型中的算子分配到可用工作节点上，每个工作节点仅评估和更新模型参数的一个子集（针对所有输入）。通信的数据量是需要跨工作节点传输的中间输出（及相应的梯度）的大小。 Hybrid Intra-batch Parallelism: OWT, FlexFlow \u0026hellip; 怎么理解 intra-batch 和 inter-batch parallelism 一次训练的同一批次，比如一些数据/模型拆分，data/model 分配到不同 worker 并行，加以流水线 https://blog.csdn.net/weixin_36378508/article/details/129838193 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:4:1","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#intra-batch-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Inter-batch Parallelism GPipe（与 PipeDream 的早期预印本 [25] 同时期的工作）在模型并行训练的背景下使用流水线技术来训练非常大的模型 GPipe 进一步将一个小批次拆分为 m 个微批次（microbatches） 并对这些微批次执行前向传播，然后进行反向传播（见图，m=4） Gpipe 还使用了 权重梯度聚合（Weight Gradient Aggregation） 和 通过丢弃激活值存储来以计算换内存（Trading Computation for Memory by Discarding Activation Stashes） 前者是否是 All-Reduce？ ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:4:2","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#inter-batch-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"DNN Model and Hardware Diversity DNN 模型具有多样性，常见的模型包括卷积层、LSTM [55]、注意力层 [53] 和全连接层。这些不同类型的模型在使用不同的并行化策略时表现出截然不同的性能特征，因此最优的并行化策略高度依赖于模型本身。 不同模型不同的并行表现，Pipedream 是更通用的选择还是？ ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:4:3","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#dnn-model-and-hardware-diversity"},{"categories":["ML Systems"],"collections":null,"content":"PIPELINE PARALLELISM PipeDream 使用了一种新的并行化策略——流水线并行化 Pipeline，结合了批内并行化和批间并行化。流水线并行化将 DNN 模型的层划分为多个阶段（stage），每个阶段由模型中一组连续的层组成。每个阶段被映射到一个单独的 GPU 上，该 GPU 负责执行该阶段中所有层的前向传播和反向传播。 pipedream 是把一个 batch 切分的更细吗，这样一小段就可以立刻发给下一个 gpu 开始 forward？ 这样利用率更高， gpu bubble 减少 流水线并行化优于批内并行化的原因： 减少了通信量（小于 DP），DP 需要聚合，PP 只需要发给另一个节点 重叠通信和计算：异步通信使得前向传播的激活值和反向传播的梯度可以在不同阶段之间重叠计算和通信 这种训练方式一般如何保证正确性？如何切分才能保证负载均衡 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:5:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#pipeline-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Challenge 1: Work Partitioning 与任何流水线一样，最终流水线的稳态吞吐量取决于最慢阶段的吞吐量 工作节点之间的过多通信也会降低训练流水线的吞吐量 Solution: PipeDream Optimizer 输出一个平衡的流水线 PipeDream 在初始分析步骤中记录每个层的前向和反向传播的计算时间、层输出的激活值大小以及相关参数的大小；这些分析数据作为优化器划分算法的输入（图 6）。划分算法还考虑了其他约束条件，如硬件拓扑和带宽、工作节点数量以及计算设备的内存容量。 Profiler: Partitioning Algorithm: PipeDream 的优化器从最低级别到最高级别逐步解决动态规划问题 省略公式，转化成了一个 DP 问题 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:5:1","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#challenge-1-work-partitioning"},{"categories":["ML Systems"],"collections":null,"content":"Challenge2: Work Scheduling 流水线中的每个活跃小批量数据可能处于不同的阶段，要么在前向传播中，要么在反向传播中。 Solution: 在启动阶段，输入阶段会接收足够多的小批量数据，以使流水线在稳态下保持满负荷 一旦进入稳态，每个阶段会交替执行一个小批量数据的前向传播和另一个较早小批量数据的反向传播。我们称之为“一前一后”（1F1B）调度。 “一前一后轮询” 1F1B-RR：当一个阶段以数据并行配置运行（在多个 GPU 上复制）时，我们使用基于小批量数据标识符的确定性轮询负载均衡，将工作分配到各个副本上 观察到在实践中反向传播总是比前向传播耗时更长。1F1B-RR 仍然是一种有效的调度机制 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:5:2","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#challenge2-work-scheduling"},{"categories":["ML Systems"],"collections":null,"content":"Challenge3: Effective Learning 在一个简单的流水线系统中，每个阶段的前向传播使用一个版本的参数，而其反向传播则使用另一个版本的参数 因此，在阶段 1 中，小批量数据 5 的反向传播中使用的权重与对应前向传播中使用的权重不同；这种权重版本的不一致会导致无效的梯度，并可能阻止模型收敛。 Solution: PipeDream 使用一种称为权重暂存（weight stashing）的技术来避免前向传播和反向传播中使用的权重版本之间的根本性不匹配。 Memory Overhead：流水线并不会显著增加每个工作节点的内存使用量，即使使用权重暂存也是如此。 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:5:3","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#challenge3-effective-learning"},{"categories":["ML Systems"],"collections":null,"content":"Implementation PipeDream 的接口实现为一个独立的 Python 库，约 3000 行代码，负责管理设备内存、调度工作并处理通信。 PipeDream 使用 PyTorch 进行自动微分和执行算子；然而，PipeDream 是可扩展的，并且可以与其他机器学习框架（如 TensorFlow、MXNet 和 CNTK）一起工作。 Parameter State: PipeDream 将所有与阶段相关的参数直接保存在 GPU 内存中，只有在使用更新参数的向后传播完成后，参数才会被丢弃。 Intermediate State: 每个阶段的输入和输出数据被分配一个唯一的 blob ID。当从前一个阶段（或输入阶段从磁盘）接收到中间数据时，PipeDream 将中间数据复制到 GPU 内存中，并将指向相关缓冲区的指针放入工作队列中 Stage Replication: PipeDream 使用 PyTorch 的 DistributedDataParallel 库 [6] 来同步数据并行阶段的层的参数。 Checkpointing: PipeDream 支持定期检查模型参数以实现容错，默认在每个 epoch 结束时跨阶段创建检查点。 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:6:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#implementation"},{"categories":["ML Systems"],"collections":null,"content":"EVALUATION PipeDream 在不同硬件部署上的多种学习任务中显著加速了达到目标精度的时间； PipeDream 比其他最近提出的跨批次方法更高效； PipeDream 大大减少了通信开销，并且与数据并行训练相比，并未显著增加内存占用； 结合流水线并行、模型并行和数据并行优于单独的模型并行、数据并行或混合并行。 减少通信开销，并未显著增加内存占用 批次如何设置？ Batch Sizes and Training Methodology: 我们使用适合单个 GPU 内存的最大每 GPU 小批量数据——更大的批量会导致内存不足异常。这确保我们在单个设备上达到峰值 FLOPs。 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:7:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#evaluation"},{"categories":["ML Systems"],"collections":null,"content":"Comparison to Data Parallelism 表 1 总结了 PipeDream 与数据并行训练（DP）的比较结果。表中显示了 PipeDream 自动生成的配置及其在达到目标精度训练时间上相对于相应数据并行配置的加速比。 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:7:1","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#comparison-to-data-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Comparison to Other Intra-batch Parallelism Schemes Model Parallelism: For VGG-16 and AlexNet 14.9x and 6.5x ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:7:2","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#comparison-to-other-intra-batch-parallelism-schemes"},{"categories":["ML Systems"],"collections":null,"content":"Comparison to Inter-batch Parallelism Gpipe: These throughput slow downs are largely due to more frequent pipeline flushes compared to PipeDream ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:7:3","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#comparison-to-inter-batch-parallelism"},{"categories":["ML Systems"],"collections":null,"content":"Microbenchmarks 优化器：PipeDream 的优化器非常高效，对于所有评估的模型和硬件部署，生成最佳训练配置的时间不到 8 秒。 内存占用：图 16 显示了 PipeDream 在 4 阶段配置下三个不同模型的每阶段内存占用。PipeDream 的最坏情况内存占用与数据并行相当，尽管 PipeDream 存储了多个权重和激活版本。 通信开销： 流水线深度的影响：最佳非 DP 配置的通信开销远低于 DP 配置的通信开销 optimizer 如何实现？ ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:7:4","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#microbenchmarks"},{"categories":["ML Systems"],"collections":null,"content":"CONCLUSION 流水线并行的 DNN 训练有助于减少可能成为批次内并行瓶颈的通信开销。PipeDream 自动将 DNN 训练划分到多个工作节点上，结合跨批次流水线与批次内并行，以更好地重叠计算与通信，同时最小化通信数据量。 pipeline 的实现？很好奇 optimizer 如何实现，profiling 是提前采样吗？ 基于 pytorch 的简单实现 PipeDream 实现 在 gpipe 和 pipedream 有个很有意思的 tradeoff： 由于 GPU 显存反而更加珍贵，会使用 checkpoint 时间换空间 pipedream 更加偏向异步、gpipe 是同步 ","date":"2025-02-24","objectID":"/posts/paper-pipedream/:8:0","tags":["Paper Reading","Deep Learning","Training"],"title":"Paper Reading: PipeDream: Generalized Pipeline Parallelism for DNN Training [SOSP2019]","uri":"/posts/paper-pipedream/#conclusion"},{"categories":["ML Systems"],"collections":null,"content":"\u0026ldquo;Manual\u0026rdquo; Neural Networks / Backprop 还是复习 ML 的内容 From linear to nonlinear hypothesis classes Neural networks Backpropagation (i.e., computiing gradients) hypothesis classes 是用于模型训练的函数类型 线性假设类、非线性假设类 多项式回归或神经网络等模型都属于非线性假设类，能捕捉更复杂的关系 以下是一个两层神经网络的简化表示： hθ(x)=θ2Tσ(W1Tx)h_\\theta(x) = \\theta_2^T \\sigma(W_1^T x)hθ​(x)=θ2T​σ(W1T​x)其中，W1W_1W1​ 和 W2W_2W2​ 是权重矩阵，σ\\sigmaσ是非线性激活函数，如 ReLU 或 sigmoid。 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:1:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#manual-neural-networks--backprop"},{"categories":["ML Systems"],"collections":null,"content":"nonlinear hypothesis classes linear hypothesis classes: h_θ(x)=θTxh\\_\\theta(x) = \\theta^T xh_θ(x)=θTx 线性的分类可能可以 fit 一些情况，但如果类型是分散的圆圈等等情况，可能难以拟合？ One idea: apply a linear classifier to some (potentially higher-dimensional) features of the data: hθ(x)=θTϕ(x),θ∈Rd×k,ϕ:Rn→Rdh_\\theta(x) = \\theta^T \\phi(x), \\theta \\in R^{d\\times k}, \\phi: R^n \\rightarrow R^dhθ​(x)=θTϕ(x),θ∈Rd×k,ϕ:Rn→Rd phi 把 n 维的输入变成 d 维的 隐藏层节点的激活函数（如 ReLU）可以视为一种非线性特征映射，这使得神经网络能够捕捉到数据中的复杂非线性关系。 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:2:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#nonlinear-hypothesis-classes"},{"categories":["ML Systems"],"collections":null,"content":"How do we create features? manual engineering, the “old” way of doing machine learning In a way that itself is learned from data, the “new” way of doing ML 传统机器学习，手动提取特征比如房价预测里的面积、房间特征 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:2:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#how-do-we-create-features"},{"categories":["ML Systems"],"collections":null,"content":"Neural networks / deep learning Neural Network, a particular type of hypothesis class, multiple, parameterized differentiable functions (a.k.a. “layers”) composed together Deep Network, synonym for \u0026ldquo;neural network\u0026rdquo;, composing together a lot of functions, so “deep” is typically an appropriate qualifier 深度学习，指的就是用神经网络的机器学习？ two layer neural network hθ(x)=W2Tσ(W1Tx)h_\\theta(x) = W^T_2\\sigma(W_1^Tx)hθ​(x)=W2T​σ(W1T​x) where σ:R→R\\sigma: R \\rightarrow Rσ:R→R is nonlinear function like ReLU or sigmoid σ\\sigmaσ 非线性函数, 比如 ReLU，sigmod θ\\thetaθ 可以看作是参数 batch matrix form: hθ(X)=σ(XW1)W2 h_\\theta(X) = \\sigma(X W_1) W_2 hθ​(X)=σ(XW1​)W2​ 为什么加一个 non linear 就可以表示这么多结果？ 数学上：神经网络已经被证明为通用函数逼近器（Universal Function Approximators） 理论上，只要网络结构足够复杂（隐藏单元和层数足够多），它就能近似任何连续函数。 之前学过一点数值计算，当时还没接触到非线性函数可以拟合的函数，基本都是线性/指数或者分段/平滑等等，应该继续学一下非线性拟合的 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:2:2","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#neural-networks--deep-learning"},{"categories":["ML Systems"],"collections":null,"content":"Fully-connected deep networks 𝐿-layer neural network – a.k.a. \u0026ldquo;Multi-layer perceptron\u0026rdquo; (MLP) Zi+1=σi(ZiWi),i=1,…,LZ_{i+1} = \\sigma_i(Z_i W_i), \\quad i = 1, \\dots, LZi+1​=σi​(Zi​Wi​),i=1,…,Lhθ(X)=ZL+1h_\\theta(X) = Z_{L+1}hθ​(X)=ZL+1​ 每一层的输入都是上一层的 非线性函数 (输出 x 权重矩阵) ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:3:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#fully-connected-deep-networks"},{"categories":["ML Systems"],"collections":null,"content":"why deep networks? work like the brain? parity 奇偶性无法学习？ empirically it seems like they work better for a fixed parameter count 多层结构/深层网络能更均匀地分布参数 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:4:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#why-deep-networks"},{"categories":["ML Systems"],"collections":null,"content":"Backpropagation neural networks: Hypothesis Class: MLP Loss function: cross-entropy loss Optimization procedure: SGD min⁡θ1m∑i=1mℓce(hθ(xi),yi)\\min_\\theta \\frac{1}{m} \\sum_{i=1}^m \\ell_{ce}(h_\\theta(x_i), y_i) θmin​m1​i=1∑m​ℓce​(hθ​(xi​),yi​) 我知道这些组件，但具体如何运作？ ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:5:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#backpropagation"},{"categories":["ML Systems"],"collections":null,"content":"The gradient(s) of a two-layer network ∇{W1,W2}ℓce(σ((XW1)W2),y)=σ(XW1)T⋅(S−Iy) \\nabla_{\\{W_1, W_2\\}} \\ell_{ce}(\\sigma((XW_1)W_2), y) = \\sigma(XW_1)^T \\cdot (S - I_y) ∇{W1​,W2​}​ℓce​(σ((XW1​)W2​),y)=σ(XW1​)T⋅(S−Iy​) 链式法则（chain rule）计算偏导数 偏导数是多变量函数对其中一个变量的变化率，而保持其他变量固定不变 梯度是多变量函数所有偏导数组成的向量，梯度的方向用于描述函数值增长最快的路径，负方向是函数值下降最快的路径 对多元函数，假设有个初始点 (x0,y0)(x_0, y_0)(x0​,y0​) 可以求出梯度，更新其值 x1=x0−η⋅∂f∂xx_1 = x_0 - \\eta \\cdot \\frac{\\partial f}{\\partial x}x1​=x0​−η⋅∂x∂f​ 其中 η\\etaη 是学习率，一旦梯度足够小，就可以减少原函数的值，也就是损失函数的值减少 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:5:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#the-gradients-of-a-two-layer-network"},{"categories":["ML Systems"],"collections":null,"content":"Backpropagation “in general” consider our fully-connected network Z_i+1=σi(ZiWi),i=1,...,L∂ℓ∂Wi=∂ℓ∂ZL+1⋅∂ZL+1∂ZL⋅∂ZL∂ZL−1⋅…⋅∂Zi+2∂Zi+1⋅Gi+1 Z\\_{i+1} = \\sigma_i(Z_i W_i), i = 1, ..., L \\\\ \\frac{\\partial \\ell}{\\partial W_i} = \\frac{\\partial \\ell}{\\partial Z_{L+1}} \\cdot \\frac{\\partial Z_{L+1}}{\\partial Z_L} \\cdot \\frac{\\partial Z_L}{\\partial Z_{L-1}} \\cdot \\ldots \\cdot \\frac{\\partial Z_{i+2}}{\\partial Z_{i+1}} \\cdot G_{i+1} Z_i+1=σi​(Zi​Wi​),i=1,...,L∂Wi​∂ℓ​=∂ZL+1​∂ℓ​⋅∂ZL​∂ZL+1​​⋅∂ZL−1​∂ZL​​⋅…⋅∂Zi+1​∂Zi+2​​⋅Gi+1​Then we have a simple ”backward” iteration to compute the GiG_iGi​’s Gi=Gi+1⋅∂ℓ∂Zi+1 G_i = G_{i+1} \\cdot \\frac{\\partial \\ell}{\\partial Z_{i+1}} Gi​=Gi+1​⋅∂Zi+1​∂ℓ​","date":"2025-02-10","objectID":"/posts/ml-sys-02/:6:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#backpropagation-in-general"},{"categories":["ML Systems"],"collections":null,"content":"Computing the real gradients ∇Wiℓ=ZiT⋅(Gi+1∘σi′(ZiWi)) \\nabla_{W_i} \\ell = Z_i^T \\cdot (G_{i+1} \\circ \\sigma_i\u0026#x27;(Z_i W_i)) ∇Wi​​ℓ=ZiT​⋅(Gi+1​∘σi′​(Zi​Wi​)) 链式法则矩阵计算是反向传播（Backpropagation）的核心思想。它利用链式法则（Chain Rule）和矩阵计算，逐层向后传递误差 其中 ZiZ_iZi​ 是可以复用的 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:6:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#computing-the-real-gradients"},{"categories":["ML Systems"],"collections":null,"content":"Backpropagation: Forward and backward passes 为什么这些计算是“同时”发生的？ 前向传播的过程中，我们已经缓存了每一层的输出 ZiZ_iZi​ 和激活函数的导数 σ′(ZiWi)\\sigma\u0026#x27;(Z_i W_i)σ′(Zi​Wi​) 后向传播时，利用这些缓存值直接进行梯度计算，避免重复计算中间值。 What is really happening with the backward iteration? ∂ℓ∂Wi=∂Zi+1∂Wi⋅∂ℓ∂ZL+1⋅∂ZL+1∂ZL⋅…⋅∂Zi+2∂Zi+1 \\frac{\\partial \\ell}{\\partial W_i} = \\frac{\\partial Z_{i+1}}{\\partial W_i} \\cdot \\frac{\\partial \\ell}{\\partial Z_{L+1}} \\cdot \\frac{\\partial Z_{L+1}}{\\partial Z_L} \\cdot \\ldots \\cdot \\frac{\\partial Z_{i+2}}{\\partial Z_{i+1}} ∂Wi​∂ℓ​=∂Wi​∂Zi+1​​⋅∂ZL+1​∂ℓ​⋅∂ZL​∂ZL+1​​⋅…⋅∂Zi+1​∂Zi+2​​Each layer needs to be able to multiply the “incoming backward” gradient Gi+1G_{i+1}Gi+1​ by its derivatives ∂Zi+1∂Wi\\frac{\\partial Z_{i+1}}{\\partial W_i}∂Wi​∂Zi+1​​ an operation called the “vector Jacobian product” automatic differentiation 每一层的梯度 GiG_iGi​ 是从后一层的梯度 Gi+1G_{i+1}Gi+1​ 递归计算得到。 向量雅可比乘积 反向传播可以推广到任意计算图，成为实现自动微分的基础 ","date":"2025-02-10","objectID":"/posts/ml-sys-02/:6:2","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 02-03 Neural Networks","uri":"/posts/ml-sys-02/#backpropagation-forward-and-backward-passes"},{"categories":["ML Systems"],"collections":null,"content":"Deep Learning Systems https://dlsyscourse.org/ 10-414/714: Deep Learning Systems 这学期选了 ML systems，一些 DL 的概念不太熟悉，补一下 Tianqi Chen 大神的课 ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:1:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#deep-learning-systems"},{"categories":["ML Systems"],"collections":null,"content":"Introduction AlexNet: Image Classification AlphaGo, StyleGAN, GPT-3, stable diffusion \u0026hellip; DL 是深度神经网络，越深越好吗？ transformer 和 GAN 也都是深度学习 CNN：卷积层、池化、全连接 RNN, LSTM, GAN, Transformer\u0026hellip; Pytorch, Tensorflow, \u0026hellip; 深度学习和传统机器学习的区别是自动微分吗？ ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:2:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#introduction"},{"categories":["ML Systems"],"collections":null,"content":"ML Refresher / Softmax Regression 回顾 ML 里的图像识别任务 其实不知道要不要先看看 ML 的基础课，很多 ML 的基础也不太记得了 Machine Learning as data-driven programming classify handwritten drawing of digits computers dont see pics like us (supervised) ML approach: train with known labels -\u0026gt; ML algo -\u0026gt; Model h ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:0","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#ml-refresher--softmax-regression"},{"categories":["ML Systems"],"collections":null,"content":"3 ingredients hypothesis class: program structure, a set of parameters, how we map inputs (images of digits) to ouputs (labels, probabilities) loss function: a function specifies how well a given hypothesis performs optimization method: a procedure for determining a set of params that minimize the sum of losses over the training set 框架都是一样的 ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:1","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#3-ingredients"},{"categories":["ML Systems"],"collections":null,"content":"softmax regression x(i)∈Rny(i)∈{1,…,k}fori=1,…,m \\begin{align} x^{(i)} \u0026amp; \\in \\mathbb{R}^n \\\\ y^{(i)} \u0026amp; \\in \\{1, \\ldots, k\\} \\quad \\text{for} \\quad i = 1, \\ldots, m \\end{align} x(i)y(i)​∈Rn∈{1,…,k}fori=1,…,m​​ R\\mathbb{R}R 表示实数集（Real numbers）。因此，公式中的 x(i)∈Rnx^{(i)} \\in \\mathbb{R}^n x(i)∈Rn 意思是每个数据点 x(i)x^{(i)}x(i) 是一个 nnn 维的实数向量。也就是说，x(i)x^{(i)}x(i) 是一个包含 nnn 个实数的向量，每个数据点的特征向量。 比如 x(i)=(2.5−1.03.7)∈R3x^{(i)} = \\begin{pmatrix} 2.5 \\\\ -1.0 \\\\ 3.7 \\end{pmatrix} \\in \\mathbb{R}^3x(i)=​2.5−1.03.7​​∈R3 y(i)y^{(i)}y(i) 是每个数据点对应的标签 nnn 输入数据的维度（特征数量） kkk 类别的数量，也就是 0 到 9 的数字 mmm 训练集中的数据点数量 Our hypothesis function maps inputs x(i)∈Rnx^{(i)} \\in \\mathbb{R}^nx(i)∈Rn to kkk-dimensional vectors h:R𝑛→Rk h: \\mathbb{R}^𝑛 \\rightarrow \\mathbb{R}^k h:Rn→Rkhi(x)h_i(x)hi​(x) indicates some measure of “belief” in how much likely the label is to be class iii h_i 表示是 class i 的“可能性”，但目前不一定是概率？ A linear hypothesis function uses a linear operator (i.e. matrix multiplication) for this transformation 线性假设函数，将输入 n 维输入 x 映射到 k 维 线性假设函数使用线性算子（即矩阵乘法）进行这种变换 hθ(x)=θTx h_\\theta(x) = \\theta^T x hθ​(x)=θTx 其中 θ\\thetaθ 是 n×kn\\times kn×k 维度的，一个权重矩阵，最后输出一个 k 维向量，每个元素对应每个类别的置信度 ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:2","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#softmax-regression"},{"categories":["ML Systems"],"collections":null,"content":"Matrix batch notation 矩阵运算更加高效，GPU/CPU ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:3","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#matrix-batch-notation"},{"categories":["ML Systems"],"collections":null,"content":"Loss Function classification error ℓerr(h(x),y)={0if arg⁡max⁡ihi(x)=y1otherwise \\ell_{\\text{err}}(h(x), y) = \\begin{cases} 0 \u0026amp; \\text{if } \\arg\\max_i h_i(x) = y \\\\ 1 \u0026amp; \\text{otherwise} \\end{cases} ℓerr​(h(x),y)={01​if argmaxi​hi​(x)=yotherwise​ 分类错误损失函数，只检查分类是否失败 argmax 就是取最大的可能性，是否等于标签 y 分类错误损失函数不适合作为优化过程中选择最佳参数的损失函数，因为它不可微分 在优化算法中，我们通常需要用到梯度信息，而分类错误损失函数由于其离散的性质，无法提供连续的梯度。 softmax / cross-entropy loss zi=p(label=i∣x)=exp⁡(hi(x))∑j=1kexp⁡(hj(x)) z_i = p(\\text{label} = i | x) = \\frac{\\exp(h_i(x))}{\\sum_{j=1}^k \\exp(h_j(x))} zi​=p(label=i∣x)=∑j=1k​exp(hj​(x))exp(hi​(x))​ hi(x)h_i(x)hi​(x) 是假设函数的第 iii 个输出。 kkk 是类别的总数。 p(label=i∣x)p(\\text{label} = i | x)p(label=i∣x) 是输入 xxx 属于类别 iii 的概率。 ℓce(h(x),y)=−log⁡(p(label=y∣x))=−hy(x)+log⁡(∑∗j=1kexp⁡(hj(x))) \\ell_{ce}(h(x), y) = -\\log(p(\\text{label} = y | x)) = -h_y(x) + \\log \\left( \\sum*{j=1}^k \\exp(h_j(x)) \\right) ℓce​(h(x),y)=−log(p(label=y∣x))=−hy​(x)+log(∑∗j=1kexp(hj​(x))) 交叉熵损失衡量的是预测概率与实际类别之间的差异。当模型对正确类别的预测概率较高时，损失较低；反之，当模型对错误类别的预测概率较高时，损失较高。通过最小化交叉熵损失，我们可以训练模型，使其对正确类别的预测概率最大化。 最小化的是负的对数概率，这意味着我们希望增加正确类别的预测概率。通过最小化这个损失函数，我们可以训练模型，使其对正确类别的预测概率最大化。 也就是上面将其转成了概率，输入为 x 且 label 为 i 的概率，除所有 j 将其归一化，就变成了错误概率。 交叉熵与信息论：https://zhuanlan.zhihu.com/p/149186719 在 Softmax 回归（或多分类逻辑回归）中，假设函数是线性的？那 ReLU 呢 The softmax regression optimization problem min⁡θ1m∑i=1mℓce(hθ(xi),yi) \\min_{\\theta} \\frac{1}{m} \\sum_{i=1}^{m} \\ell_{\\text{ce}}(h_{\\theta}(x_i), y_i) θmin​m1​i=1∑m​ℓce​(hθ​(xi​),yi​) θ\\thetaθ 是模型参数 mmm 是训练集中样本的数量。 xix_ixi​ 和 yiy_iyi​ 分别是第 iii 个样本的输入和真实标签。 ℓce\\ell_{\\text{ce}}ℓce​ 是交叉熵损失函数。 在软最大值回归中，假设函数是线性的，并且使用软最大值损失： min⁡∗θ1m∑∗i=1mℓ_ce(θTxi,yi) \\min*{\\theta} \\frac{1}{m} \\sum*{i=1}^{m} \\ell\\_{\\text{ce}}(\\theta^T x_i, y_i) min∗θm1​∑∗i=1mℓ_ce(θTxi​,yi​) 就是找到这个 theta 参数，计算梯度。在实际应用中，通常使用一种称为随机梯度下降（Stochastic Gradient Descent, SGD） ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:4","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#loss-function"},{"categories":["ML Systems"],"collections":null,"content":"Loss Function classification error ℓerr(h(x),y)={0if arg⁡max⁡ihi(x)=y1otherwise \\ell_{\\text{err}}(h(x), y) = \\begin{cases} 0 \u0026amp; \\text{if } \\arg\\max_i h_i(x) = y \\\\ 1 \u0026amp; \\text{otherwise} \\end{cases} ℓerr​(h(x),y)={01​if argmaxi​hi​(x)=yotherwise​ 分类错误损失函数，只检查分类是否失败 argmax 就是取最大的可能性，是否等于标签 y 分类错误损失函数不适合作为优化过程中选择最佳参数的损失函数，因为它不可微分 在优化算法中，我们通常需要用到梯度信息，而分类错误损失函数由于其离散的性质，无法提供连续的梯度。 softmax / cross-entropy loss zi=p(label=i∣x)=exp⁡(hi(x))∑j=1kexp⁡(hj(x)) z_i = p(\\text{label} = i | x) = \\frac{\\exp(h_i(x))}{\\sum_{j=1}^k \\exp(h_j(x))} zi​=p(label=i∣x)=∑j=1k​exp(hj​(x))exp(hi​(x))​ hi(x)h_i(x)hi​(x) 是假设函数的第 iii 个输出。 kkk 是类别的总数。 p(label=i∣x)p(\\text{label} = i | x)p(label=i∣x) 是输入 xxx 属于类别 iii 的概率。 ℓce(h(x),y)=−log⁡(p(label=y∣x))=−hy(x)+log⁡(∑∗j=1kexp⁡(hj(x))) \\ell_{ce}(h(x), y) = -\\log(p(\\text{label} = y | x)) = -h_y(x) + \\log \\left( \\sum*{j=1}^k \\exp(h_j(x)) \\right) ℓce​(h(x),y)=−log(p(label=y∣x))=−hy​(x)+log(∑∗j=1kexp(hj​(x))) 交叉熵损失衡量的是预测概率与实际类别之间的差异。当模型对正确类别的预测概率较高时，损失较低；反之，当模型对错误类别的预测概率较高时，损失较高。通过最小化交叉熵损失，我们可以训练模型，使其对正确类别的预测概率最大化。 最小化的是负的对数概率，这意味着我们希望增加正确类别的预测概率。通过最小化这个损失函数，我们可以训练模型，使其对正确类别的预测概率最大化。 也就是上面将其转成了概率，输入为 x 且 label 为 i 的概率，除所有 j 将其归一化，就变成了错误概率。 交叉熵与信息论：https://zhuanlan.zhihu.com/p/149186719 在 Softmax 回归（或多分类逻辑回归）中，假设函数是线性的？那 ReLU 呢 The softmax regression optimization problem min⁡θ1m∑i=1mℓce(hθ(xi),yi) \\min_{\\theta} \\frac{1}{m} \\sum_{i=1}^{m} \\ell_{\\text{ce}}(h_{\\theta}(x_i), y_i) θmin​m1​i=1∑m​ℓce​(hθ​(xi​),yi​) θ\\thetaθ 是模型参数 mmm 是训练集中样本的数量。 xix_ixi​ 和 yiy_iyi​ 分别是第 iii 个样本的输入和真实标签。 ℓce\\ell_{\\text{ce}}ℓce​ 是交叉熵损失函数。 在软最大值回归中，假设函数是线性的，并且使用软最大值损失： min⁡∗θ1m∑∗i=1mℓ_ce(θTxi,yi) \\min*{\\theta} \\frac{1}{m} \\sum*{i=1}^{m} \\ell\\_{\\text{ce}}(\\theta^T x_i, y_i) min∗θm1​∑∗i=1mℓ_ce(θTxi​,yi​) 就是找到这个 theta 参数，计算梯度。在实际应用中，通常使用一种称为随机梯度下降（Stochastic Gradient Descent, SGD） ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:4","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#classification-error"},{"categories":["ML Systems"],"collections":null,"content":"Loss Function classification error ℓerr(h(x),y)={0if arg⁡max⁡ihi(x)=y1otherwise \\ell_{\\text{err}}(h(x), y) = \\begin{cases} 0 \u0026amp; \\text{if } \\arg\\max_i h_i(x) = y \\\\ 1 \u0026amp; \\text{otherwise} \\end{cases} ℓerr​(h(x),y)={01​if argmaxi​hi​(x)=yotherwise​ 分类错误损失函数，只检查分类是否失败 argmax 就是取最大的可能性，是否等于标签 y 分类错误损失函数不适合作为优化过程中选择最佳参数的损失函数，因为它不可微分 在优化算法中，我们通常需要用到梯度信息，而分类错误损失函数由于其离散的性质，无法提供连续的梯度。 softmax / cross-entropy loss zi=p(label=i∣x)=exp⁡(hi(x))∑j=1kexp⁡(hj(x)) z_i = p(\\text{label} = i | x) = \\frac{\\exp(h_i(x))}{\\sum_{j=1}^k \\exp(h_j(x))} zi​=p(label=i∣x)=∑j=1k​exp(hj​(x))exp(hi​(x))​ hi(x)h_i(x)hi​(x) 是假设函数的第 iii 个输出。 kkk 是类别的总数。 p(label=i∣x)p(\\text{label} = i | x)p(label=i∣x) 是输入 xxx 属于类别 iii 的概率。 ℓce(h(x),y)=−log⁡(p(label=y∣x))=−hy(x)+log⁡(∑∗j=1kexp⁡(hj(x))) \\ell_{ce}(h(x), y) = -\\log(p(\\text{label} = y | x)) = -h_y(x) + \\log \\left( \\sum*{j=1}^k \\exp(h_j(x)) \\right) ℓce​(h(x),y)=−log(p(label=y∣x))=−hy​(x)+log(∑∗j=1kexp(hj​(x))) 交叉熵损失衡量的是预测概率与实际类别之间的差异。当模型对正确类别的预测概率较高时，损失较低；反之，当模型对错误类别的预测概率较高时，损失较高。通过最小化交叉熵损失，我们可以训练模型，使其对正确类别的预测概率最大化。 最小化的是负的对数概率，这意味着我们希望增加正确类别的预测概率。通过最小化这个损失函数，我们可以训练模型，使其对正确类别的预测概率最大化。 也就是上面将其转成了概率，输入为 x 且 label 为 i 的概率，除所有 j 将其归一化，就变成了错误概率。 交叉熵与信息论：https://zhuanlan.zhihu.com/p/149186719 在 Softmax 回归（或多分类逻辑回归）中，假设函数是线性的？那 ReLU 呢 The softmax regression optimization problem min⁡θ1m∑i=1mℓce(hθ(xi),yi) \\min_{\\theta} \\frac{1}{m} \\sum_{i=1}^{m} \\ell_{\\text{ce}}(h_{\\theta}(x_i), y_i) θmin​m1​i=1∑m​ℓce​(hθ​(xi​),yi​) θ\\thetaθ 是模型参数 mmm 是训练集中样本的数量。 xix_ixi​ 和 yiy_iyi​ 分别是第 iii 个样本的输入和真实标签。 ℓce\\ell_{\\text{ce}}ℓce​ 是交叉熵损失函数。 在软最大值回归中，假设函数是线性的，并且使用软最大值损失： min⁡∗θ1m∑∗i=1mℓ_ce(θTxi,yi) \\min*{\\theta} \\frac{1}{m} \\sum*{i=1}^{m} \\ell\\_{\\text{ce}}(\\theta^T x_i, y_i) min∗θm1​∑∗i=1mℓ_ce(θTxi​,yi​) 就是找到这个 theta 参数，计算梯度。在实际应用中，通常使用一种称为随机梯度下降（Stochastic Gradient Descent, SGD） ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:4","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#softmax--cross-entropy-loss"},{"categories":["ML Systems"],"collections":null,"content":"Optimization: gradient descent the gradient is defined as the matrix of partial derivatives 梯度是向量函数的一个重要概念，用于描述函数在特定点的变化率 梯度下降是一种优化算法，用于最小化目标函数 f(θ)f(\\theta)f(θ) 的值。这个算法的核心思想是通过迭代地沿着负梯度方向更新参数，使得目标函数的值逐渐减小。 https://dlsyscourse.org/slides/2-softmax_regression.pdf 图解可以看出 α\\alphaα 是 step size 或 learning rate 大于 0 的时候 θ\\thetaθ 发生变化 θ←θ−α∇θf \\theta \\leftarrow \\theta - \\alpha \\nabla_{\\theta} f θ←θ−α∇θ​f","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:5","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#optimization-gradient-descent"},{"categories":["ML Systems"],"collections":null,"content":"Stochastic Gradient Descent 随机梯度下降 用于在机器学习模型的训练过程中最小化损失函数。与传统的梯度下降不同，SGD 不是在每次更新时使用整个训练集的数据，而是使用一个小的随机数据子集（即小批量 mini batch） http://zh.gluon.ai/chapter_optimization/gd-sgd.html 小批量随机梯度下降（SGD）引入了一定的随机性，使得模型可以更快跳出局部最优，朝全局最优收敛。这种随机性有助于在复杂的损失函数空间中进行更有效的搜索。 θ←θ−αB∑i=1B∇θℓi(hθ(xi),yi) \\mathbf{\\theta} \\leftarrow \\mathbf{\\theta} - \\frac{\\alpha}{B} \\sum_{i=1}^B \\nabla_{\\mathbf{\\theta}} \\ell_i(h_{\\mathbf{\\theta}}(\\mathbf{x}_i), \\mathbf{y}_i) θ←θ−Bα​i=1∑B​∇θ​ℓi​(hθ​(xi​),yi​)","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:6","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#stochastic-gradient-descent"},{"categories":["ML Systems"],"collections":null,"content":"The gradient of the softmax objective auto grad? 如果手动求导，需要链式法则，会很复杂 大部分人当成了标量 scalar 正式的方法：使用矩阵微积分、雅可比矩阵、克罗内克积和向量化 vector 技术。这种方法是“正确的方式”，更加严谨和系统，但也更加复杂和费时。 大家实际使用的捷径方法：假装所有变量都是标量，使用典型的链式法则，然后在计算结果后重新排列/转置矩阵和向量，使其维度匹配。虽然这种方法不那么严谨，但在实际操作中更加快捷和方便。 ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:7","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#the-gradient-of-the-softmax-objective"},{"categories":["ML Systems"],"collections":null,"content":"softmax regression algorithm 尽管推导过程相当复杂，但最终的算法非常简单。 Softmax 回归是多分类问题的线性模型，通过交叉熵损失函数和梯度下降优化参数 MNIST 数据集：手写数字分类（10 类，28x28 像素）。 结果：错误率\u0026lt;8%，训练时间仅数秒。 Softmax 回归是线性分类器，而神经网络通过非线性层（激活函数）和隐藏层构建更复杂的假设空间。 ","date":"2025-02-05","objectID":"/posts/ml-sys-01/:3:8","tags":["笔记","Deep Learning"],"title":"CMU 10-414/714: Deep Learning Systems (2020) - 深度学习系统 01 Softmax","uri":"/posts/ml-sys-01/#softmax-regression-algorithm"},{"categories":["Notes"],"collections":null,"content":"微服务架构 回顾下微服务架构的一些知识，包括服务发现、负载均衡、可用性，很多东西没有实操过所以难以记住，需要多多回顾。 来自极客时间的课程 https://time.geekbang.org/column/intro/100551601 后端工程师的高阶面经 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:0","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#微服务架构"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#服务注册与发现"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#高可用"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#注册服务端崩溃检测"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#客户端容错"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#注册中心选型"},{"categories":["Notes"],"collections":null,"content":"服务注册与发现 服务端启动需要向注册中心里注册自身的信息，保持心跳 客户端调用，需要找注册中心获取服务节点，并且缓存列表。 客户端与注册中心也要保持心跳和数据同步，服务端发生变动需要通知客户端更新 客户端发送请求，服务端返回响应 如果服务端下线怎么办？ 服务端通知注册中心准备下线，注册中心通知客户端更新列表，客户端不再发请求到该服务端，服务端等待一段时间后下线。 等待时间是需要的 etcd / ZooKeeper 可以当作注册中心，用一致性协议比如 Raft, ZAB 保证一致性 为什么需要服务注册和发现？ HTTP 需要域名解析，多级缓存，而 RPC 一般需要绑定多个端口，DNS 不能注册端口？ RPC 一般需要更新服务，DNS 多级缓存难以更新 高可用 服务注册与发现怎么保证高可用 注册服务端崩溃检测 注册中心和服务端之间维持心跳 比如租约、长连接 ws 等等，但最重要的问题是，心跳失败后的处理，是连续几次心跳失败才认为失败还是？连续重试，还是间隔重试，间隔是？ 参考指数退避算法：一开始重试几次，间隔较短，然后间隔指数上升，类似冷启动？ 客户端容错 注册中心或者服务端节点出问题时，请求依旧能发送到正确的服务端节点 failover 换节点 没太理解，意思就是客户端维护可用列表？ 注册中心选型 注册中心更加关注 CAP 中选 CP 还是选 AP 的问题 C：Consistency，数据一致性 A：Availability，服务可用性 P：Partition-tolerance，分区容错性 CAP 理论最多满足其中两个，CP 表示一致 + 分区容错，AP 表示可用 + 分区容错 P 分区容错性是肯定要选的， 选 C（一致性） 还是选 A（可用性）？哪个更加重要？ 可用性，也就是选 AP 如果注册中心选了 AP，那么客户端可能拿到不一致的可用节点列表，如果客户端发送到已经失效的服务端节点，那么就会发生错误，就需要客户端容错，failover 换节点重试。 ZooKeeper/ETCD 实际上是一个 CP 系统，而不是 AP 系统。 它们的一致性协议出色 但是 CP 面对负载较高的情况会出现什么问题呢？写入？ 总结 微服务架构可以看成三角形，围绕哪条边/哪个点会出问题来进行容错 注册中心 / \\ 客户端 ----- 服务端","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:1","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#总结"},{"categories":["Notes"],"collections":null,"content":"负载均衡 负载均衡是很重要的，问题在于考虑到性能和可用、请求该发给哪个服务端？ 常见的负载均衡算法： 轮询：依次分配 加权轮询：每个服务器有权重，权重高的优先分配 随机：随机 加权随机：每个服务器有权重 哈希：根据请求特征比如客户端 IP 计算哈希分配给对应的服务器 一致性哈希：服务器映射成环，请求也会映射到环上分配到最近的服务器 一致性哈希比较出名，但存在一些问题，负载并不完美均衡，但可以很显著解决节点增加或减少导致的数据迁移，只需要部分迁移。通过增加虚拟节点（每个节点会存在多个副本）、随机权重可以提高负载均衡效果 轮询与加权轮询 加权轮询如何实现？能不能直接 [1,1,1,2,3] 如果不想连续选择同一个节点，可以使用平滑加权轮询算法，比如 [1,2,1,3,1] 平滑加权 smooth weighted round-robin balancing 除了定义的 weight 还有个动态改变的 current_weight 每次选择 weight 最大的，并且将其减去 total_weight = sum(current_weight) 然后每个节点 current_weight += weight 重复这两步选择节点 图例：https://blog.csdn.net/gqtcgq/article/details/52076997 哈希与一致性哈希 简单哈希：(请求的一些参数或者 ip 得到哈希值) % n 个节点 = 序号 但如果哈希算法选的不好，可能会导致热点，并不均匀。 一致性哈希：服务端节点落在哈希环上，客户端请求参数计算哈希值，落在环上最近的服务端节点（顺时针） 一致性哈希并不保证服务端节点均匀分散在哈希环上，需要通过加权随机 (Rendezvous hashing)、虚拟节点（分散多个副本，尽可能均匀） 为什么用哈希，一个原因可能是和缓存有关，如果一样的请求可以一直到同一个服务端，利用缓存可以提高效率加快响应速度。 最少连接数：每次选择当前连接数最少的 但当前连接数并不代表实际负载，尤其是可以多路复用 最少活跃数：活跃请求是已经接受但是还没返回的请求，客户端自己维护，每次选择最少的。但同样这不代表负载，活跃请求有可能是延迟/大请求造成的。 最快响应时间：客户端维持每个节点的响应时间，而后每次挑选响应时间最短的。 这些都是单一指标，也可以用 CPU 等负载作为均衡算法，但可能需要中间代理来记录这些信息，然后转发 让客户端来维护就很奇怪 基于一致性哈希的改进： Rendezvous hashing 能获得更好的负载均衡，因为它需要计算所有节点的哈希值 hashfunction.hash(key + node.key()); 最后选择最大的 而一致性哈希只计算当前 key 的哈希值，在哈希环上找 详细对比：https://blog.prochase.top/2024/08/rendezvous-hashing/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:2","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#负载均衡"},{"categories":["Notes"],"collections":null,"content":"负载均衡 负载均衡是很重要的，问题在于考虑到性能和可用、请求该发给哪个服务端？ 常见的负载均衡算法： 轮询：依次分配 加权轮询：每个服务器有权重，权重高的优先分配 随机：随机 加权随机：每个服务器有权重 哈希：根据请求特征比如客户端 IP 计算哈希分配给对应的服务器 一致性哈希：服务器映射成环，请求也会映射到环上分配到最近的服务器 一致性哈希比较出名，但存在一些问题，负载并不完美均衡，但可以很显著解决节点增加或减少导致的数据迁移，只需要部分迁移。通过增加虚拟节点（每个节点会存在多个副本）、随机权重可以提高负载均衡效果 轮询与加权轮询 加权轮询如何实现？能不能直接 [1,1,1,2,3] 如果不想连续选择同一个节点，可以使用平滑加权轮询算法，比如 [1,2,1,3,1] 平滑加权 smooth weighted round-robin balancing 除了定义的 weight 还有个动态改变的 current_weight 每次选择 weight 最大的，并且将其减去 total_weight = sum(current_weight) 然后每个节点 current_weight += weight 重复这两步选择节点 图例：https://blog.csdn.net/gqtcgq/article/details/52076997 哈希与一致性哈希 简单哈希：(请求的一些参数或者 ip 得到哈希值) % n 个节点 = 序号 但如果哈希算法选的不好，可能会导致热点，并不均匀。 一致性哈希：服务端节点落在哈希环上，客户端请求参数计算哈希值，落在环上最近的服务端节点（顺时针） 一致性哈希并不保证服务端节点均匀分散在哈希环上，需要通过加权随机 (Rendezvous hashing)、虚拟节点（分散多个副本，尽可能均匀） 为什么用哈希，一个原因可能是和缓存有关，如果一样的请求可以一直到同一个服务端，利用缓存可以提高效率加快响应速度。 最少连接数：每次选择当前连接数最少的 但当前连接数并不代表实际负载，尤其是可以多路复用 最少活跃数：活跃请求是已经接受但是还没返回的请求，客户端自己维护，每次选择最少的。但同样这不代表负载，活跃请求有可能是延迟/大请求造成的。 最快响应时间：客户端维持每个节点的响应时间，而后每次挑选响应时间最短的。 这些都是单一指标，也可以用 CPU 等负载作为均衡算法，但可能需要中间代理来记录这些信息，然后转发 让客户端来维护就很奇怪 基于一致性哈希的改进： Rendezvous hashing 能获得更好的负载均衡，因为它需要计算所有节点的哈希值 hashfunction.hash(key + node.key()); 最后选择最大的 而一致性哈希只计算当前 key 的哈希值，在哈希环上找 详细对比：https://blog.prochase.top/2024/08/rendezvous-hashing/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:2","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#轮询与加权轮询"},{"categories":["Notes"],"collections":null,"content":"负载均衡 负载均衡是很重要的，问题在于考虑到性能和可用、请求该发给哪个服务端？ 常见的负载均衡算法： 轮询：依次分配 加权轮询：每个服务器有权重，权重高的优先分配 随机：随机 加权随机：每个服务器有权重 哈希：根据请求特征比如客户端 IP 计算哈希分配给对应的服务器 一致性哈希：服务器映射成环，请求也会映射到环上分配到最近的服务器 一致性哈希比较出名，但存在一些问题，负载并不完美均衡，但可以很显著解决节点增加或减少导致的数据迁移，只需要部分迁移。通过增加虚拟节点（每个节点会存在多个副本）、随机权重可以提高负载均衡效果 轮询与加权轮询 加权轮询如何实现？能不能直接 [1,1,1,2,3] 如果不想连续选择同一个节点，可以使用平滑加权轮询算法，比如 [1,2,1,3,1] 平滑加权 smooth weighted round-robin balancing 除了定义的 weight 还有个动态改变的 current_weight 每次选择 weight 最大的，并且将其减去 total_weight = sum(current_weight) 然后每个节点 current_weight += weight 重复这两步选择节点 图例：https://blog.csdn.net/gqtcgq/article/details/52076997 哈希与一致性哈希 简单哈希：(请求的一些参数或者 ip 得到哈希值) % n 个节点 = 序号 但如果哈希算法选的不好，可能会导致热点，并不均匀。 一致性哈希：服务端节点落在哈希环上，客户端请求参数计算哈希值，落在环上最近的服务端节点（顺时针） 一致性哈希并不保证服务端节点均匀分散在哈希环上，需要通过加权随机 (Rendezvous hashing)、虚拟节点（分散多个副本，尽可能均匀） 为什么用哈希，一个原因可能是和缓存有关，如果一样的请求可以一直到同一个服务端，利用缓存可以提高效率加快响应速度。 最少连接数：每次选择当前连接数最少的 但当前连接数并不代表实际负载，尤其是可以多路复用 最少活跃数：活跃请求是已经接受但是还没返回的请求，客户端自己维护，每次选择最少的。但同样这不代表负载，活跃请求有可能是延迟/大请求造成的。 最快响应时间：客户端维持每个节点的响应时间，而后每次挑选响应时间最短的。 这些都是单一指标，也可以用 CPU 等负载作为均衡算法，但可能需要中间代理来记录这些信息，然后转发 让客户端来维护就很奇怪 基于一致性哈希的改进： Rendezvous hashing 能获得更好的负载均衡，因为它需要计算所有节点的哈希值 hashfunction.hash(key + node.key()); 最后选择最大的 而一致性哈希只计算当前 key 的哈希值，在哈希环上找 详细对比：https://blog.prochase.top/2024/08/rendezvous-hashing/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:2","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#哈希与一致性哈希"},{"categories":["Notes"],"collections":null,"content":"熔断 熔断，限流，降级等等是微服务架构可用性的一些保障 熔断：微服务本身出问题时，拒绝服务直到恢复，类似股票的熔断机制 circuit breaker。熔断期间就可以恢复服务端. 判定服务的健康状态 如何判断微服务出现问题？一些指标：响应时间、错误率等等，但问题是什么阈值才是最合适的，超过阈值之后什么时候触发熔断 比如响应时间超过 1s 2s 之类的，如果 p99 = 1s 那么可以设置 1.2s 作为熔断阈值 一旦超过就要触发熔断吗？还是需要响应时间超过一段时间后才触发。防止偶发和抖动 jitter 服务恢复 熔断后需要拒绝服务，结束熔断后需要恢复服务。但如果发生抖动，频繁在正常和熔断两个状态切换怎么办？比如就根本支撑不了 QPS 1000 的服务，熔断后还是有 QPS 1000 的请求。 熔断可以逐步放开流量，还是让客户端控制流量去请求别的节点。 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:3","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#熔断"},{"categories":["Notes"],"collections":null,"content":"熔断 熔断，限流，降级等等是微服务架构可用性的一些保障 熔断：微服务本身出问题时，拒绝服务直到恢复，类似股票的熔断机制 circuit breaker。熔断期间就可以恢复服务端. 判定服务的健康状态 如何判断微服务出现问题？一些指标：响应时间、错误率等等，但问题是什么阈值才是最合适的，超过阈值之后什么时候触发熔断 比如响应时间超过 1s 2s 之类的，如果 p99 = 1s 那么可以设置 1.2s 作为熔断阈值 一旦超过就要触发熔断吗？还是需要响应时间超过一段时间后才触发。防止偶发和抖动 jitter 服务恢复 熔断后需要拒绝服务，结束熔断后需要恢复服务。但如果发生抖动，频繁在正常和熔断两个状态切换怎么办？比如就根本支撑不了 QPS 1000 的服务，熔断后还是有 QPS 1000 的请求。 熔断可以逐步放开流量，还是让客户端控制流量去请求别的节点。 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:3","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#判定服务的健康状态"},{"categories":["Notes"],"collections":null,"content":"熔断 熔断，限流，降级等等是微服务架构可用性的一些保障 熔断：微服务本身出问题时，拒绝服务直到恢复，类似股票的熔断机制 circuit breaker。熔断期间就可以恢复服务端. 判定服务的健康状态 如何判断微服务出现问题？一些指标：响应时间、错误率等等，但问题是什么阈值才是最合适的，超过阈值之后什么时候触发熔断 比如响应时间超过 1s 2s 之类的，如果 p99 = 1s 那么可以设置 1.2s 作为熔断阈值 一旦超过就要触发熔断吗？还是需要响应时间超过一段时间后才触发。防止偶发和抖动 jitter 服务恢复 熔断后需要拒绝服务，结束熔断后需要恢复服务。但如果发生抖动，频繁在正常和熔断两个状态切换怎么办？比如就根本支撑不了 QPS 1000 的服务，熔断后还是有 QPS 1000 的请求。 熔断可以逐步放开流量，还是让客户端控制流量去请求别的节点。 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:3","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#服务恢复"},{"categories":["Notes"],"collections":null,"content":"降级 熔断、降级、限流都是微服务架构的可用性保障 高峰期间会关闭一些服务，腾出服务器资源和减少公共组件比如数据库的压力， 降级的条件和熔断很像，如何判断什么时候降级，降级后怎么恢复，怎么处理抖动？ 但还有个问题，什么服务能降级什么服务不能？ 写服务一般不能降级，从前端接受数据然后写到数据库这种服务一般不降级。 跨服务降级：资源不够的时候暂停某些服务，让给其他更重要和核心的服务。 本服务提供有损服务：比如 app 首页也存在降级，触发降级后跨年不会做个性化推荐而使用静态页面。 降级思路：返回默认、禁用可观测组件，埋点，降低采样率等等、同步转异步、简化流程\u0026hellip; 降级的恢复和抖动？主要是控制流量，服务端或者客户端，部分流量依旧熔断/降级。 例子：读服务 QPS 更高更重要，那么就可以降级写服务，比如商店页面读，商家写，高峰期间可以暂停写入。 以前我会以为高峰期间增加冗余和副本才是更好的选择，但实际上增加副本可能导致更多的成本和问题，对公共组件增加更多的负担，甚至多个副本间产生一致性问题。 为了高可用，拒绝服务熔断和降级保证可用应该是成本更低的手段。 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:4","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#降级"},{"categories":["Notes"],"collections":null,"content":"限流 limiting 限流就更加常见了，但也同样更复杂，阈值更加难定。 限流通过限制流量来提高可用性，防止异常流量突发打崩系统， 限流算法 静态限流：令牌桶、漏桶、固定窗口、滑动窗口 动态限流（自适应限流）：BBR 等利用一系列指标来判定是否应该减少流量或方法流量，类似 TCP 拥塞控制。 令牌桶：系统以恒定速率产生令牌，放到桶里，每个请求只有拿到了令牌才能执行。 漏桶：限流器以均匀速度交给业务逻辑，来处理不稳定的请求速度，可以类比匀速的令牌桶 固定窗口：在一个固定时间段只执行固定数量的请求，比如一秒内一百个请求 滑动窗口：同样，一个窗口内只能执行固定数量的请求，但是会平滑移动窗口 如何限流？借助 redis 中间件记录流量和阈值，或者利用网关来限流。 这里也有一些业务逻辑，vip 用户不限流什么的，ip 限流，普通用户限流等等。 BBR 动态限流：自适应（QPS 指标，响应时间 RT） + 滑动窗口 + 限流公式 bilibili 开源的奎托斯 go kratos 框架 https://github.com/go-kratos/kratos 其限流器就是用 bbr limiter 实现的 https://go-kratos.dev/en/docs/component/middleware/ratelimit/ https://github.com/go-kratos/aegis/tree/main/ratelimit/bbr ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:5","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#限流"},{"categories":["Notes"],"collections":null,"content":"限流 limiting 限流就更加常见了，但也同样更复杂，阈值更加难定。 限流通过限制流量来提高可用性，防止异常流量突发打崩系统， 限流算法 静态限流：令牌桶、漏桶、固定窗口、滑动窗口 动态限流（自适应限流）：BBR 等利用一系列指标来判定是否应该减少流量或方法流量，类似 TCP 拥塞控制。 令牌桶：系统以恒定速率产生令牌，放到桶里，每个请求只有拿到了令牌才能执行。 漏桶：限流器以均匀速度交给业务逻辑，来处理不稳定的请求速度，可以类比匀速的令牌桶 固定窗口：在一个固定时间段只执行固定数量的请求，比如一秒内一百个请求 滑动窗口：同样，一个窗口内只能执行固定数量的请求，但是会平滑移动窗口 如何限流？借助 redis 中间件记录流量和阈值，或者利用网关来限流。 这里也有一些业务逻辑，vip 用户不限流什么的，ip 限流，普通用户限流等等。 BBR 动态限流：自适应（QPS 指标，响应时间 RT） + 滑动窗口 + 限流公式 bilibili 开源的奎托斯 go kratos 框架 https://github.com/go-kratos/kratos 其限流器就是用 bbr limiter 实现的 https://go-kratos.dev/en/docs/component/middleware/ratelimit/ https://github.com/go-kratos/aegis/tree/main/ratelimit/bbr ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:5","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#限流算法"},{"categories":["Notes"],"collections":null,"content":"隔离 隔离比较少见？但对于高可用也很重要，分离普通用户和 VIP 用户等等，也可以提高可用性、性能和安全性。 实例隔离：某个服务独享某个实例全部资源，无共享。而不是一台机器多个服务共享。 分组隔离： 连接池隔离/线程池隔离： 第三方依赖隔离：越是关键的业务，业务上越是关键的路径，就越要小心隔离 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:6","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#隔离"},{"categories":["Notes"],"collections":null,"content":"超时控制 超时控制同样也是可用性的一个方案，指在规定的时间内完成操作，如果不能完成，那么就返回一个超时响应。 确保客户端能在预期的时间内拿到响应，而不是没有任何响应。 及时释放资源，尤其是释放线程和连接，比如 go 协程会被一直占有。释放 RPC 连接和数据库连接等等。 实际操作中也遇到过这种问题，go 协程一直未被释放导致协程挤压和切换最后连接数据库超时了 超时控制形态：调用超时、链路超时 如何确定超时时间：用户体验、被调用接口响应时间、压测、代码。。。 一般来说都根据用户体验，比如产品经理认为 300ms 等待时间是合理的。根据响应时间，可以选择 p99 或者 p999 等 tail latency 压力测试可以测到 p99 和 p999 线等，但如果很难压力测试，可以尝试代码推算，比如有三次数据库操作和 redis 操作和发送消息操作，需要将他们全部 加起来，并且加一些余量。 超时中断业务：如果业务逻辑含有多个业务，其中一个超时怎么办？比如链路超时，可以用协议头传递超时时间，比如 rcp 协议头，http 协议头等等， 至于传递剩余时间还是超时时间，前者需要考虑网络传输时间，后者需要考虑时钟同步和偏移问题。 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:7","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#超时控制"},{"categories":["Notes"],"collections":null,"content":"调用第三方 任何系统都可能需要和第三方打交道，但是如何保证第三方接口的可用性？ 比如微信支付等等，实习期间也需要接入各种三方 API，比如 loki query 等等， 三方 API 基本会有限流，失败了怎么办？重试？ 测试环境下一般需要 mock 第三方服务的响应 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:8","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#调用第三方"},{"categories":["Notes"],"collections":null,"content":"综合 如何保证微服务应用的高可用性？高并发、高可用和大数据 可用性：SLA Service Level Aggrement 比如 99.9%可用性，全年只有 8.76h 停机时间。如何做到高可用？ 容错：熔断、重试、限流、降级、负载均衡、隔离等等 限制故障影响范围：隔离，相互依赖，共享基础设施。 出现故障，快速修复：完备的观测和告警系统 规范变更流程：review 等等 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:9","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#综合"},{"categories":["Notes"],"collections":null,"content":"RPC 常规 HTTP 调用，通过域名，发送参数和协议头等等，中间需要经过 DNS 等等 而 RPC 更像调用本地方法，和 HTTP 不同的是，采用了体积更小的 protobuf 序列化协议来保存结构体，也不需要考虑 HTTP 状态码比如 302 重定向等等，这在微服务架构中表现更好。 HTTP 是应用层的协议，RPC 可以基于 TDP 也可以基于 UDP，比 HTTP 更早出现， gRPC 底层用的 HTTP/2 所以是一种架构概念而不是通用协议？ 完整的 RPC 流程： stub -\u0026gt; serialization -\u0026gt; tcp 定义 IDL 文件比如 protobuf，生成 stub 文件（静态库和函数映射） 网络传输的数据是二进制数据，需要 encode decode 参数和结果 根据 RPC 协议约定数据头、元数据、消息体，保证有 基于 TCP/UDP 传输 stub 其实就是一段代码，客户端 stub 可以是远端代码的表示，服务端 stub HTTP 一般用 JSON 序列化，一般需要用反射来得到类型 而 Protobuf 体积更小，序列化和反序列化更快 HTTP 一般有很多协议头，而微服务一般不需要这些，用 RPC 更适合 gRPC 用 HTTP/2 拥有多路复用、优先级控制、头部压缩等优势，具有 连接池 RPC 框架一般需要生成代码，比如 protobuf。需要序列化和反序列化，将 object 变成二进制字节流。 具有安全性、通用性、和兼容性，同时性能很好。 协议层 支持解析多种协议，包含 HTTP, HTTP2, HTTP3 自定义 RPC 协议，私有协议等等 大厂内部大部分使用自定义 RPC 协议，TCP 中的二进制数据包会被拆分、合并，需要应用层协议确定边界。 gRPC, Thrift 等等 网络传输层一般使用成熟的网络通信框架，比如 Netty 和 RPC 解耦。 IO 多路复用，实现可靠传输等等。 RPC 不足 RPC 协议本身无法解决微服务集群的问题：服务发现等等，需要其他工具 调用方比如客户端，对服务端的 RPC 接口有强依赖关系，需要自动化工具、版本管理工具来保证代码级别的依赖，比如 stub 文件的更新。 RPC 热门框架 跨语言调用：grpc, thrift 提供基础的 RPC 通信能力，专注跨语言调用等等，但不带有服务治理等机制，需要其他框架来实现服务发现和负载均衡等等。 服务治理：rpcx, kitex, dubbo 等，提供 rpc 通信（多消息传输协议比如序列化协议、多网络通信协议比如 TCP, UDP, HTTP/2 和 QUIC 等等、服务定义和函数映射）并且提供服务发现、负载均衡等服务。 https://github.com/cloudwego/kitex https://www.cloudwego.io/ https://www.cloudwego.io/docs/kitex/getting-started/pre-knowledge/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:10","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#rpc"},{"categories":["Notes"],"collections":null,"content":"RPC 常规 HTTP 调用，通过域名，发送参数和协议头等等，中间需要经过 DNS 等等 而 RPC 更像调用本地方法，和 HTTP 不同的是，采用了体积更小的 protobuf 序列化协议来保存结构体，也不需要考虑 HTTP 状态码比如 302 重定向等等，这在微服务架构中表现更好。 HTTP 是应用层的协议，RPC 可以基于 TDP 也可以基于 UDP，比 HTTP 更早出现， gRPC 底层用的 HTTP/2 所以是一种架构概念而不是通用协议？ 完整的 RPC 流程： stub -\u0026gt; serialization -\u0026gt; tcp 定义 IDL 文件比如 protobuf，生成 stub 文件（静态库和函数映射） 网络传输的数据是二进制数据，需要 encode decode 参数和结果 根据 RPC 协议约定数据头、元数据、消息体，保证有 基于 TCP/UDP 传输 stub 其实就是一段代码，客户端 stub 可以是远端代码的表示，服务端 stub HTTP 一般用 JSON 序列化，一般需要用反射来得到类型 而 Protobuf 体积更小，序列化和反序列化更快 HTTP 一般有很多协议头，而微服务一般不需要这些，用 RPC 更适合 gRPC 用 HTTP/2 拥有多路复用、优先级控制、头部压缩等优势，具有 连接池 RPC 框架一般需要生成代码，比如 protobuf。需要序列化和反序列化，将 object 变成二进制字节流。 具有安全性、通用性、和兼容性，同时性能很好。 协议层 支持解析多种协议，包含 HTTP, HTTP2, HTTP3 自定义 RPC 协议，私有协议等等 大厂内部大部分使用自定义 RPC 协议，TCP 中的二进制数据包会被拆分、合并，需要应用层协议确定边界。 gRPC, Thrift 等等 网络传输层一般使用成熟的网络通信框架，比如 Netty 和 RPC 解耦。 IO 多路复用，实现可靠传输等等。 RPC 不足 RPC 协议本身无法解决微服务集群的问题：服务发现等等，需要其他工具 调用方比如客户端，对服务端的 RPC 接口有强依赖关系，需要自动化工具、版本管理工具来保证代码级别的依赖，比如 stub 文件的更新。 RPC 热门框架 跨语言调用：grpc, thrift 提供基础的 RPC 通信能力，专注跨语言调用等等，但不带有服务治理等机制，需要其他框架来实现服务发现和负载均衡等等。 服务治理：rpcx, kitex, dubbo 等，提供 rpc 通信（多消息传输协议比如序列化协议、多网络通信协议比如 TCP, UDP, HTTP/2 和 QUIC 等等、服务定义和函数映射）并且提供服务发现、负载均衡等服务。 https://github.com/cloudwego/kitex https://www.cloudwego.io/ https://www.cloudwego.io/docs/kitex/getting-started/pre-knowledge/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:10","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#协议层"},{"categories":["Notes"],"collections":null,"content":"RPC 常规 HTTP 调用，通过域名，发送参数和协议头等等，中间需要经过 DNS 等等 而 RPC 更像调用本地方法，和 HTTP 不同的是，采用了体积更小的 protobuf 序列化协议来保存结构体，也不需要考虑 HTTP 状态码比如 302 重定向等等，这在微服务架构中表现更好。 HTTP 是应用层的协议，RPC 可以基于 TDP 也可以基于 UDP，比 HTTP 更早出现， gRPC 底层用的 HTTP/2 所以是一种架构概念而不是通用协议？ 完整的 RPC 流程： stub -\u0026gt; serialization -\u0026gt; tcp 定义 IDL 文件比如 protobuf，生成 stub 文件（静态库和函数映射） 网络传输的数据是二进制数据，需要 encode decode 参数和结果 根据 RPC 协议约定数据头、元数据、消息体，保证有 基于 TCP/UDP 传输 stub 其实就是一段代码，客户端 stub 可以是远端代码的表示，服务端 stub HTTP 一般用 JSON 序列化，一般需要用反射来得到类型 而 Protobuf 体积更小，序列化和反序列化更快 HTTP 一般有很多协议头，而微服务一般不需要这些，用 RPC 更适合 gRPC 用 HTTP/2 拥有多路复用、优先级控制、头部压缩等优势，具有 连接池 RPC 框架一般需要生成代码，比如 protobuf。需要序列化和反序列化，将 object 变成二进制字节流。 具有安全性、通用性、和兼容性，同时性能很好。 协议层 支持解析多种协议，包含 HTTP, HTTP2, HTTP3 自定义 RPC 协议，私有协议等等 大厂内部大部分使用自定义 RPC 协议，TCP 中的二进制数据包会被拆分、合并，需要应用层协议确定边界。 gRPC, Thrift 等等 网络传输层一般使用成熟的网络通信框架，比如 Netty 和 RPC 解耦。 IO 多路复用，实现可靠传输等等。 RPC 不足 RPC 协议本身无法解决微服务集群的问题：服务发现等等，需要其他工具 调用方比如客户端，对服务端的 RPC 接口有强依赖关系，需要自动化工具、版本管理工具来保证代码级别的依赖，比如 stub 文件的更新。 RPC 热门框架 跨语言调用：grpc, thrift 提供基础的 RPC 通信能力，专注跨语言调用等等，但不带有服务治理等机制，需要其他框架来实现服务发现和负载均衡等等。 服务治理：rpcx, kitex, dubbo 等，提供 rpc 通信（多消息传输协议比如序列化协议、多网络通信协议比如 TCP, UDP, HTTP/2 和 QUIC 等等、服务定义和函数映射）并且提供服务发现、负载均衡等服务。 https://github.com/cloudwego/kitex https://www.cloudwego.io/ https://www.cloudwego.io/docs/kitex/getting-started/pre-knowledge/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:10","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#rpc-不足"},{"categories":["Notes"],"collections":null,"content":"RPC 常规 HTTP 调用，通过域名，发送参数和协议头等等，中间需要经过 DNS 等等 而 RPC 更像调用本地方法，和 HTTP 不同的是，采用了体积更小的 protobuf 序列化协议来保存结构体，也不需要考虑 HTTP 状态码比如 302 重定向等等，这在微服务架构中表现更好。 HTTP 是应用层的协议，RPC 可以基于 TDP 也可以基于 UDP，比 HTTP 更早出现， gRPC 底层用的 HTTP/2 所以是一种架构概念而不是通用协议？ 完整的 RPC 流程： stub -\u0026gt; serialization -\u0026gt; tcp 定义 IDL 文件比如 protobuf，生成 stub 文件（静态库和函数映射） 网络传输的数据是二进制数据，需要 encode decode 参数和结果 根据 RPC 协议约定数据头、元数据、消息体，保证有 基于 TCP/UDP 传输 stub 其实就是一段代码，客户端 stub 可以是远端代码的表示，服务端 stub HTTP 一般用 JSON 序列化，一般需要用反射来得到类型 而 Protobuf 体积更小，序列化和反序列化更快 HTTP 一般有很多协议头，而微服务一般不需要这些，用 RPC 更适合 gRPC 用 HTTP/2 拥有多路复用、优先级控制、头部压缩等优势，具有 连接池 RPC 框架一般需要生成代码，比如 protobuf。需要序列化和反序列化，将 object 变成二进制字节流。 具有安全性、通用性、和兼容性，同时性能很好。 协议层 支持解析多种协议，包含 HTTP, HTTP2, HTTP3 自定义 RPC 协议，私有协议等等 大厂内部大部分使用自定义 RPC 协议，TCP 中的二进制数据包会被拆分、合并，需要应用层协议确定边界。 gRPC, Thrift 等等 网络传输层一般使用成熟的网络通信框架，比如 Netty 和 RPC 解耦。 IO 多路复用，实现可靠传输等等。 RPC 不足 RPC 协议本身无法解决微服务集群的问题：服务发现等等，需要其他工具 调用方比如客户端，对服务端的 RPC 接口有强依赖关系，需要自动化工具、版本管理工具来保证代码级别的依赖，比如 stub 文件的更新。 RPC 热门框架 跨语言调用：grpc, thrift 提供基础的 RPC 通信能力，专注跨语言调用等等，但不带有服务治理等机制，需要其他框架来实现服务发现和负载均衡等等。 服务治理：rpcx, kitex, dubbo 等，提供 rpc 通信（多消息传输协议比如序列化协议、多网络通信协议比如 TCP, UDP, HTTP/2 和 QUIC 等等、服务定义和函数映射）并且提供服务发现、负载均衡等服务。 https://github.com/cloudwego/kitex https://www.cloudwego.io/ https://www.cloudwego.io/docs/kitex/getting-started/pre-knowledge/ ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:10","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#rpc-热门框架"},{"categories":["Notes"],"collections":null,"content":"Kitex Demo https://github.com/cloudwego/kitex-examples https://github.com/cloudwego/kitex-examples/tree/main/bizdemo/kitex_gorm 使用 thrift 生成 RPC IDL（Remote Procedure Call Interface Definition Language） 生成的文件在 kitex_gen/user 然后在 handler 上实现 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:11","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#kitex-demo"},{"categories":["Notes"],"collections":null,"content":"其他 设计幂等接口，针对写请求，可以对请求进行去重，确保同一个请求处理一次和多次的结果是相同的 请求方每次请求生成唯一的 id，首次调用和重试时，唯一的 ID 保持不变 服务端接受请求时，检查 ID 是否被处理过，如果处理过不需要再重复执行业务逻辑。 分布式锁如何实现幂等性：https://juejin.cn/post/6965740344335925279 幂等设计 https://juejin.cn/post/7049140742182141959 ","date":"2024-12-25","objectID":"/posts/backend-microservice/:1:12","tags":["Backend"],"title":"阅读笔记：微服务架构","uri":"/posts/backend-microservice/#其他"},{"categories":["Distributed"],"collections":null,"content":"From Cloud Computing to Sky Computing 21 年 UCB 提出了 Sky Computing 的概念，后续也有一篇 35 页的 The Sky Above The Clouds 相比起传统的云服务，sky computing 更像一个大一统的中间层，比如可以调用不同的云服务。但看他们实验室的项目已经基本都在做 LLM 相关，没有太多 sky computing 的后续了。 作者 Ion 也是 Databricks 的 cofounder，也是 spark 的原作者 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:1:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#from-cloud-computing-to-sky-computing"},{"categories":["Distributed"],"collections":null,"content":"Abstract 我们考虑云计算的未来，并探讨如何引导其发展成为一个更为连贯的服务，我们称之为天空计算。 主要的障碍更多是经济层面的而非技术层面的，我们提出互惠对等作为关键的推动步骤。 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:2:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#abstract"},{"categories":["Distributed"],"collections":null,"content":"Introduction In 1961, John McCarthy “computation may someday be organized as a public utility, just as the telephone system is a public utility. We can envisage computer service companies whose subscribers are connected to them [\u0026hellip;]. Each subscriber needs to pay only for the capacity that he actually uses, but he has access to all programming languages characteristic of a very large system.” 也就是一次付费可以用多种云的服务 他简短的描述准确地描绘了我们如今称之为云计算的情景，用户可以访问大量的计算和存储资源，并且只需为使用的资源付费。相反，他对经济学的预测则大相径庭；如今，在美国，电话服务已不再是公共事业，而是通过一系列竞争的供应商提供。然而，尽管电话服务不是公共事业，但它基本上是一种商品，提供了统一的用户体验：即，无论你使用哪个供应商，你都可以联系到任何人，并且切换供应商相对容易；你甚至可以在切换时保留你的号码。 回到麦卡锡的预测，我们在这里关注的不是没有单一的公共计算事业，而是云计算并不是一种无差别的商品。与电话服务不同，云计算市场已经远离了商品化，云服务提供商通过专有服务来努力使自己与众不同。在本文中，我们建议采取一些步骤来克服这种差异化，并帮助创建一个更加商品化的云计算版本，我们称之为天空计算。在此之前，我们简要总结了云计算的历史，为本文的其余部分提供背景。 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:3:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#introduction"},{"categories":["Distributed"],"collections":null,"content":"Historical Context The NSF high performance computing (HPC) initiative in the 1980s provided an early glimpse of the utility computing vision, though limited to the HPC community 个人计算机产业由那些想要拥有自己的机器进行捣鼓的爱好者发起，并由摩尔定律推动，使得个人计算机能够迅速满足不断增长的用户需求。 互联网的出现迅速使得包括电子邮件、公告板系统和游戏在内的多种流行服务在全球范围内可访问。 许多公司，如雅虎、谷歌、eBay 和亚马逊，都遵循了这条道路，但创建大规模服务特定基础设施的责任成为了进入互联网服务市场的障碍，因为这些步骤需要巨额投资，大多数公司无法负担。 这一情况在 2006 年发生了变化，当时亚马逊推出了 S3 和 EC2，通过 democratizing access to compute/storage and promoting the “pay-as-you-go” business model. 这，再加上摩尔定律的终结（这使得在本地集群中构建和扩展服务变得相当昂贵），导致了对可能成为 utility computing 的重新推动。 然而，商业趋势将我们推向了不同的方向。在亚马逊在云计算领域占据主导地位的早期，他们为云计算设定了事实上的标准。然而，在过去十年中，这个市场上出现了多个竞争对手。根据最近的一份报告[6]，AWS 现在仅拥有 32%的市场份额，其次是微软占 19%，谷歌占 7%，阿里巴巴占 6%，其他云（如 IBM 和甲骨文）瓜分了剩余的 37%市场份额（由于四舍五入误差，这总计为 101%）。 这种竞争导致了价格的下降和产品与服务的不断增加。例如，仅 AWS 就提供了超过 175 种产品和服务[8]。然而，许多这些服务是专有的，这些专有服务是云提供商区分自己的主要方式之一。例如，每个云都有自己管理集群的 API，自己的对象存储版本，自己的数据仓库版本，自己的无服务器产品，等等。在一个云上开发的应用程序通常在不同的云上无法运行，除非进行大量修改，就像为微软 Windows 编写的应用程序需要大量修改才能在 Mac OS 上运行一样。因此，云计算市场的竞争使我们远离了公用计算的愿景。 当然，有许多呼吁标准化云计算[28, 39]，但这些对推动云计算差异化的影响甚微。当前云提供商的商业模式围绕着吸引并留住客户，这与提供纯粹的商品化服务相悖。那么，我们要解决的问题是，我们如何朝着公用计算的目标取得进展？ 我们的提议被称为天空计算，意在表明我们试图超越单个云的局限。然而，我们并不是第一个使用“天空计算”这一术语的人。自 2009 年以来，几篇论文提出了以这个名字命名的设计[30, 34, 35]。然而，这些论文关注于特定的技术解决方案，例如在跨云基础设施即服务平台（如 Nimbus）上运行中间件，并针对特定的工作负载，如高性能计算（HPC）。本文对天空计算持更广泛的视角，将其作为未来应用程序的通用软件平台，并考虑技术趋势和市场力量如何在天空计算的出现中发挥关键作用。 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:4:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#historical-context"},{"categories":["Distributed"],"collections":null,"content":"Lessons from the Internet 尽管云计算和互联网在许多方面存在差异，但我们认为互联网提供了一组有用的历史教训。在 20 世纪 60 年代初，几个团体正在开发分组交换技术。这些早期的网络工作良好，但彼此不兼容。社区面临一个选择：是应该标准化单一的网络技术，还是能找到一种方式来容纳多样性？1972 年，罗伯特·卡恩提出了开放架构网络[32]，主张一个通用的互操作性层，允许任何两个网络互联。这成为了互联网协议（IP）。 个人觉得 Internet 和 Cloud 还是不太一样 Cloud 从本质来说就不太适合标准化，成本来说，谁便宜就用谁。 因此，有三个关键的设计决策使得互联网能够为由异构技术（从以太网到 ATM 到无线）和竞争公司组成的大型基础设施提供统一的接口。首先是掩盖技术异构性的“兼容性”层。第二是域间路由，它将互联网粘合在一起，使其对终端用户来说像一个网络。第三是一组经济协议，形成了我们称之为“对等”层，允许竞争网络在创建统一网络中合作。 互联网要解决的三个问题： compatibility interdomain routing economic agreements 这对云计算有什么启示？为了实现公用计算的愿景，应用程序应该能够在任何云提供商上运行（即一次编写，随处运行）。此外，用户不应该需要管理单个云上的部署，或者在从一个云迁移到另一个云时面临重大障碍。简而言之，对于开发者来说，构建一个多云应用程序应该像构建一个运行在单一云上的应用程序一样容易。我们称之为天空计算。我们使用这个术语是因为公用计算意味着基础设施是一个公共事业，而天空计算指的是在由多个和异构的竞争商业云提供商组成的基础设施上构建公用计算的幻象。 我们认为，互联网必须解决的三个设计问题正是从我们当前的云集合中创建天空计算所需的组成部分。我们需要一个兼容性层来掩盖低级技术差异，一个跨云层来将作业路由到正确的云，以及一个对等层，允许云之间就如何交换服务达成协议。在接下来的三个部分中，我们将更详细地描述这些层。然后，我们通过推测未来来结束本文。 表 1 展示了所提出的天空架构与互联网之间的相似性：路由器类似于服务器，AS 类似于可用区，ISP 类似于云提供商。就像在互联网中，IP 对在同一 ISP 或跨 ISP 的路由器之间路由数据包一无所知一样，构建在跨云层上的应用程序应该对其运行的云一无所知。 IP 协议变成了 compatibility layer BGP（Border Gateway Protocol，边界网关协议）是一种用于在不同自治系统（AS，Autonomous System）之间交换路由信息的协议。它是互联网的核心协议之一，负责在不同网络之间传递数据包。BGP 的主要功能是选择最佳路径，将数据从一个网络传输到另一个网络。 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:5:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#lessons-from-the-internet"},{"categories":["Distributed"],"collections":null,"content":"Compatibility Layer 实现天空计算愿景的第一步是提供一个云兼容性层；即，一个抽象掉云所提供服务的层，使得在这个层之上开发的应用程序可以在不同的云上运行而无需修改。简而言之，兼容性层是一组应用程序可以构建在其上的接口或 API；然后，这个兼容性层可以使用云的一组（可能是专有的）接口移植到每个云上。 其实 POE 这种整合了多种 LLM API 的是不是也算一种 sky computing 在我们的互联网类比中，这类似于 IP 层，它使使用不同底层（L2）通信技术的路由器能够处理 IP 数据包。然而，与 IP 不同，IP 是一个 narrow waist，云兼容性层要宽得多，定义也不那么明确，因为云向应用程序暴露了丰富的（且不断增长的）服务集，包括计算、存储、数据传输等。因此，云兼容性层在精神上更像是一个操作系统（例如 Linux），它管理计算机的资源并向应用程序暴露 API。 IP is the narrow waist 是一个比较有意思的理念 我们如何构建这样一个云兼容性层？虽然每个云都提供了一组专有的低级接口，但今天的用户大多与更高级别的管理和服务接口交互。其中一些是专有的，但越来越多的接口得到了开源软件（OSS）的支持。 这些 OSS 项目存在于软件栈的各个层次，包括操作系统（Linux）、集群资源管理器（Kubernetes [12]，Apache Mesos [31]）、应用程序打包（Docker [10]）、数据库（MySQL [15]，Postgres [17]）、大数据执行引擎（Apache Spark [42]，Apache Hadoop [41]）、流引擎（Apache Flink [26]，Apache Spark [42]，Apache Kafka [5]）、分布式查询引擎和数据库（Cassandra [4]，MongoDB [14]，Presto [18]，SparkSQL [22]，Redis [19]）、机器学习库（PyTorch [37]，Tensorflow [24]，MXNet [27]，MLFlow [13]，Horovod [40]，Ray RLlib [33]）以及通用分布式框架（Ray [36]，Erlang [25]，Akka [1]）。 此外，由 OSS 创建者创立的大量公司已经出现，为多个云提供托管服务。例如 Cloudera（Apache Hadoop）、Confluent（Apache Kafka）、MongoDB、Redis Labs、HashiCorp（Terraform，Consul）、Datastax（Cassandra）和 Databricks（Apache Spark，MLFlow，Delta）。这些发展使得企业如果使用这些基于多云 OSS 的产品，相对容易从一个云切换到另一个云。 其实现在 iceberg 等等使用 parquet 应该也是类似的理念把 虽然 OSS 在软件栈的大多数层提供了解决方案，但一个明显的差距是存储层。每个云提供商都有自己的专有高度可扩展存储版本。例如 AWS 的 S3 [2]、微软的 Azure Blob Storage [7]和谷歌的 Cloud Storage [11]。尽管如此，已经有几种解决方案为 Azure 的 Blob Storage 和谷歌的 Cloud Storage 提供 S3 兼容 API，例如 S3Proxy [20]和 Scality [21]。一些云提供商提供自己的 S3 兼容 API，以帮助客户从 AWS 过渡到他们自己的云。在下文中，我们假设兼容性层提供的存储 API 允许跨云读取数据。 因此，我们认为在纯技术基础上实现一个广泛可用的兼容性层是容易实现的。问题在于市场是否会支持这样的努力，因为虽然兼容性层对用户有明显的好处，但它自然会导致云提供商的商品化，这可能不符合他们的利益。我们将在第 7 节讨论激励问题。 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:6:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#compatibility-layer"},{"categories":["Distributed"],"collections":null,"content":"Intercloud Layer 兼容性层只是实现天空愿景的第一步。即使兼容性层允许用户在不同的云上运行应用程序而无需修改，用户仍然需要决定在哪个云上运行应用程序。因此，用户仍然负责在不同的云之间进行性能/成本权衡。这类似于要求互联网用户为其域间流量显式选择 AS 路径，这将是一项繁重的任务。 为了解决这个问题，互联网使用 BGP 进行 AS 级路由决策，这些决策对用户是透明的。类似地，天空架构应该实现一个跨云层，将云提供商从用户中抽象出来；也就是说，用户不应该知道应用程序运行在哪个云上（除非他们明确想要知道）。跨云层在兼容性层之上实现，如图 1 所示。 跨云层必须允许用户指定他们的作业应该在哪里运行的策略，但不需要用户做出关于作业放置的低级决策（但如果用户愿意，可以允许他们这样做）。 intercloud 还是需要考虑不同的性能、成本和隐私等等 (1) A uniform naming scheme for OSS services. (2) A directory service which allows cloud providers to register their services, and applications to select a service based on their preferences. (3) An accounting and charging mechanism across clouds. Service Naming Scheme 命名方案来标识该实例，尽管一个自然的可能性是利用 DNS 来命名这些服务实例。此外，我们需要将元数据与每个这样的服务实例关联起来。这样的元数据应包含如何调用服务、云提供商的名称、位置、软件或 API 版本、硬件类型等。此外，我们可能还希望添加动态信息，如定价、负载和可用性。 Directory service 需要特定服务的应用程序必须找到满足其要求和偏好的服务实例。这就需要一个目录服务。 Accounting and charging ，用户的应用程序可以在许多云中的一个或甚至在多个云上同时运行，每个云必须对使用的资源进行会计处理。如果每个云都进行计费，每个用户都需要在每个云上拥有账户。我们建议一个替代方案，每个用户与第三方经纪人（可能是云提供商之一）签约，该经纪人在所有云上都有账户，并累积费用，然后将每个用户的付款分配回各个云。 其实最大的问题是，谁来做 sky computing? the issue is whether the market will produce one? ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:7:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#intercloud-layer"},{"categories":["Distributed"],"collections":null,"content":"Peering Between Clouds 跨云层旨在在最能满足其需求的云（或云）上运行作业。如果作业涉及大数据集，就像许多常见的云工作负载一样，这将需要将数据移动到计算发生的云上 流量和机器比谁更加贵？ 还是说数据移动才是贵的 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:8:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#peering-between-clouds"},{"categories":["Distributed"],"collections":null,"content":"Speculations About The Future 如上所述，虽然兼容性层几乎没有技术障碍，但它会使云计算更像一种商品。 此外，虽然大型现有云提供商可能不会对兼容性层感到高兴，但我们预计较小的云提供商会拥抱这样的层。 其实确实，不少云 DB 都会支持 aws/azure/gcp 多种云，但应该是为了满足不同客户的需求 而不是真的愿意去做大一统，不同技术兼容还是满困难和繁琐的 还是太理想，但确实可能慢慢的大家会往这个方向走 ","date":"2024-12-04","objectID":"/posts/paper-skycomputing/:9:0","tags":["Paper Reading"],"title":"Paper Reading: From Cloud Computing to Sky Computing","uri":"/posts/paper-skycomputing/#speculations-about-the-future"},{"categories":["Distributed"],"collections":null,"content":"ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta Meta 的全球服务网格 ServiceRouter（SR） 服务网格（Service Mesh）是一种用于处理微服务架构中服务之间通信的软件层。它通过在服务之间插入代理，实现通信控制和监控，从而提高系统的可靠性、安全性和可观测性。 流量管理、负载均衡？ ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:1:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#servicerouter-hyperscale-and-minimal-cost-service-mesh-at-meta"},{"categories":["Distributed"],"collections":null,"content":"Abstract 数据中心应用程序通常被构建为许多相互连接的微服务，而服务网格已成为在服务之间路由 RPC 流量的流行方法。 SR 与公开已知的服务网格在几个重要方面有所不同。首先，SR 是为超大规模设计的，目前使用数百万个 L7 路由器在数万个服务之间每秒路由数千亿个请求 其次，虽然 SR 采用了使用 sidecar 或远程代理来路由我们 fleet 中 1%的 RPC 请求的常见方法，但它采用了一个路由库，该库直接链接到服务可执行文件中，以将剩余的 99% 直接从客户端路由到服务器，而无需通过代理的额外跳转。这种方法显著降低了我们超大规模服务网格的硬件成本，节省了数十万台机器。第三，SR 内置支持分片服务，这些服务占我们舰队中 68%的 RPC 请求，而现有的通用服务网格不支持分片 sharded 服务。最后，SR 引入了局部性环的概念，以同时最小化 RPC 延迟并在地理分布的数据中心区域之间平衡负载，据我们所知，这是以前未曾尝试过的。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:2:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#abstract"},{"categories":["Distributed"],"collections":null,"content":"Introduction 微服务架构 为了管理这些服务之间的远程过程调用（RPC）流量，许多组织使用服务网格 service mesh 图 1 展示了最常见的第 7 层（L7，即应用层）服务网格形式。在这种架构中，每个服务进程都伴随着一个运行在同一台机器上的 L7 边车代理，该代理代表服务路由 RPC 请求。 例如，当机器 1 上的服务 A 向服务 B 发送请求时，机器 1 上的代理将请求在机器 2 和机器 3 之间进行负载均衡。如果自动扩展系统检测到负载增加并开始在机器 4 上启动服务 B 的新副本，控制平面的服务发现功能将通知机器 1 上的代理，该代理随后将机器 4 包含在其未来对服务 B 请求的负载均衡目标中。 本文介绍了 Meta 的全球服务网格，称为 ServiceRouter（SR）。SR 支持一系列全面的功能，包括服务发现、负载均衡、故障转移、认证、加密、可观测性、过载保护、分布式请求跟踪、容量管理的资源归属以及用于影子测试的流量复制。 Scalability: 传统上，软件定义网络使用集中式控制平面和分散式数据平面。大多数服务网格遵循这种方法，并使用中央控制器来配置每个边车代理的路由表 图 2 展示了 SR 的可扩展架构 在顶部，不同的控制器独立执行诸如注册服务和生成每个服务的跨区域路由表等功能。每个控制器独立更新中央路由信息库（RIB），而不关心配置或管理单个 L7 路由器 Routing Information Base (RIB) 负责服务发现、路由配置、跨区路由配置等等 Hardware cost. SR 通过提供一个名为 SRLib 的库来消除对代理及其相关硬件成本的需求。 为了满足服务的多样化需求，SR 实现了不同类型 L7 路由器的无缝共存，包括 Istio 风格的边车代理、AWS-ELB 风格的专用负载均衡器和 gRPC 风格的旁路负载均衡器 Sharded services SR 将分片支持作为首要任务，并使用单一框架来支持分片和复制。 Cross-region routing 现有的解决方案未针对跨地理分布的数据中心区域的路由进行优化。 为了更好地支持跨区域路由，我们引入了局部性环的概念，让服务表达它们在延迟和负载之间的首选权衡。 Contributions SR 是为超大规模设计的。 SR 支持在一个服务网格中不同类型 L7 路由器的无缝共存，包括边车代理、专用负载均衡器、旁路负载均衡器和嵌入式路由库。 尽管现有服务网格仅关注未分片的服务，这些服务仅占我们舰队 RPC 请求的 32%，SR 提供了对分片服务的内置支持，这些服务占我们流量的 68%。 局部性环 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:3:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#introduction"},{"categories":["Distributed"],"collections":null,"content":"Comparison of Services Mesh Architectures (1) which component fetches and caches the routing metadata, and (2) which component routes application RPC traffic. ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:4:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#comparison-of-services-mesh-architectures"},{"categories":["Distributed"],"collections":null,"content":"Different Types of L7 Routers in SR SRLib SRLookaside SRSidecarProxy SRRemoteProxy 分别是路由库，旁路负载均衡器， sidecar，远程代理 SRLib 直接发送请求到 Server SRLookaside 负载均衡 SRSidecarProxy 边车 SRRemoteProxy 跨区域？ ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:4:1","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#different-types-of-l7-routers-in-sr"},{"categories":["Distributed"],"collections":null,"content":"Comparison of L7 Routers 对比了不同的 miniRIB 路由库？ 接下来，我们将比较表 1 中的解决方案。解决方案(1)–(4)是不理想的，因为管理嵌入式库中的 miniRIB 会影响应用程序的性能，由于缺乏隔离。 解决方案(5)、(7)和(8)是不理想的，因为没有系统调用来访问内核中缓存的 miniRIB。例如，Cilium 的 eBPF 程序只能处理 L3/L4 协议，仍然需要使用边车代理来处理 L7 协议。与解决方案(6)类似，解决方案(10)和(14)是不理想的，因为难以在内核中实现高级 L7 路由功能。解决方案(12)是不理想的，因为它严格比(16)差，即如果路由由远程代理执行，最好也将 miniRIB 移到远程代理。理论上，解决方案(15)在客户端机器上使用的内存比(11)少。然而，(15)在 Meta 中没有使用，因为即使(11)也没有广泛使用，(15)的额外好处有限。最后，为了便于访问，我们在表 2 中总结了设计替代方案的比较。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:4:2","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#comparison-of-l7-routers"},{"categories":["Distributed"],"collections":null,"content":"ServiceRouter Design ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#servicerouter-design"},{"categories":["Distributed"],"collections":null,"content":"Overview SR 支持图 4 中描述的所有四种 L7 路由器类型。对于边车或远程代理设置，我们在 SRLib 代码之上添加了一个包装层，以使其作为独立代理运行。SR 的控制平面组件如图 6 所示，并进一步解释如下。 Routing Information Base （RIB）。RIB 是一个基于 Paxos 的键值存储 Global Registry Service（GRS）在 RIB 中维护服务和分片发现信息。图 7 显示了在 GRS 注册的两个示例服务。服务 A 是复制的但未分片。当集群管理器启动或停止服务 A 的容器时，它会通知 GRS 更新服务 A 的副本列表。我们将在 §3.3 中解释 SR 对分片服务的内置支持。 Configuration Management System (CMS) 允许为每个服务定制路由策略，包括 RPC 超时、连接重用、局部性路由偏好等。服务所有者遵循配置即代码范式来编写、审查和提交路由配置。它还支持自动配置更新。例如，延迟监控服务（LMS）定期聚合并提交与跨区域延迟相关的配置更新，以指导 SRLib 的路由决策。 Cross-region Routing Service (xRS). 与集中式负载均衡器相比，SRLib 只有一个客户端的本地视图，可能无法做出全局最优的路由决策。xRS 通过聚合每个服务的全局流量信息并计算跨区域路由表来解决这个问题，该路由表通过 RIB 分发并由 SRLib 使用以指导其路由决策 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:1","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#overview"},{"categories":["Distributed"],"collections":null,"content":"Service Discovery 每个机器上运行一个 RIBDaemon，维护一个所谓的 miniRIB，缓存 RIB 中 RPC 客户端运行所需的具体部分。最初，miniRIB 是空的。当 SRLib 想要向特定服务（如服务 X）发送 RPC 请求时，它会从 RIBDaemon 请求服务 X 的路由元数据。RIBDaemon 从 RIB 副本中获取元数据，将其缓存到磁盘上以便在机器重启后仍然可用，订阅与服务 X 相关的未来更新，最后将元数据返回给 SRLib。SRLib 也订阅 RIBDaemon 的未来更新，并将元数据缓存在内存中（但不缓存到磁盘）以供后续重用，这样它就不会在每次 RPC 请求时都联系 RIBDaemon。 当服务 X 的部署在未来发生变化时，集群管理器通知 GRS 更新 RIB。更新会立即推送到所有 RIB 副本，这些副本进一步将更新推送到订阅服务 X 路由元数据的所有 RIBDaemon。最后，RIBDaemon 将更新推送到 SRLib。服务 X 可能部署在多个数据中心区域，每个区域的副本由不同的区域集群管理器管理。所有这些集群管理器都会通知 GRS 更新服务 X 的相同服务注册记录，以便客户端的 RPC 请求可以潜在地路由到任何区域的副本（§3.4.1）。服务的服务客户端可以选择仅向位于与客户端相同区域的服务器发送请求。在这种情况下，为了减少开销，RIBDaemon 仅订阅来自本地区域的更新。 在集群管理器的帮助下，客户端不需要通过超时独立发现服务器的故障。当服务器因计划维护（如代码部署）而关闭时，集群管理器首先更新 RIB 以通知客户端，然后停止服务器。对于计划外故障，集群管理器检测各种故障，如进程崩溃/挂起和机器故障，并更新 RIB 以通知客户端。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:2","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#service-discovery"},{"categories":["Distributed"],"collections":null,"content":"Support for Sharded Services SR 的分片映射抽象是通用的，目前支持数百个分片服务。其中大多数（但不是全部）由一个通用的分片管理器管理，当添加或删除新分片或现有分片在容器之间迁移时，分片管理器会通知 GRS 更新分片注册表。 通过 SR，分片和未分片服务共享并重用 SR 中的所有复杂组件（图 2 和图 6）。此外，分片服务的路由开箱即用，无需任何额外努力。相比之下，现有的通用服务网格不支持分片服务，应用程序必须开发自己的解决方案。 Design alternatives: 一致性哈希， 但一致性哈希对于高级分片用例是不够的，因为其确定性的键分配不支持响应分片负载变化的动态分片迁移。SR 提供了对一致性哈希和分片映射方法的内置支持。如我们之前的工作所示，在 Meta 的数百个分片服务中，选择使用灵活分片映射的服务数量是选择使用一致性哈希的服务数量的 5.4 倍，这证实了分片映射方法的重要性和有效性。 SR 的分片映射方法的另一个替代方案是允许服务提供自己的自定义旁路服务实现。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:3","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#support-for-sharded-services"},{"categories":["Distributed"],"collections":null,"content":"Load Balancing Pick-2 算法 Michael Mitzenmacher. The power of two choices in randomized load balancing. IEEE Transactions on Parallel and Distributed Systems, 12(10):1094–1104, 2001. 开发了三种新技术来补充 Pick-2：1) 在抽取两个随机服务器时考虑区域局部性（§3.4.1）。2) 从服务器的稳定子集中抽取两个随机服务器，而不是所有服务器，以最大化连接重用（§3.4.2）。3) 根据工作负载特征采用自适应负载估计方法（§3.4.3）。以下部分提供了这些技术的更多细节。 Locality Awareness SR 不是从候选池中随机抽取两个服务器，而是使用所谓的局部性环来过滤掉远离客户端的长延迟服务器，然后从剩余的附近服务器中抽取。每个服务可以定义一组具有递增延迟的环，例如 [ring1: 5 毫秒 | ring2: 35 毫秒 | ring3: 80 毫秒 | ring4: 无限制]。延迟监控服务（LMS）定期更新区域之间的 RTT，RPC 客户端通过 CMS 获取这些信息。 通过局部性环过滤可以减少路由延迟，但由于缺乏全局视图，仍然存在局限性。 Design alternative: xRS, 然而，SR 没有采用这种方法，因为根据排队理论和我们的生产经验，在高利用率下建模延迟并不稳健。 总的来说，在网络 RTT 可能从 100 毫秒到 100 毫秒变化三个数量级的地理分布环境中，以延迟为中心的方法并不稳健。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:4","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#load-balancing"},{"categories":["Distributed"],"collections":null,"content":"RPC Connection Reuse 建立一个新的 TLS/TCP 连接需要 1.6 毫秒，并且在每侧消耗 14KB 的内存。为了减少这种开销，SR 保持 TLS/TCP 连接并在不同的 RPC 请求之间重用它们。然而，Pick-2 使用的随机化使得连接重用无效。 为了提高连接重用， 在 SR 中，每个 RPC 客户端使用 Rendezvous Hashing 来选择 ā 个稳定服务器，这实现了上述理想的特性 Design alternative 我们更喜欢 Rendezvous Hashing 而不是一致性哈希，因为它允许 SR 使用加权哈希按服务器的计算能力成比例地分配客户端连接。 更加负载均衡，Rendezvous Hashing 比起一致性哈希 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:5","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#rpc-connection-reuse"},{"categories":["Distributed"],"collections":null,"content":"Adaptive Load Estimation 为了使 Pick-2 在两个候选服务器之间选择路由目标，它需要知道负载信息。默认情况下，SR 使用 RPC 服务器上的未完成请求数量来表示其负载 为了确定服务器的负载，客户端有两个选项： 在决定是否向服务器发送请求之前，轮询服务器以获取其负载，这会带来额外的开销和延迟。 让服务器在其响应中包含其负载信息，然后在客户端缓存以供后续重用，这很高效，但可能导致客户端使用过时的负载信息并导致负载不平衡。 Design alternative. LI [13] 试图通过仅使用方法 1 和方法 3 来解决负载估计问题，而不使用方法 2（轮询）。我们生产系统中的数据显示，使用 SR 的自适应机制，大约 50%、25% 和 25% 的 RPC 请求分别使用方法 1、方法 2 和方法 3。这证实了引入 just-in-time polling 方法的有用性。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:5:6","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#adaptive-load-estimation"},{"categories":["Distributed"],"collections":null,"content":"Evaluation Does SR scale well? (§ 4.1) To what extent does SRLib save hardware costs, and when should one use SRProxy versus SRLib? (§ 4.2) Can SR balance load within and across regions? (§ 4.3) Are sharded services important, and can SR effectively support both sharded and unsharded services? (§ 4.4) ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#evaluation"},{"categories":["Distributed"],"collections":null,"content":"Scalability 超大规模是区分 SR 与大多数现有服务网格的关键设计目标。 过去，我们使用 ZooKeeper 作为 RIB 的数据存储，它无法扩展到几 GB 以上，因此我们分片了 RIB。现在我们使用内部数据存储，它扩展性良好，没有进一步分片 RIB 的紧迫性。总体而言，目前 RIB 不是瓶颈，如果需要，可以进一步分片以扩展。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:1","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#scalability"},{"categories":["Distributed"],"collections":null,"content":"Hardware Cost SRLib 和 SRProxy 的 CPU 开销 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:2","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#hardware-cost"},{"categories":["Distributed"],"collections":null,"content":"SRLib versus SRProxy 为了量化硬件成本，我们进行了一项实验，比较了三种 RPC 设置：1) SRLib，客户端使用 SRLib 将请求路由到一个在 10 台机器上运行的简单服务；2) SRProxy，客户端将请求发送到远程 SRProxy，后者将请求转发到服务器；3) Thrift，裸客户端硬编码一个最有效的方式随机选择 10 台服务器之一，并使用 Thrift RPC 协议调用它。SRLib 和 SRProxy 的内部实现也使用 Thrift，但在其上添加了额外的逻辑。因此，Thrift 代表了下限基线。 Case Study of When to Use SRProxy E-Comm. Key-value store ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:3","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#srlib-versus-srproxy"},{"categories":["Distributed"],"collections":null,"content":"SRLib versus SRProxy 为了量化硬件成本，我们进行了一项实验，比较了三种 RPC 设置：1) SRLib，客户端使用 SRLib 将请求路由到一个在 10 台机器上运行的简单服务；2) SRProxy，客户端将请求发送到远程 SRProxy，后者将请求转发到服务器；3) Thrift，裸客户端硬编码一个最有效的方式随机选择 10 台服务器之一，并使用 Thrift RPC 协议调用它。SRLib 和 SRProxy 的内部实现也使用 Thrift，但在其上添加了额外的逻辑。因此，Thrift 代表了下限基线。 Case Study of When to Use SRProxy E-Comm. Key-value store ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:3","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#case-study-of-when-to-use-srproxy"},{"categories":["Distributed"],"collections":null,"content":"Load Balancing SR 在区域内和跨区域执行负载均衡。 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:4","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#load-balancing-1"},{"categories":["Distributed"],"collections":null,"content":"Sharded Services 目前，我们的舰队运行数百个分片服务。尽管它们仅占我们数万个服务的约 3%，但它们产生的流量比其他 97% 的未分片服务更多，因为许多分片服务是我们最大和最高流量的服务之一。 我们的 memcache 是分片的， ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:6:5","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#sharded-services"},{"categories":["Distributed"],"collections":null,"content":"Limitations of SRLib and Our Solutions Dynamic policy updates: Source code modification: SRLib 的一个缺点是它需要对服务进行代码更改。传统的 RPC 使用 IP 地址和端口号来获取 RPC 客户端，而 SRLib 使用服务名称获取 RPC 客户端 Library code deployment 部署新版本的 SRLib 比部署新版本的边车代理更困难。这是因为 SRLib 被编译到数万个服务中，每个服务都有自己的部署计划。 Bugs in SRLib: 如果 SRLib 的新代码有错误，可能很难立即回滚所有服务 ","date":"2024-12-02","objectID":"/posts/paper-servicerouter/:7:0","tags":["Paper Reading"],"title":"Paper Reading: ServiceRouter: Hyperscale and Minimal Cost Service Mesh at Meta","uri":"/posts/paper-servicerouter/#limitations-of-srlib-and-our-solutions"},{"categories":["Distributed"],"collections":null,"content":"From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers 同样是 standford DBOS 组关于 serverless 的论文，作者还有一篇 R2E2 很厉害，用 serverless 做 ray tracing 实现很高的加速，但 cost 估计控制不住 ","date":"2024-11-20","objectID":"/posts/paper-gg/:1:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#from-laptop-to-lambda-outsourcing-everyday-jobs-to-thousands-of-transient-functional-containers"},{"categories":["Distributed"],"collections":null,"content":"Abstract gg framework users might push a button that spawns 10,000 parallel cloud functions to execute a large job in a few seconds from start. 通过 gg，应用程序将任务表示为轻量级操作系统容器的组合，这些容器是单独瞬态的（生命周期为 1-60 秒）和功能性的（每个容器都是密封且确定性的）。gg 负责在云函数上实例化这些容器，加载依赖项，最小化数据移动，在容器之间移动数据，并处理故障和滞后者。 我们移植了几个对延迟敏感的应用程序以在 gg 上运行，并评估了其性能。在最佳情况下，基于 gg 构建的分布式编译器比传统工具（icecc）快 2-5 倍，而无需持续运行的 warm 集群。在最差情况下，gg 的性能与现有视频编码工具（ExCamera）的手动调优性能相差 20% 以内。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:2:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#abstract"},{"categories":["Distributed"],"collections":null,"content":"Introduction 云函数，也称为无服务器计算。Amazon 的 Lambda 服务将以每 100 毫秒的最小间隔出租一个 Linux 容器来运行任意的 x86-64 可执行文件，启动时间不到一秒，空闲时不收费。 云函数原本是为异步调用的微服务设计的，但其粒度和规模使得研究人员探索了一种不同的用途：作为按需的突发超级计算机。 最近的工作验证了这一愿景。ExCamera [15] 和 Sprocket [3] 启动了数千个云函数，通过 TCP 进行线程间通信，以快速编码、搜索和转换视频文件。PyWren [23] 提供了一个 Python API，并使用 AWS Lambda 函数进行线性代数和机器学习。Serverless MapReduce [35] 和 Spark-on-Lambda [36] 展示了类似的方法。 不幸的是，在云函数的集群上构建应用程序是困难的。每个应用程序都必须克服这个环境中固有的一系列挑战： （1）工作者是无状态的，可能需要在启动时下载大量代码和数据， （2）工作者在运行时有限，之后会被终止， （3）工作者上的存储是有限的，但比外部存储快得多， （4）可用云工作者的数量取决于提供商的整体负载，无法提前精确知道， （5）在大规模运行时会发生工作者故障， （6）云函数与本地机器上的库和依赖项不同， （7）云的延迟使得往返成本高昂。过去的应用程序只以特定的方式解决了这些挑战的一部分。 gg 与其他并行执行系统。在目标和方法上，gg 与容器编排系统如 Kubernetes [5] 和 Docker Swarm [10]、外包工具如 Utility Coprocessor [12] 和 icecc [20]、以及集群计算工具如 Hadoop [38]、Dryad [22]、Spark [40] 和 CIEL [27] 有相似之处。 但 gg 也与这些系统在关注点上有所不同，它专注于新的计算基底（云函数）、执行模式（从零开始的突发并行、对延迟敏感的程序）和目标应用领域（日常的“本地”程序，例如软件编译，依赖于从用户自己的笔记本电脑捕获的环境）。 例如，云函数的“无状态”特性（它们启动时没有任何可靠的瞬态状态）使得 gg 特别关注高效的容器化和依赖管理：在启动时将正确文件的最小集合加载到每个容器中。 gg 在启动速度上比 Google Kubernetes Engine 快 45 倍，比 Spark-on-Lambda 快 13 倍（图 7）。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:3:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#introduction"},{"categories":["Distributed"],"collections":null,"content":"Summary of Results 我们将四个应用程序移植到 gg 的格式中，以表达它们的工作：每个容器的描述以及它如何依赖于其他容器，我们称之为 intermediate representation (IR)（§3）。其中一个应用程序通过从现有的软件构建系统（例如 make 或 ninja）推断 IR 来自动完成此操作。其余的应用程序则明确地写出描述：一个单元测试框架（Google Test [17]）、带有线程间通信的并行视频编码（ExCamera [15]），以及使用 Scanner [30]和 TensorFlow [1]的对象识别。 这里的 IR 是 LLVM 类似的概念吗 然后，我们为五个计算引擎（本地机器、a cluster of warm VMs、AWS Lambda、IBM Cloud Functions 和 Google Cloud Functions）和三个存储引擎（S3、Google Cloud Storage 和 Redis）实现了 gg 的后端，这些后端解释 IR 并执行任务 对于从冷启动编译大型程序，gg 的功能性方法和细粒度的依赖管理带来了显著的性能提升。 总之，gg 是一个实用的工具，解决了突发并行云函数应用程序面临的主要挑战。它帮助开发者和用户构建应用程序，从零突发到数千个并行线程，以实现日常任务的低延迟。gg 是开源软件，源代码在 https://snr.stanford.edu/gg ","date":"2024-11-20","objectID":"/posts/paper-gg/:3:1","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#summary-of-results"},{"categories":["Distributed"],"collections":null,"content":"Related Work gg 有许多前身——集群计算系统如 Hadoop [38]、Spark [40]、Dryad [22]和 CIEL [27]；容器编排系统如 Docker Swarm 和 Kubernetes；外包工具如 distcc [8]、icecc [20]和 UCop [12]；基于规则的工作流系统如 make [13]、CMake [7]和 Bazel [4]；以及云函数工具如 ExCamera/mu [15]、PyWren [23]和 Spark-on-Lambda [36]。 Process migration and outsourcing: gg 从冷启动开始编排数千个不可靠和无状态的云函数 Container orchestration: gg 的 IR 类似于容器和环境描述语言，包括 Docker [10]和 Vagrant [34]，以及容器编排系统如 Docker Swarm 和 Kubernetes。与这些系统相比，gg 的 thunk 设计为在云函数中高效实例化， thunk 到底是什么，延迟计算？ haskeel 里的惰性求值？ Workflow systems. gg is aimed at a different kind of application gg uses OS abstractions gg is considerably lighter weight. gg supports dynamic data access 为什么说 spark 不能用于编译？ 动态数据访问也不太理解，这篇论文前半部分有点莫名其妙的，讲一大堆 related work gg 支持非 DAG 数据流？？怎么实现的 Burst-parallel cloud functions: gg 的主要贡献是指定一个 IR，允许各种应用程序（用任何编程语言编写）从计算和存储平台中抽象出来，并利用常见的服务进行依赖管理、滞后者缓解和调度。 Build tools: 几个构建系统（例如 make [13]、Bazel [4]、Nix [11]和 Vesta [19]）和外包工具（如 distcc [8]、icecc [20]和 mrcc [26]）寻求增量化、并行化或将编译分布到更强大的远程机器上。基于这些系统，gg 自动将现有的构建过程转换为其自己的 IR。目标是快速编译程序——不考虑软件自身的构建系统——通过利用可以从完全休眠状态突发到数千倍并行性并返回的云函数平台。 现有的远程编译系统 remote compilation systems，包括 distcc 和 icecc，在构建过程中频繁地在主节点和工作节点之间传输数据。这些系统在局域网上表现最佳，并且在云中更远程的服务器上构建时会增加大量延迟。相比之下，gg 一次性上传所有构建输入，并在云中纯粹执行和交换数据，减少了网络延迟的影响。 gg 的卖点好像注重分布式编译 ","date":"2024-11-20","objectID":"/posts/paper-gg/:4:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#related-work"},{"categories":["Distributed"],"collections":null,"content":"Design and Implementation gg 被设计为一个通用系统，帮助应用程序开发者管理创建突发并行云函数应用程序的挑战。预期用户将把通常在本地或小型集群上长时间运行的计算（例如测试套件、机器学习、数据探索和分析、软件编译、视频编码和处理）外包给云中数千个短暂的并行线程，以实现接近交互式的完成时间。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#design-and-implementation"},{"categories":["Distributed"],"collections":null,"content":"gg’s Intermediate Representation gg 使用的格式——一组描述容器及其对其他容器依赖关系的文档——旨在从应用程序中提取足够的信息（细粒度的依赖关系和数据流），以便能够在受限和无状态的云函数上高效执行任务。它包括： 内容寻址 cloud thunk 的原语：应用于命名输入数据的代码片段或可执行文件。 中间表示（IR），将任务表示为相互依赖的 thunk 的惰性求值 lambda 表达式。 一种策略，用于以语言无关和可记忆化的方式表示动态计算图和数据访问模式，使用尾递归。 我们讨论这些元素中的每一个。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:1","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#ggs-intermediate-representation"},{"categories":["Distributed"],"collections":null,"content":"Thunk: A Lightweight Container 在函数式编程文献中，thunk 是一个无参数的闭包（函数），它捕获其参数和环境的快照以供以后求值。求值 thunk 的过程——将函数应用于其参数并保存结果——称为强制求值[2]。 对于 gg，我们的目标是简化新应用程序的创建,允许它们针对 IR，从而利用后端引擎提供的常见服务。因此，thunk 的表示遵循几个设计目标。它应该是：（1）足够简单，以便可以移植到不同的计算和存储平台，（2）足够通用，以表达各种合理的应用程序，（3）与实现函数的编程语言无关，（4）足够高效，以捕获可以在无状态和空间受限的云函数上具体化的细粒度依赖关系，（5）能够记忆化以防止重复工作。 怎么记忆化？ 在内容寻址方案中，对象的名称有四个组件：（1）对象是原始值（哈希以 V 开头）还是表示强制其他 thunk 的结果（哈希以 T 开头），（2）SHA-256 哈希，（3）字节长度，（4）可选标签，命名对象或 thunk 的输出。 强制 thunk 意味着实例化描述的容器并运行代码。为此，执行器必须获取代码和数据值。因为这些是内容寻址的，所以可以从任何能够生成具有正确名称的 blob 的机制中获取——持久或短暂的存储（例如 S3、Redis 或 Bigtable）、从另一个节点的网络传输，或者通过在先前执行中已经可用的 RAM 中找到对象。然后，执行器使用提供的参数和环境运行可执行文件——出于调试或安全目的，最好是在防止可执行文件访问网络或未列为依赖项的任何数据的模式下运行。执行器收集输出 blob，计算其哈希值，并记录输出可以替换对刚刚强制的 thunk 的任何引用。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:2","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#thunk-a-lightweight-container"},{"categories":["Distributed"],"collections":null,"content":"gg IR: A Lazily Evaluated Lambda Expression Figure 3 使用哈希来追踪 thunk 依赖？ 相互依赖的 thunk 的结构定义了 gg 的 IR，我们使用单向 IR，一种应用程序用来表达其任务的文档格式，而不是双向 API（例如，调用函数来生成新任务并观察其结果），因为我们期望应用程序将在用户的计算机上运行，而在某个远程云函数引擎上执行：目的是通过将应用程序排除在外来避免通过高延迟路径的往返。我们还设想，如果应用程序在执行开始之前被排除在外，将更容易更好地调度和优化任务，并更容易维护不同的可互操作的后端。2 这种表示形式向后台暴露了计算图，以及需要在 thunk 之间通信的对象的身份和大小。基于这些信息，后台可以调度 thunk 的强制执行，将具有相似数据依赖性或输出-输入关系的 thunk 放置在相同的物理基础设施上，并管理中间结果的存储或传输，而无需往返用户的计算机。 IR 允许 gg 高效地调度任务，通过在关键路径上调用多个并发 thunk 来缓解滞后者的影响，通过第二次强制 thunk 来恢复故障，并记忆化 thunk。这是以应用程序无关、语言无关的方式实现的。 应用程序通常从强制表示交互操作最终结果的单个 thunk 开始。这个 thunk 通常依赖于其他需要首先强制的 thunk，等等，导致后台递归地惰性强制 thunk，直到获得最终结果。图 3 显示了一个计算表达式 ASSEMBLE(COMPILE(PREPROCESS(hello.c)))的示例 IR。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:3","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#gg-ir-a-lazily-evaluated-lambda-expression"},{"categories":["Distributed"],"collections":null,"content":"Tail Recursion: Supporting Dynamic Execution 上述设计足以描述在云中执行的确定性任务的有向无环图（DAG）。然而，许多任务的数据访问模式并不是完全预先已知的。例如，在编译软件时，无法事先知道某个阶段需要读取哪些头文件和库。其他应用程序使用循环、递归和其他非 DAG 数据流。 相反，gg 通过语言无关的尾递归来处理这种情况：一个 thunk 可以将其输出写为另一个 thunk。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:4","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#tail-recursion-supporting-dynamic-execution"},{"categories":["Distributed"],"collections":null,"content":"Front-ends 我们开发了四个前端，它们生成 gg IR：一个 C++ SDK、一个 Python SDK、一组命令行工具和一系列模型替换原语，可以从软件构建系统推断 gg IR。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:5","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#front-ends"},{"categories":["Distributed"],"collections":null,"content":"Back-ends gg IR 表达应用程序针对一个抽象机器，该机器需要两个组件：一个用于强制单个 thunk 的执行引擎，以及一个用于存储 thunk 引用或生成的命名 blob 的内容寻址存储引擎。协调程序将这两个组件结合在一起。 Storage engine 存储引擎提供了一个简单的接口，用于内容寻址存储，包括 GET 和 PUT 函数来检索和存储对象。 Execution engine 与存储引擎结合，每个执行引擎实现了一个简单的抽象：一个接收 thunk 作为输入并返回其输出对象哈希（可以是值或 thunk）的函数。 The coordinator 执行 thunk 的主要入口点是协调程序。该程序的输入是目标 thunk、可用执行引擎列表和存储引擎。该程序实现了 gg 提供的服务，如作业调度、记忆化、故障恢复和滞后者缓 启动时，该程序具体化目标 thunk 的依赖图，其中包括获取输出所需的所有其他 thunk。然后，根据其可用容量，将准备好执行的 thunk 传递给执行引擎。当 thunk 的执行完成后，程序通过替换对刚刚强制的 thunk 的引用来更新图，并添加一个缓存条目，将输出哈希与输入哈希关联。准备好执行的 thunk 被放置在队列中，并在其容量允许时传递给执行引擎。统一的接口允许用户混合和匹配不同的执行引擎，只要它们共享相同的存储引擎。 调用、执行和放置的细节留给执行引擎。例如，AWS Lambda/S3 的默认引擎为每个 thunk 调用一个新的 Lambda。Lambda 从 S3 下载所有依赖项并设置环境，执行 thunk，将输出上传回 S3 并关闭。对于具有大输入/输出对象的应用程序，往返 S3 可能会影响性能。作为此类情况的优化，用户可以选择以“长期运行”模式运行执行引擎，其中每个 Lambda 工作者保持活动状态，直到作业完成并寻找新的 thunk 来执行。执行引擎维护每个工作者本地存储中已存在的所有对象的索引。在将 thunk 放置在工作者上时，它选择具有最多可用数据的工作者，以最小化从存储后端获取依赖项的需求。 协调程序还可以对依赖图应用优化。例如，多个 thunk 可以捆绑为一个并发送给执行引擎。当一个 thunk 的输出将被下一个 thunk 消耗时，这很有用，创建了一个线性工作管道。通过在一个工作者上调度所有这些 thunk，系统减少了往返次数。 Failure recovery and straggler mitigation. 在协调程序失败的情况下，作业可以从上次中断的地方继续，因为协调程序使用磁盘缓存条目来避免重复已经完成的工作 Straggler mitigation 滞后者缓解是协调程序管理的另一项服务，它会在相同或不同的执行引擎中复制挂起的执行。程序跟踪每个 thunk 的执行时间，如果执行时间超过超时（由用户或应用程序开发者设置），作业将被复制。由于函数没有任何副作用，协调程序只需选择首先准备好的输出。 麻了随便看看了这论文，讲解的不是很清晰 ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:6","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#back-ends"},{"categories":["Distributed"],"collections":null,"content":"Implementation Notes ","date":"2024-11-20","objectID":"/posts/paper-gg/:5:7","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#implementation-notes"},{"categories":["Distributed"],"collections":null,"content":"Applications We used gg to implement several applications, each emitting jobs in the gg IR. We describe these in turn ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#applications"},{"categories":["Distributed"],"collections":null,"content":"Software Compilation 软件编译所需的时间一直是软件开发者的持续烦恼；甚至有一幅流行的卡通讽刺了这个任务的持续时间[39]。如今的开源应用程序已经变得越来越大。例如，从冷启动开始，Chromium Web 浏览器在四核笔记本电脑上编译需要四个多小时。已经开发了许多解决方案来利用本地集群或云数据中心的温暖机器（例如 distcc 或 icecc）。我们在 gg 之上开发了这样一个应用程序。 使用模型替换，我们为 C 或 C++软件构建管道的七个流行阶段实现了模型：预处理器、编译器、汇编器、链接器、归档器、索引器和 strip。这些模型允许我们自动将一些软件构建过程（例如 Makefile 或 build.ninja 文件）转换为 gg IR 中的表达式，然后可以在云函数平台上以数千倍的并行性执行，以获得与本地执行构建系统相同的结果。图 4 展示了一个示例调用生成的 IR（枚举的 thunk 在图 3 中详细说明）。这些模型足以捕获一些主要开源应用程序的构建过程，包括 OpenSSH [29]、Python 解释器 [32]、Protobuf 库 [31]、FFmpeg 视频系统 [14]、GIMP 图像编辑器 [16]、Inkscape 矢量图形编辑器 [21]和 Chromium 浏览器 [6] Capturing dependencies of the preprocessor. 为了解决这个问题，应用程序使用 gg 在运行时动态数据流的能力。gg 的预处理器模型生成 thunk，这些 thunk 在云函数上并行进行依赖项推断。这些 thunk 只能访问用户包含目录的简化版本，仅保留带有 C 预处理器指令（如#include 和#define）的行。然后，这些 thunk 生成进一步的 thunk，通过仅列出必要的头文件来预处理给定的源代码文件。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:1","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#software-compilation"},{"categories":["Distributed"],"collections":null,"content":"Unit Testing 通常，每个测试都是一个独立的程序，可以与其他测试并行运行，它们之间没有依赖关系。代码不需要更改，但有一个例外：如果测试用例需要访问文件系统上的文件，那么程序员必须在测试用例中注释出它想要访问的文件列表。这个过程可以通过在本地运行测试，然后跟踪每个测试用例调用的打开系统调用来实现自动化。该工具使用这些注释，无论是手工编写的还是自动生成的，来捕获每个测试的依赖项。为每个测试用例创建一个单独的 thunk，允许执行引擎利用可用的并行性。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:2","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#unit-testing"},{"categories":["Distributed"],"collections":null,"content":"Video Encoding ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:3","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#video-encoding"},{"categories":["Distributed"],"collections":null,"content":"Object Recognition ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:4","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#object-recognition"},{"categories":["Distributed"],"collections":null,"content":"Recursive Fibonacci ","date":"2024-11-20","objectID":"/posts/paper-gg/:6:5","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#recursive-fibonacci"},{"categories":["Distributed"],"collections":null,"content":"Evaluation ","date":"2024-11-20","objectID":"/posts/paper-gg/:7:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#evaluation"},{"categories":["Distributed"],"collections":null,"content":"Startup Overhead 为了说明 gg 轻量级抽象的重要性，我们使用四个框架实现了一个简单的任务：1,000 个并行任务，每个任务执行 sleep(2)，使用的框架是 gg、PyWren、Spark-on-Lambda 和 Kubernetes。前三个框架在 AWS Lambda 上执行，最后一个在 Google Kubernetes Engine (GKE)上执行，GKE 提供了一个温暖的集群，包含十一个 96 核虚拟机（总共 1,056 个核心），用于分配容器。图 7 显示了结果。 gg 能够快速扩展到 1,000 个并发容器，并且与其他两个在 Lambda 上运行的框架相比，完成任务的速度快 7.5-9 倍。减去 2 秒的睡眠时间后，这相当于减少了 11-13 倍的额外开销。对于 PyWren，平均每个工作者花费 70%的时间设置 Python 运行时（下载和解压 tarball）。这个运行时的大部分由我们的 sleep(2)程序未使用的包组成（参见 gg 的细粒度依赖跟踪）。Google Kubernetes Engine 不是为瞬态计算设计的，也没有针对这种用例进行优化；启动 1,000 个 Docker 容器要慢得多。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:7:1","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#startup-overhead"},{"categories":["Distributed"],"collections":null,"content":"Software Compilation 为了基准测试 gg 的性能，我们选择了四个用 C 或 C++编写的开源程序：FFmpeg、GIMP、Inkscape 和 Chromium。这些包的代码或底层构建系统没有进行任何更改。我们使用 GCC 7.2 编译所有包 图 9 显示了包构建的中位时间。gg 在构建中等和大型软件包时比传统工具（icecc）快约 2-5 倍。例如，gg 在 AWS Lambda 上编译 Inkscape 仅需 87 秒，而使用 icecc 外包到温暖的 384 核集群需要 7 分钟。这是 4.8 倍的加速。Chromium 是最大的可用开源项目之一，使用 gg 在 AWS Lambda 上编译仅需不到 20 分钟，比 icecc (384)快 2.2 倍。 我们认为 gg 在 AWS Lambda 上的性能提升不能简单地归因于比我们的 384 核集群更多的核心；icecc 在 48 核和 384 核情况下的改进只是适度，并且似乎没有有效利用更高的并行度。这主要是因为 icecc 为了简化依赖跟踪，在本地运行预处理器，这成为了一个主要瓶颈。gg 的细粒度依赖跟踪允许系统将这一步骤高效外包到云端，并最小化本地机器上的工作。 图 10 显示了编译 Inkscape 的执行分解。我们观察到两个重要特征。首先，大的峰值对应于失败的或比平时完成时间更长的 Lambda。gg 的滞后者缓解检测并重新启动这些任务，以防止端到端延迟增加。其次，最后几个任务主要是串行的（归档和链接），消耗了近四分之一的总任务完成时间。这些特征在其他构建任务中也有所体现。 真的莫名其妙的论文，看不懂 ","date":"2024-11-20","objectID":"/posts/paper-gg/:7:2","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#software-compilation-1"},{"categories":["Distributed"],"collections":null,"content":"Limitations and Discussion 仅限于 CPU 程序 gg 将代码格式指定为 x86-64 Linux ELF 可执行文件。IR 没有机制来表示对 GPU 或其他加速器的需求，并且高效调度这些资源提出了非平凡的挑战，因为从 GPU 加载和卸载配置状态比内存映射文件更昂贵的操作。我们计划研究 gg 后端将 thunk 调度到 GPU 的适当机制。 这是绝大部分 serverless 的问题吧 ","date":"2024-11-20","objectID":"/posts/paper-gg/:8:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#limitations-and-discussion"},{"categories":["Distributed"],"collections":null,"content":"Conclusion 在本文中，我们描述了 gg，一个帮助开发者构建和执行突发并行应用程序的框架。gg 提供了一个可移植的抽象：一个中间表示（IR），将任务的未来执行捕获为轻量级 Linux 容器的组合。这使得 gg 能够支持新旧应用程序，这些应用程序在各种语言中从计算和存储平台以及解决底层挑战的运行时特性中抽象出来：依赖管理、滞后者缓解、放置和记忆化。 ","date":"2024-11-20","objectID":"/posts/paper-gg/:9:0","tags":["Paper Reading"],"title":"Paper Reading: From Laptop to Lambda: Outsourcing Everyday Jobs to Thousands of Transient Functional Containers","uri":"/posts/paper-gg/#conclusion"},{"categories":["Database"],"collections":null,"content":"Apiary: A DBMS-Backed Transactional Function-as-a-Service Framework 来自 DBOS 的一个很有意思的项目，DBOS 这个项目去年关注过，txn + serverless 很有趣的思路 传统 FaaS 客户端调用函数去连接 DB 查询 DBOS 直接 DB as OS，提出万物都是表的概念 https://juejin.cn/post/7345644573652582440 InfoQ 和 MeetDBOS 的一些介绍 https://www.infoq.cn/article/r9eev0oqqs8qip31eutg \u0026gt; https://www.infoq.com/news/2024/03/dbos-cloud-serverless/ 这一篇 Apiary 应该是开源版本的 DBOS，DBOS 本身是不开源的，DBOS 论文讨论了不少实现和 IPC 与 scheduler https://zhuanlan.zhihu.com/p/463722216 丁凯大佬的 DBOS 解读 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#apiary-a-dbms-backed-transactional-function-as-a-service-framework"},{"categories":["Database"],"collections":null,"content":"Abstract 开发人员越来越多地使用函数即服务（FaaS）平台来构建数据密集型应用程序，这些应用程序需要对数据进行低延迟和事务性操作，例如用于微服务或 Web 服务。然而，现有的 FaaS 平台在这些应用程序的支持上表现不佳，因为它们在物理上和逻辑上将应用程序逻辑（在云函数中执行）与数据管理（通过访问远程存储的交互式事务完成）分离开来。物理上的分离损害了性能，而逻辑上的分离则使得高效提供事务保证和容错性变得复杂。 本文介绍了一种名为 Apiary 的新型 DBMS 集成 FaaS 平台，用于部署和组合具有容错性的事务性函数。Apiary 通过将分布式 DBMS 引擎封装并将其用作函数执行、数据管理和操作日志记录的统一运行时，从而在物理上共置和逻辑上集成函数执行和数据管理，从而提供与同类系统相当或更强的事务保证，同时显著提高性能和可观察性。 结果显示，在微服务工作负载上，Apiary 的表现优于它们 2 到 68 倍，通过减少通信开销实现了这一优势。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#abstract"},{"categories":["Database"],"collections":null,"content":"Introduction FaaS 模型在数据密集型应用程序中越来越受欢迎：例如电子商务 Web 服务等需要低延迟和事务性操作的应用程序。然而，现有的 FaaS 平台在这些应用程序的支持上表现不佳，因为它们在物理上和逻辑上将函数执行与数据管理分离开来，采用了图 1a 所示的架构，并在每次数据操作时调用远程 DBMS 真的吗？这里说的是生产还是学术的 faas 模型。。 不一定所有 faas 都会去调用 dbms 把 逻辑上的分离使得容错性和事务保证变得复杂，因为函数可能会被任意地重新执行，而事务不能跨越多个函数。它们要么提供物理上的共置以获得良好的性能，要么提供逻辑上的集成以获得强有力的保证，但无法同时做到两者。 例如，Cloudburst[42]通过使用本地缓存来物理上共置计算和数据，但不提供事务支持。其他如 Boki[22]和 Beldi[47]通过远程存储上的外部事务管理器提供事务性函数，但这增加了已经很高的存储访问时间，达到了 3 倍。 本文介绍了 Apiary，一种用于数据密集型应用程序的事务性、高性能 FaaS 平台。与现有平台不同，Apiary 通过封装分布式 DBMS 引擎并将其用作函数执行、数据管理和操作日志记录的统一运行时，从而在物理上共置和逻辑上集成函数执行和数据管理（图 1b）。我们将函数编译为存储过程，即在非 SQL 语言中运行的本地 DBMS 事务，从而使函数成为控制流和原子性的基本单位。然后，我们利用这种集成来支持高效、容错性的函数组合和新颖的可观察性能力。我们证明，Apiary 提供了（a）与同类平台相当或更强的保证，（b）与最先进的系统相比，性能提升了 2 到 68 倍，以及（c）自动跟踪应用程序与数据库交互以实现可观察性。 设计用于数据密集型应用程序的 FaaS 平台的主要挑战是提供高效、容错性的函数组合。FaaS 开发者通过将函数组合成工作流 workflows 来编写复杂的程序。为了在故障面前保持稳健，工作流需要强大的执行保证：它们必须始终运行到完成，并且每个函数必须恰好执行一次。现有平台通过要求函数具有幂等性[6]或构建昂贵的外部事务管理器[47]来提供这些保证。相比之下，我们可以利用 Apiary 中函数和数据的集成，构建一个容错性前端，以最小的开发者要求和低开销提供这些保证。由于函数是存储过程，Apiary 可以对它们进行检测，以事务性地记录它们在 DBMS 中的执行情况。因此，如果工作流执行失败，前端可以通过从头开始调用其函数来安全地恢复它，跳过已经执行过的函数。然而，naively 地检测 instrumenting 所有函数是昂贵的，会降低性能高达 2.2 倍。因此，我们开发了一种新颖的算法来识别函数何时可以安全地重新执行，将开销降低到 \u0026lt;5%。 exectly once 保证 idempotent 幂等 Apiary 还解决了 FaaS 开发者面临的一个常见挑战：了解应用程序如何与数据交互的可观察性。 tracing 是 dbos 的一个特色 总结起来，我们的贡献如下： 我们提出了 Apiary，一种事务性 FaaS 平台，通过封装分布式 DBMS 在物理上共置和逻辑上集成函数与数据。Apiary 在提供类似或更强保证的同时，性能优于研究和生产平台 2 到 68 倍。 我们利用 Apiary 的架构设计了一个 fault-tolerant frontend，用于编排函数工作流。它保证了无论是否发生故障，工作流总是能运行到完成，并且其中的每个函数都恰好执行一次。 我们利用 Apiary 的架构来增强可观察性，通过自动检测应用程序及其与数据的交互，实现了\u0026lt;15%的开销，相比手动日志记录的 92%开销有了显著改进。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#introduction"},{"categories":["Database"],"collections":null,"content":"Apiary Overview ","date":"2024-11-16","objectID":"/posts/paper-apiary/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#apiary-overview"},{"categories":["Database"],"collections":null,"content":"System Architecture Apiary 的设计动机源于一个关键观察：数据密集型应用程序在其运行时中，大部分时间要么在与 DBMS 通信，要么在执行 DBMS 操作。 这是假设还是观察？ 其实我一直不明白 DBOS 是做了个 serverless DB 还是什么 看起来是非存算分离的架构？ 我们将 Apiary 设计为通过封装分布式 DBMS 并将函数编译为数据库存储过程，从而在物理上共置计算和数据。由于存储过程是事务性的，这种架构也在逻辑上集成了函数执行和数据管理；我们利用这种集成来高效地提供事务保证、容错性和可观察性 Clients 客户端通过客户端库向前端发送请求，以调用工作流和函数，这些工作流和函数在后端执行。开发者使用我们的编程接口编写函数并组合工作流。 Frontend 前端服务器将客户端请求路由并认证到后端。每个服务器都有一个调度器，它通过在后端调用每个函数，传入其输入，收集其输出以发送给后续函数，并强制执行容错保证（§3，§4）来管理工作流执行。服务器还包含注册器，负责函数和工作流的注册、检测和编译。 Backend 后端执行函数并管理数据。它封装了一个分布式 DBMS 及其存储过程。Apiary 在 DBMS 服务器上以事务性方式执行函数，对其进行检测以提供容错性（§3，§4），并捕获应用程序与数据库交互的信息以实现可观察性（§5）。由于函数在物理上与 DBMS 共置，我们依赖 DBMS 的本地弹性扩展能力来扩展后端。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#system-architecture"},{"categories":["Database"],"collections":null,"content":"Non-Goals Compute-Heavy Workloads Apiary 的设计专注于短生命周期的数据密集型应用程序，而不是长时间运行的计算密集型工作负载，如视频处理[17]或批量分析[37] Non-Relational Data Models Apiary 目前仅支持关系型数据模型。我们相信可以将 Apiary 扩展以支持事务性的非关系型数据库，如 MongoDB，但这超出了本文的范围。 其实几个微服务 benchmark 也有 mongodb 把 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#non-goals"},{"categories":["Database"],"collections":null,"content":"Apiary Semantics ","date":"2024-11-16","objectID":"/posts/paper-apiary/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#apiary-semantics"},{"categories":["Database"],"collections":null,"content":"Programming Interface 酒店预订服务的例子来概述其编程接口，该服务检查房间是否可用，预订房间，然后发送确认邮件。 Function Interface: Apiary 要求函数遵循三条规则： 函数中的所有 SQL 查询必须静态定义 defined statically 为参数化预备语句 parameterized prepared statements。 函数必须是确定性 deterministic 的。 外部服务或 API 调用必须是幂等 idempotent 的。 第一条规则支持静态分析（§4）和数据跟踪以实现可观察性（§5）；最后两条规则支持实现恰好一次语义的实际实现（§4）。 Workflow Interface: 开发者使用图 5 中的接口将程序构造为函数的工作流。每个工作流是一个有向无环图（DAG），其中节点是函数，边是数据流，函数的输入是其父函数的输出。开发者从函数列表和将早期函数的输出映射到后期函数输入的规范中构建工作流。不允许递归或循环依赖。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#programming-interface"},{"categories":["Database"],"collections":null,"content":"Transactional Semantics FaaS 程序通常需要事务保证；例如，我们的酒店预订工作流需要事务来保证房间不会被重复预订。 因此，Apiary 在逻辑上集成了函数执行和数据管理：每个函数在数据库中作为可序列化的 ACID 事务执行。这一设计的一个关键含义是，函数既是控制流的基本单位，也是原子性的基本单位；我们利用这一点来实现容错性（§4），并跟踪应用程序与数据库的交互以实现可观察性（§5）。 一个重要的问题是我们为工作流提供什么样的事务语义。天真地，我们可以将整个工作流在一个事务中执行，但这会导致不必要的、性能较差的大型事务 但开发者通常需要跨多个函数的事务保证。例如，在酒店工作流中，前两个函数（checkAvail 和 reserve）必须在一次事务中执行，以确保房间在预订时确实可用。为了平衡这些权衡，我们提供了多函数事务 multi-function transactions: 开发者可以将工作流中的多个函数分组，以作为单个 ACID 事务执行，前提是它们形成工作流图的一个连通子图。这种设计使开发者能够灵活地事务性执行相关操作，但将不相关的操作分开，以避免过大事务的性能开销。 限制蛮大的 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#transactional-semantics"},{"categories":["Database"],"collections":null,"content":"Fault-Tolerance Guarantees First, workflows run to completion 因此即使调度器或 DBMS 服务器的故障导致工作流执行暂停，工作流最终也会恢复。 Second, functions in workflows execute exactly once 因此即使工作流或其任何函数多次失败并重新启动，工作流对应用程序状态（在数据库中）的影响与工作流中的每个函数恰好执行一次的效果相同。例如，在酒店工作流中，Apiary 保证房间只被预订一次，并且如果预订成功，确认邮件总是会被发送。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#fault-tolerance-guarantees"},{"categories":["Database"],"collections":null,"content":"Comparison with Related Systems 在表 1 中，我们将 Apiary 的语义与相关系统的语义进行了比较。Apiary 提供的保证 guarantees 比商业系统如 AWS Step Functions[7]和 Azure Durable Functions[31]要强得多。这些系统支持运行到完成的工作流，但既不提供事务性函数保证，也无法看到应用程序数据库 AWS Step Functions 声称它也可以提供恰好一次的语义，但这实际上是一个最多一次的保证：它在失败时不重试，而是保证任务永远不会运行超过一次 比起 lambda，这类系统应该更加适合 dag 任务 Apiary 提供的保证与事务性 FaaS 系统如 Beldi[47]、Boki[22]和 Transactional Statefun[15]类似。它们都允许开发者为单个函数提供 ACID 事务保证，尽管 Transactional Statefun 实现了一个有限的“one-shot”模型，其中事务中较早查询的输出不能用作同一事务中较晚查询的输入。所有系统都为函数提供了恰好一次的语义，并支持运行到完成的工作流。没有一个系统为整个工作流提供事务保证，但所有系统都支持在单个事务中运行多个函数，类似于 Apiary 的多函数事务。在 Beldi 和 Boki 中，事务性函数可以同步调用其他函数，因此它们都在一个大事务中执行。在 Transactional Statefun 中，“协调器函数”可以通过两阶段提交协调多个其他函数，使它们作为一个事务执行。然而，尽管这些系统在远程存储上构建了昂贵的外部事务管理器，Apiary 通过 co-locating compute and data 来最小化事务开销。 一个重要的相关系统类别是围绕因果一致性构建的 FaaS 平台，如 Cloudburst[42]、Hydrocache[45]和 FaaSTCC[27]。这些系统将数据存储在远程键值存储中，并使用本地缓存来提高性能。它们提供的最强保证是整个工作流的 transactional causal consistency（TCC）。 TCC 保证工作流在完全完成之前不能看到其他工作流的效果，只有当整个工作流在一个多函数事务中执行时，Apiary 或其他事务性 FaaS 系统才能提供这种保证。然而，对于单个函数，TCC 提供了相对较弱的保证，允许严重的异常，如 stale reads 和 write-write conflicts。相比之下，Apiary 将每个函数作为具有可序列化隔离的 ACID 事务运行，不允许这些异常。此外，这些系统仅提供键值 API 进行数据管理，而 Apiary 支持关系模型。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#comparison-with-related-systems"},{"categories":["Database"],"collections":null,"content":"Fault-Tolerant Workflows run-to-completion workflow execution and exactly-once function execution. ","date":"2024-11-16","objectID":"/posts/paper-apiary/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#fault-tolerant-workflows"},{"categories":["Database"],"collections":null,"content":"Handling Machine Failures Apiary 必须在调度器或 DBMS 后端服务器故障的情况下强制执行其执行保证。大多数分布式 DBMS 可以从其服务器故障中恢复，通常使用复制和日志记录。 单个服务器失败，DBMS 可以通过故障转移到副本而不会损失可用性，因此工作流执行不受影响。 多个服务器失败，DBMS 可以从持久日志中恢复而不会丢失数据，因此调度器必须等到恢复完成后再恢复执行。 如果在工作流执行期间调度器失败，具有待处理工作流调用的客户端会超时，然后重新提交其调用以在新调度器上恢复部分执行的工作流。为了唯一标识工作流以进行恢复，客户端为每次工作流调用生成一个唯一 ID，并使用数据库生成的唯一客户端 ID 作为前缀。新调度器必须从失败调度器停止的地方恢复工作流执行，完成工作流而不重新执行任何已完成的函数。由于 Apiary 将函数作为存储过程以事务性方式运行，我们可以通过检测函数在返回之前以事务性方式记录其输出（以二进制格式序列化）来实现这一点。每个记录的输出都与一个唯一函数调用 ID 相关联，该 ID 派生自工作流 ID，并且仅在工作流的生命周期内保留。在重试期间，调度器从开始恢复工作流并（重新）调度每个函数。函数首先检查早期执行的记录，如果找到一个，则返回它而不是执行。 当前实现的局限性在于它依赖客户端检测调度器故障。在未来的工作中，我们计划通过让调度器将工作流元数据预写到 DBMS 中，并以去中心化的方式相互 ping 以检测故障（使用 DBMS 进行发现）来解决这个问题。然后，检测到另一个调度器故障的调度器可以从 DBMS 中检索其待处理的工作流并完成其执行，而无需客户端参与。 比如酒店预订，如果某个函数出错，发生重试，唯一标识 客户端来检测超时，怎么设置呢？ ","date":"2024-11-16","objectID":"/posts/paper-apiary/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#handling-machine-failures"},{"categories":["Database"],"collections":null,"content":"Optimizing Function Recording 协议在数据库中记录每个函数的输出，但这样做天真地会导致高达 2.2 倍的开销，因为它需要在每个函数中执行额外的数据库查找和更新。 然而，我们可以通过识别一些函数可以在不违反恰好一次语义的情况下安全地重新执行，从而将这种开销减少到\u0026lt;5%（在我们测试的所有工作负载中），因此它们的输出不需要记录。例如，如果整个工作流是只读的，如果其原始执行失败，它可以安全地重新执行，因此我们不需要记录其任何函数。因此，我们开发了一种新的算法，称为选择性函数记录 selective function recording（SFR），使用静态分析在工作流注册时确定哪些函数必须记录，哪些可以安全地重新执行。 我们必须记录任何执行 DBMS 写操作的函数，以确保写操作不会重新执行，此外，如果存在从它到多个不同记录函数或至少一个记录函数和汇的不相交路径，我们必须记录一个只读函数。 我们在算法 1 中概述了 SFR Correctness: 我们不保证找到最小的记录函数集 没给证明，看上去是个拓扑排序，找相关联的，但是说时间复杂度很低，有点奇怪 Complexity: 我们可以记忆化工作流图搜索，因此，SFR 的时间复杂度为 O(V + E)，其中 V 是函数的数量，E 是工作流图中的边数。我们只在每次工作流注册时运行此算法一次。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#optimizing-function-recording"},{"categories":["Database"],"collections":null,"content":"Handling Function Failures Apiary 还必须在单个函数出现故障或错误的情况下强制执行其执行保证。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#handling-function-failures"},{"categories":["Database"],"collections":null,"content":"Observability 开发者通常需要了解应用程序如何与数据交互的信息，以便用于调试、监控和审计用途，例如验证程序是否未不当访问私有数据 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#observability"},{"categories":["Database"],"collections":null,"content":"Observability Interface Apiary 检测工作流以跟踪工作流和函数执行的历史，检测查询以记录数据库操作，然后将这些信息结合起来，创建应用程序与数据交互的完整记录。 跟踪层自动将此信息 spool 到分析数据库（在我们的实现中，Vertica[43]）以进行长期存储和分析 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#observability-interface"},{"categories":["Database"],"collections":null,"content":"Implementing Tracing Layer 我们利用 Apiary 与数据的紧密集成，将数据库技术（如变更数据捕获和查询重写[4, 20]）适应到 FaaS 环境中，构建一个跟踪层，以高效捕获应用程序与数据的交互。 当一个函数执行时，跟踪层向 FunctionInvocations 添加一个条目。 实际上应该就是 log？ CDC + 写入 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#implementing-tracing-layer"},{"categories":["Database"],"collections":null,"content":"Implementation ","date":"2024-11-16","objectID":"/posts/paper-apiary/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#implementation"},{"categories":["Database"],"collections":null,"content":"Choosing a DBMS Apiary 需要一个具有以下四个特性的分布式 DBMS： 支持 ACID 事务。 支持在存储过程中以事务性方式运行非 SQL 语言的用户代码。 支持 change data capture（用于可观察性，§5.2）。 支持弹性 DBMS 集群调整大小。 尽管许多 DBMS 具有这些特性（例如，SingleStore[40]，Yugabyte[46]），我们选择了 VoltDB，因为它能最有效地执行我们的目标工作负载。大多数分布式 DBMS，包括 VoltDB，通过数据分区来扩展。我们观察到，在我们的目标工作负载中，几乎所有事务都是单站点[24]的，并且只访问单个分区中的数据。VoltDB 高效地执行这些事务，在内存中运行它们到完成，而不需要锁。然而，VoltDB 的一个限制是它在多站点事务中效率较低；一个事务必须持有全局锁才能访问多个分区上的数据。最近有一些研究试图解决这个问题[49]，但我们把高效实现 multi-sited transactions 多站点事务留到未来的工作中。 之前见过一些 serverless 框架也用 voltdb 实现，有机会看一下 voltdb 多站点事务：事务涉及多个分区的数据。例如，在下订单时，需要同时更新库存和订单信息，而这两个数据可能位于不同的分区。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#choosing-a-dbms"},{"categories":["Database"],"collections":null,"content":"Compilation 当开发者在 Apiary 中注册函数和工作流时，它将每个函数编译为一个存储过程，这是一个在非 SQL 语言中运行的本地 DBMS 事务。 在我们的实现中，函数提供的保证与 VoltDB 事务相同：它们是 ACID 和可序列化的。为了实现多函数事务，Apiary 将所有涉及的函数编译为一个存储过程。编译分两步进行。首先，Apiary 检测每个函数以捕获应用程序与数据库的交互（§5），并记录其执行以实现恰好一次语义（§4）。然后，Apiary 将检测后的函数（或多函数事务）编译为存储过程，并将其注册到 DBMS 中。 Apiary 扩展了 DBMS 存储过程接口，因此它可以编译任何使用其编程接口（图 5）并遵循§3.1 中概述的规则的函数。此外，在我们的基于 VoltDB 的实现中，由于 VoltDB 可以高效执行单站点事务，我们让开发者指定一个函数（或多函数事务）是否是单站点的，如果是，哪个函数输入指定了站点。 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#compilation"},{"categories":["Database"],"collections":null,"content":"Evaluation physically co-locating compute and data, 7–68× and research systems by 2–27× By selectively instrumenting functions using the SFR algorithm, Apiary provides fault tolerance with overhead of \u0026lt;5% compared to 2.2× for a naive solution By instrumenting database operations and functions, Apiary captures information on application-database interactions critical to observability with overhead of \u0026lt;15% as compared to 92% with manual logging ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#evaluation"},{"categories":["Database"],"collections":null,"content":"Experimental Setup ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:1","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#experimental-setup"},{"categories":["Database"],"collections":null,"content":"Baselines Microservice benchmark information 以后可以用到 为什么 Apiary 的延迟要比 rpc 更优秀一点呢？ 个人认为就是少了一层通信。。直接访问数据库的意思？ ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:2","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#baselines"},{"categories":["Database"],"collections":null,"content":"Microservice Workloads Shop。这个基准测试改编自 Google Cloud 的演示[19]，模拟了一个服务，用户在其中浏览在线商店，更新购物车，并结账商品。 Hotel。这个基准测试来自 DeathStarBench[18]，模拟了搜索和预订酒店房间。我们的实现包含一个多函数事务，类似于图 6，其中验证和预订是事务性执行的。 Retwis。这个基准测试来自 Redis[38] ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:3","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#microservice-workloads"},{"categories":["Database"],"collections":null,"content":"End-to-End Benchmarks Apiary 优于 RPC 服务器基准的原因是减少了通信开销：因为它将服务编译为在数据库服务器中运行的存储过程，所以执行数据库操作所需的往返次数更少 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:4","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#end-to-end-benchmarks"},{"categories":["Database"],"collections":null,"content":"Comparing with Boki and Cloudburst Retwis 是读密集型的，因此为了评估写操作的性能影响，我们使用了一个微基准测试，该测试检索并递增与键关联的计数器。 没看过这两篇 但是 apiary 的性能好到离谱，认为 cloudburst 用 py 实现都本地缓存慢，voltdb 读更快，大量读的情况更好 ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:5","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#comparing-with-boki-and-cloudburst"},{"categories":["Database"],"collections":null,"content":"Fault-Tolerant Workflows Performance Analysis ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:6","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#fault-tolerant-workflows-performance-analysis"},{"categories":["Database"],"collections":null,"content":"Enhancing Observability with Apiary 其实按理来说，我觉得 dbos/apiary 更出彩的地方是 tracing 和 fault tolerance 但论文却没大量讲，整个结构也不是很完善，没第一次看 dbos 那么有意思，比如说 time travel, OpenTelemetry traces ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:7","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#enhancing-observability-with-apiary"},{"categories":["Database"],"collections":null,"content":"Cost Analysis ","date":"2024-11-16","objectID":"/posts/paper-apiary/:9:8","tags":["Paper Reading"],"title":"Paper Reading: Apiary: A DBMS-Integrated Transactional Function-as-a-Service Framework","uri":"/posts/paper-apiary/#cost-analysis"},{"categories":["Database"],"collections":null,"content":"Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores https://github.com/delta-io/delta data lake? delta lake? data warehouse? databricks 的论文 DeltaLake 类似的产品有 Hudi, Iceberg, Apache Paimon 其他论文笔记 https://www.cnblogs.com/Aitozi/p/17552466.html 大数据技术换代也太快了，但底层原理我还是一问三不知，还得练 Towards Multi-Table Transactions in Delta Lake Delta 4.0 尝试解决多表事务的问题，感觉有点像 2PC https://www.databricks.com/dataaisummit/session/towards-multi-table-transactions-delta-lake ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#delta-lake-high-performance-acid-table-storage-over-cloud-object-stores"},{"categories":["Database"],"collections":null,"content":"Abstract 云对象存储（如 Amazon S3）是地球上最大且最具成本效益的存储系统之一 然而，它们作为键值存储的实现方式使得实现 ACID 事务和高性能变得困难：元数据操作（如列出对象）成本高昂，一致性保证有限。 在本文中，我们介绍了 Delta Lake，这是一个开源的 ACID 表存储层，最初由 Databricks 开发，用于云对象存储。Delta Lake 使用一个事务日志，该日志被压缩为 Apache Parquet 格式，以提供 ACID 属性、时间旅行，以及对大型表格数据集（例如，能够快速搜索与查询相关的数十亿个表分区）的元数据操作的显著加速。 它还利用这一设计提供高级功能，如自动数据布局优化、更新插入、缓存和审计日志。Delta Lake 表可以从 Apache Spark、Hive、Presto、Redshift 和其他系统访问。 data lake 提出比较早，而 delta lake 支持 ACID ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#abstract"},{"categories":["Database"],"collections":null,"content":"INTRODUCTION 因此，许多组织现在使用 use cloud object stores to manage large structured datasets in data warehouses and data lakes。主要的开源“大数据”系统，包括 Apache Spark、Hive 和 Presto，支持使用 Apache Parquet 和 ORC [13, 12]等文件格式读写云对象存储。商业服务，包括 AWS Athena、Google BigQuery 和 Redshift Spectrum，也可以直接查询这些系统和这些开放文件格式。 不幸的是，尽管许多系统支持读写云对象存储，但在这些系统上实现高性能和可变的表存储是具有挑战性的，这使得在这些系统上实现数据仓库功能变得困难。与 HDFS [5]等分布式文件系统或 DBMS 中的自定义存储引擎不同，大多数云对象存储仅仅是键值存储，没有跨键一致性保证。它们的性能特性也与分布式文件系统大不相同，需要特别注意。 云对象存储和分布式文件系统有什么区别？ 云对象存储底层可以是 kv 也可以是别的，S3 应该是没公开底层是什么 etcd, dynamoDB, redis 就典型的分布式键值存储，一般通过上层提供 ACID hdfs, ceph 是典型的分布式文件系统， 在云对象存储中存储关系数据集的最常见方式是使用 Parquet 和 ORC 等列式文件格式，，其中每个表存储为一组对象（Parquet 或 ORC“文件”），可能按某些字段（例如，每个日期的一组单独对象）聚类为“分区”。只要对象文件适度大，这种方法可以为扫描工作负载提供可接受的性能。然而，它为更复杂的工作负载带来了正确性和性能挑战。首先，由于多对象更新不是原子的，查询之间没有隔离：例如，如果一个查询需要更新表中的多个对象（例如，删除所有 Parquet 文件中关于一个用户的记录），读者将在查询逐个更新每个对象时看到部分更新。回滚写操作也很困难：如果更新查询崩溃，表将处于损坏状态。其次，对于包含数百万个对象的大型表，元数据操作成本高昂。例如，Parquet 文件包含带有最小/最大统计信息的页脚，可用于在选择性查询中跳过读取它们。在 HDFS 上读取这样的页脚可能只需几毫秒，但云对象存储的延迟要高得多，以至于这些数据跳过检查可能比实际查询花费更长时间。 Parquet 列存 根据我们与云客户合作的经验，这些一致性和性能问题为企业数据团队带来了重大挑战。大多数企业数据集是持续更新的，因此它们需要原子写入的解决方案；大多数关于用户的数据集需要表范围的更新来实施隐私政策，如 GDPR 合规 [27]；即使是纯粹的内部数据集也可能需要更新来修复错误数据、合并延迟记录等。据传闻，在 Databricks 云服务（2014-2016 年）的最初几年，我们收到的约一半支持升级是由于云存储策略导致的数据损坏、一致性或性能问题（例如，撤销崩溃的更新作业的影响，或提高读取数万个对象的查询的性能）。 为了解决这些挑战，我们设计了 Delta Lake，这是一个在云对象存储上的 ACID 表存储层 Delta Lake 的核心思想很简单：**我们使用一个预写日志（write-ahead log）以 ACID 方式维护关于哪些对象是 Delta 表一部分的信息，**该日志本身存储在云对象存储中。对象本身以 Parquet 编码，使得从已经能够处理 Parquet 的引擎编写连接器变得容易。这种设计允许客户端以序列化方式一次性更新多个对象，用另一组对象替换子集等，同时仍然从对象本身实现高并行读写性能（类似于原始 Parquet）。日志还包含每个数据文件的最小/最大统计信息等元数据，使得元数据搜索比“对象存储中的文件”方法快一个数量级。至关重要的是，我们将 Delta Lake 设计为所有元数据都在底层对象存储中，并使用针对对象存储的乐观并发协议实现事务（细节因云提供商而异）。这意味着不需要运行服务器来维护 Delta 表的状态；用户仅在运行查询时需要启动服务器，并享受独立扩展计算和存储的好处 基于这种事务性设计，我们还在 Delta Lake 中添加了多个其他功能，这些功能在传统云数据湖中不可用，以解决常见的客户痛点，包括： Time travel to let users query point-in-time snapshots or roll back erroneous updates to their data. UPSERT, DELETE and MERGE operations, which efficiently rewrite the relevant objects to implement updates to archived data and compliance workflows (e.g., for GDPR [27]). Efficient streaming I/O, by letting streaming jobs write small objects into the table at low latency, then transactionally coalescing them into larger objects later for performance. Fast “tailing” reads of the new data added to a table are also supported, so that jobs can treat a Delta table as a message bus. Caching: Because the objects in a Delta table and its log are immutable, cluster nodes can safely cache them on local storage. We leverage this in the Databricks cloud service to implement a transparent SSD cache for Delta tables. Data layout optimization: Our cloud service includes a feature that automatically optimizes the size of objects in a table and the clustering of data records (e.g., storing records in Zorder to achieve locality along multiple dimensions) without impacting running queries. Schema evolution, allowing Delta to continue reading old Parquet files without rewriting them if a table’s schema changes. Audit logging based on the transaction log 这些功能共同提高了在云对象存储中处理数据的易管理性和性能，并实现了“湖仓”范式，结合了数据仓库和数据湖的关键特性：标准 DBMS 管理功能可直接用于低成本对象存储。 事实上，我们发现许多 Databricks 客户可以通过 Delta Lake 简化其整体数据架构，用提供适当功能的 Delta 表替换之前独立的数据湖、数据仓库和流存储系统。 图 1 展示了一个极端例子，一个包括对象存储、消息队列和两个数据仓库的数据管道（每个业务智能团队运行自己的计算资源）被替换为仅在对象存储上的 Delta 表，使用 Delta 的流式 I/O 和性能特性运行 ETL 和 BI。新管道仅使用低成本对象存储，并创建较少的数据副本，从而降低了存储成本和维护开销。 开源 Delta Lake 项目 [26] 包括与 Apache Spark（批处理或流式）、Hive、Presto、AWS Athena、Redshift 和 Snowflake 的连接器，并且可以在多个云对象存储上或 HDFS 上运行。在本文的其余部分，我们将介绍 Delta Lake 的动机和设计，以及推动我们设计的客户用例和性能实验。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#introduction"},{"categories":["Database"],"collections":null,"content":"MOTIVATION: CHARACTERISTICS AND CHALLENGES OF OBJECT STORES ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#motivation-characteristics-and-challenges-of-object-stores"},{"categories":["Database"],"collections":null,"content":"Object Store APIs 云对象存储，如 Amazon S3 [4]、Azure Blob Storage [17]、Google Cloud Storage [30] 和 OpenStack Swift [38]，提供了一个简单但易于扩展的键值存储接口。这些系统允许用户创建存储多个对象的桶，每个对象是一个大小可达几 TB 的二进制大对象（例如，在 S3 上，对象大小的限制是 5TB [4]） 不幸的是，这些元数据 API 通常是昂贵的：例如，S3 的 LIST 每次调用最多返回 1000 个键，每次调用需要几十到几百毫秒的时间，因此使用顺序实现列出包含数百万个对象的数据集可能需要几分钟的时间。 在读取对象时，云对象存储通常支持字节范围请求，因此可以高效地读取大对象中的某个范围 一些云供应商还在 blob 存储之上实现了分布式文件系统接口，例如 Azure 的 ADLS Gen2 [18]，它提供了与 Hadoop 的 HDFS 类似的语义（例如，目录和原子重命名）。尽管如此，Delta Lake 解决的许多问题，如小文件 [36] 和跨多个目录的原子更新，即使在分布式文件系统上仍然存在——实际上，多个用户在 HDFS 上运行 Delta Lake。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#object-store-apis"},{"categories":["Database"],"collections":null,"content":"Consistency Properties 最流行的云对象存储为每个键提供最终一致性 eventual consistency 更新不是立刻看到 具体的一致性模型因云提供商而异，可能相当复杂。作为一个具体的例子，Amazon S3 为写入新对象的客户端提供了 read-after-write consistency，这意味着像 S3 的 GET 这样的读取操作将在 PUT 之后返回对象内容。 negative caching 是什么 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#consistency-properties"},{"categories":["Database"],"collections":null,"content":"Performance Characteristics 在使用对象存储时实现高吞吐量需要仔细平衡大块顺序 I/O 和并行性 对于读取操作，最细粒度的操作是读取顺序字节范围，如前所述。每个读取操作通常会产生至少 5-10 毫秒的基础延迟，并且随后可以以大约 50-100 MB/s 的速度读取数据，因此一个操作需要读取至少几百 KB 的数据才能达到顺序读取峰值吞吐量的一半，并且需要读取几 MB 的数据才能接近峰值吞吐量。此外，在典型的虚拟机配置中，应用程序需要并行运行多个读取操作以最大化吞吐量。例如，AWS 上最常用于分析的虚拟机类型至少具有 10 Gbps 的网络带宽，因此它们需要并行运行 8-10 个读取操作以充分利用这一带宽。 LIST 操作也需要显著的并行性来快速列出大量对象。在 Delta Lake 中，可用对象的元数据（包括它们的名称和数据统计信息）存储在 Delta 日志中，但我们也在集群上并行读取此日志 写操作通常必须替换整个对象（或追加到对象中），如第 2.1 节所述。这意味着如果一个表预计会接收点更新，那么其中的对象应该保持较小，这与支持大读取操作相矛盾。或者，可以使用 log-structured storage format. Implications for Table Storage Keep frequently accessed data close-by sequentially, which generally leads to choosing columnar formats. Make objects large, but not too large. Large objects increase the cost of updating data (e.g., deleting all data about one user) because they must be fully rewritten. Avoid LIST operations, and make these operations request lexicographic key ranges when possible. ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#performance-characteristics"},{"categories":["Database"],"collections":null,"content":"Existing Approaches for Table Storage Directories of Files 这种方法很有吸引力，因为表格“只是一堆对象”，可以从许多工具中访问，而无需运行任何额外的数据存储或系统。它起源于 HDFS 上的 Apache Hive [45]，并与在文件系统上使用 Parquet、Hive 和其他大数据软件相匹配。 这种方法的挑战。如引言中所述，“只是一堆文件”的方法在云对象存储上存在性能和一致性问题。客户遇到的最常见的挑战包括： 跨多个对象的原子性缺失：任何需要写入或更新多个对象的事务都可能面临部分写入对其他客户端可见的风险。此外，如果此类事务失败，数据将处于损坏状态。 最终一致性：即使在成功的事务中，客户端也可能看到一些更新的对象，而看不到其他对象。 性能不佳：列出对象以找到与查询相关的对象是昂贵的，即使它们按键分区到目录中。此外，访问存储在 Parquet 或 ORC 文件中的每个对象的统计信息也很昂贵，因为它需要为每个特征进行额外的高延迟读取。 缺乏管理功能：对象存储不实现数据仓库中常见的标准实用程序，如表格版本控制或审计日志。 Custom Storage Engines 例如 Snowflake 数据仓库，可以通过在单独的强一致性服务中自行管理元数据来绕过云对象存储的许多一致性挑战，该服务持有关于哪些对象构成表格的 \u0026ldquo;source of truth\u0026rdquo; 在这些引擎中，云对象存储可以被视为一个 dumb block device，并且可以使用标准技术在云对象上实现高效的元数据存储、搜索、更新等。然而，这种方法需要运行一个高度可用的服务来管理元数据，这可能很昂贵，在使用外部计算引擎查询数据时会增加开销，并且可能将用户锁定在一个提供商上。 这种方法的挑战： 所有对表格的 I/O 操作都需要联系 metadata 服务，这会增加其资源成本并降低性能和可用性。例如，当使用 Spark 访问 Snowflake 数据集时，从 Snowflake 的 Spark 连接器读取数据会通过 Snowflake 的服务流式传输数据，与直接从云对象存储读取相比，性能有所降低。 连接到现有计算引擎需要更多的工程工作来实现，而不是重用现有的开放格式（如 Parquet）。根据我们的经验，数据团队希望在其数据上使用广泛的计算引擎（例如 Spark、TensorFlow、PyTorch 等），因此使连接器易于实现非常重要。 专有的元数据服务将用户绑定到特定的服务提供商，而基于直接访问云存储中的对象的方法使用户能够始终使用不同的技术访问其数据。 所以现在都用 open formats? iceberg 是新的技术趋势吗 Apache Hive ACID [32] 通过使用 Hive Metastore（一个事务性 RDBMS，如 MySQL）来跟踪存储在 ORC 格式中的表格的多个更新文件，在 HDFS 或对象存储上实现了类似的方法。然而，这种方法受到元数据存储性能的限制，根据我们的经验，对于包含数百万对象的表格，元数据存储可能成为瓶颈。 metastore metadata 这里不太理解，这些数据的 metadata 为什么要分开存呢 Metadata in Object Stores. Delta Lake 的方法是将事务日志和元数据直接存储在云对象存储中，并使用一组对象存储操作协议来实现序列化。 表格中的数据随后以 Parquet 格式存储，使得只要有一个最小的连接器来发现要读取的对象集，就可以从任何已经支持 Parquet 的软件中轻松访问。尽管我们认为 Delta Lake 是第一个使用这种设计的系统（始于 2016 年），但另外两个软件包现在也支持它——Apache Hudi [8] 和 Apache Iceberg [10]。Delta Lake 提供了这些系统不支持的许多独特功能，例如 Z-order 聚类、缓存和后台优化。我们将在第 8 节中更详细地讨论这些系统之间的相似性和差异。 Iceberg, Hudi 等技术目前工业界应该是很有前景的 Deltalake 的特色是什么？ ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:4:4","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#existing-approaches-for-table-storage"},{"categories":["Database"],"collections":null,"content":"DELTA LAKE STORAGE FORMAT AND ACCESS PROTOCOLS Delta Lake 表格是云对象存储或文件系统上的一个目录，其中包含表格内容的文件对象和事务操作的日志 (with occasional checkpoints) 客户端使用我们为云对象存储特性定制的乐观并发控制协议来更新这些数据结构。在本节中，我们将描述 Delta Lake 的存储格式和这些访问协议。我们还将描述 Delta Lake 的事务隔离级别，包括表格内的序列化和快照隔离。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#delta-lake-storage-format-and-access-protocols"},{"categories":["Database"],"collections":null,"content":"Storage Format 图 2 展示了 Delta 表格的存储格式。每个表格都存储在文件系统目录（此处为 mytable）中，或者作为对象存储中以相同“目录”键前缀开头的对象。 Data Objects 表格内容存储在 Apache Parquet 对象中 选择 Parquet 作为底层数据格式，因为它面向列，提供多种压缩更新，支持半结构化数据的嵌套数据类型，并且在许多引擎中已经有高性能的实现。基于现有的开放文件格式也确保了 Delta Lake 可以继续利用 Parquet 库的新发布更新，并简化了开发与其他引擎的连接器（第 4.8 节）。其他开源格式，如 ORC [12]，可能也能以类似的方式工作，但 Parquet 在 Spark 中拥有最成熟的支持。 Delta 中的每个数据对象都有一个唯一的名称，通常由写入者通过生成 GUID 来选择。然而，哪些对象是表格每个版本的组成部分由事务日志决定。 Log 日志存储在表格内的_delta_log 子目录中。它包含一系列带有递增的零填充数字 ID 的 JSON 对象，用于存储日志记录，以及偶尔的检查点，用于特定日志对象，以 Parquet 格式总结到该点的日志。正如我们在第 3.2 节中讨论的那样，一些简单的访问协议（取决于每个对象存储中可用的原子操作）用于创建新的日志条目或检查点，并使客户端就事务顺序达成一致。 每个日志记录对象（例如，000003.json）包含一个操作数组，这些操作应用于表格的前一个版本以生成下一个版本。可用的操作包括： Change Metadata. Add or Remove Files. Protocol Evolution. Add Provenance Information. Update Application Transaction IDs. Log Checkpoints 为了性能，有必要定期将日志压缩成检查点。检查点以 Parquet 格式存储表格日志中到某个日志记录 ID 的所有非冗余操作。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#storage-format"},{"categories":["Database"],"collections":null,"content":"Storage Format 图 2 展示了 Delta 表格的存储格式。每个表格都存储在文件系统目录（此处为 mytable）中，或者作为对象存储中以相同“目录”键前缀开头的对象。 Data Objects 表格内容存储在 Apache Parquet 对象中 选择 Parquet 作为底层数据格式，因为它面向列，提供多种压缩更新，支持半结构化数据的嵌套数据类型，并且在许多引擎中已经有高性能的实现。基于现有的开放文件格式也确保了 Delta Lake 可以继续利用 Parquet 库的新发布更新，并简化了开发与其他引擎的连接器（第 4.8 节）。其他开源格式，如 ORC [12]，可能也能以类似的方式工作，但 Parquet 在 Spark 中拥有最成熟的支持。 Delta 中的每个数据对象都有一个唯一的名称，通常由写入者通过生成 GUID 来选择。然而，哪些对象是表格每个版本的组成部分由事务日志决定。 Log 日志存储在表格内的_delta_log 子目录中。它包含一系列带有递增的零填充数字 ID 的 JSON 对象，用于存储日志记录，以及偶尔的检查点，用于特定日志对象，以 Parquet 格式总结到该点的日志。正如我们在第 3.2 节中讨论的那样，一些简单的访问协议（取决于每个对象存储中可用的原子操作）用于创建新的日志条目或检查点，并使客户端就事务顺序达成一致。 每个日志记录对象（例如，000003.json）包含一个操作数组，这些操作应用于表格的前一个版本以生成下一个版本。可用的操作包括： Change Metadata. Add or Remove Files. Protocol Evolution. Add Provenance Information. Update Application Transaction IDs. Log Checkpoints 为了性能，有必要定期将日志压缩成检查点。检查点以 Parquet 格式存储表格日志中到某个日志记录 ID 的所有非冗余操作。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#data-objects"},{"categories":["Database"],"collections":null,"content":"Storage Format 图 2 展示了 Delta 表格的存储格式。每个表格都存储在文件系统目录（此处为 mytable）中，或者作为对象存储中以相同“目录”键前缀开头的对象。 Data Objects 表格内容存储在 Apache Parquet 对象中 选择 Parquet 作为底层数据格式，因为它面向列，提供多种压缩更新，支持半结构化数据的嵌套数据类型，并且在许多引擎中已经有高性能的实现。基于现有的开放文件格式也确保了 Delta Lake 可以继续利用 Parquet 库的新发布更新，并简化了开发与其他引擎的连接器（第 4.8 节）。其他开源格式，如 ORC [12]，可能也能以类似的方式工作，但 Parquet 在 Spark 中拥有最成熟的支持。 Delta 中的每个数据对象都有一个唯一的名称，通常由写入者通过生成 GUID 来选择。然而，哪些对象是表格每个版本的组成部分由事务日志决定。 Log 日志存储在表格内的_delta_log 子目录中。它包含一系列带有递增的零填充数字 ID 的 JSON 对象，用于存储日志记录，以及偶尔的检查点，用于特定日志对象，以 Parquet 格式总结到该点的日志。正如我们在第 3.2 节中讨论的那样，一些简单的访问协议（取决于每个对象存储中可用的原子操作）用于创建新的日志条目或检查点，并使客户端就事务顺序达成一致。 每个日志记录对象（例如，000003.json）包含一个操作数组，这些操作应用于表格的前一个版本以生成下一个版本。可用的操作包括： Change Metadata. Add or Remove Files. Protocol Evolution. Add Provenance Information. Update Application Transaction IDs. Log Checkpoints 为了性能，有必要定期将日志压缩成检查点。检查点以 Parquet 格式存储表格日志中到某个日志记录 ID 的所有非冗余操作。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#log"},{"categories":["Database"],"collections":null,"content":"Storage Format 图 2 展示了 Delta 表格的存储格式。每个表格都存储在文件系统目录（此处为 mytable）中，或者作为对象存储中以相同“目录”键前缀开头的对象。 Data Objects 表格内容存储在 Apache Parquet 对象中 选择 Parquet 作为底层数据格式，因为它面向列，提供多种压缩更新，支持半结构化数据的嵌套数据类型，并且在许多引擎中已经有高性能的实现。基于现有的开放文件格式也确保了 Delta Lake 可以继续利用 Parquet 库的新发布更新，并简化了开发与其他引擎的连接器（第 4.8 节）。其他开源格式，如 ORC [12]，可能也能以类似的方式工作，但 Parquet 在 Spark 中拥有最成熟的支持。 Delta 中的每个数据对象都有一个唯一的名称，通常由写入者通过生成 GUID 来选择。然而，哪些对象是表格每个版本的组成部分由事务日志决定。 Log 日志存储在表格内的_delta_log 子目录中。它包含一系列带有递增的零填充数字 ID 的 JSON 对象，用于存储日志记录，以及偶尔的检查点，用于特定日志对象，以 Parquet 格式总结到该点的日志。正如我们在第 3.2 节中讨论的那样，一些简单的访问协议（取决于每个对象存储中可用的原子操作）用于创建新的日志条目或检查点，并使客户端就事务顺序达成一致。 每个日志记录对象（例如，000003.json）包含一个操作数组，这些操作应用于表格的前一个版本以生成下一个版本。可用的操作包括： Change Metadata. Add or Remove Files. Protocol Evolution. Add Provenance Information. Update Application Transaction IDs. Log Checkpoints 为了性能，有必要定期将日志压缩成检查点。检查点以 Parquet 格式存储表格日志中到某个日志记录 ID 的所有非冗余操作。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#log-checkpoints"},{"categories":["Database"],"collections":null,"content":"Access Protocols Delta Lake 的访问协议旨在让客户端仅使用对象存储上的操作实现序列化事务，尽管对象存储具有最终一致性保证。 Reading from Tables 只读事务 读取表格日志目录中的 _last_checkpoint 对象（如果存在），以获取最近的检查点 ID。 使用 LIST 操作，其起始键是最后一个检查点 ID（如果存在），否则为 0，以查找表格日志目录中任何更新的 .json 和 .parquet 文件。 使用上一步中标识的检查点（如果存在）和后续日志记录重建表格的状态——即，具有添加记录但没有相应删除记录的数据对象集及其关联的数据统计信息。 使用统计信息识别与读取查询相关的数据对象文件集。 查询对象存储以读取相关的数据对象，可能在集群中并行进行 我们注意到，此协议设计为在每一步都 tolerate eventual consistency 第一步读到旧的，第二步可能拿到新的？ Write Transactions 写入数据的事务通常会根据事务中的操作分为最多五个步骤： 省略细节了，这篇论文图太少了，很不清晰 请注意，第五步，即写入检查点然后更新 _last_checkpoint 对象，仅影响性能，客户端在此步骤中的任何地方失败都不会损坏数据。例如，如果客户端未能写入检查点，或者写入了检查点 Parquet 对象但没有更新 _last_checkpoint ，那么其他客户端仍然可以使用较早的检查点读取表格。如果步骤 4 成功，事务将原子提交。 Adding Log Records Atomically 步骤 4，即创建 r + 1 的.json 日志记录对象，需要是原子的：只有一个客户端应该成功创建具有该名称的对象。不幸的是，并非所有大规模存储系统都具有原子的“不存在则放置”操作，但我们能够为不同的存储系统以不同的方式实现此步骤： 不同 cloud provider 不同实现 AWS S3, Azure Blob, google cloud storage ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#access-protocols"},{"categories":["Database"],"collections":null,"content":"Access Protocols Delta Lake 的访问协议旨在让客户端仅使用对象存储上的操作实现序列化事务，尽管对象存储具有最终一致性保证。 Reading from Tables 只读事务 读取表格日志目录中的 _last_checkpoint 对象（如果存在），以获取最近的检查点 ID。 使用 LIST 操作，其起始键是最后一个检查点 ID（如果存在），否则为 0，以查找表格日志目录中任何更新的 .json 和 .parquet 文件。 使用上一步中标识的检查点（如果存在）和后续日志记录重建表格的状态——即，具有添加记录但没有相应删除记录的数据对象集及其关联的数据统计信息。 使用统计信息识别与读取查询相关的数据对象文件集。 查询对象存储以读取相关的数据对象，可能在集群中并行进行 我们注意到，此协议设计为在每一步都 tolerate eventual consistency 第一步读到旧的，第二步可能拿到新的？ Write Transactions 写入数据的事务通常会根据事务中的操作分为最多五个步骤： 省略细节了，这篇论文图太少了，很不清晰 请注意，第五步，即写入检查点然后更新 _last_checkpoint 对象，仅影响性能，客户端在此步骤中的任何地方失败都不会损坏数据。例如，如果客户端未能写入检查点，或者写入了检查点 Parquet 对象但没有更新 _last_checkpoint ，那么其他客户端仍然可以使用较早的检查点读取表格。如果步骤 4 成功，事务将原子提交。 Adding Log Records Atomically 步骤 4，即创建 r + 1 的.json 日志记录对象，需要是原子的：只有一个客户端应该成功创建具有该名称的对象。不幸的是，并非所有大规模存储系统都具有原子的“不存在则放置”操作，但我们能够为不同的存储系统以不同的方式实现此步骤： 不同 cloud provider 不同实现 AWS S3, Azure Blob, google cloud storage ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#reading-from-tables"},{"categories":["Database"],"collections":null,"content":"Access Protocols Delta Lake 的访问协议旨在让客户端仅使用对象存储上的操作实现序列化事务，尽管对象存储具有最终一致性保证。 Reading from Tables 只读事务 读取表格日志目录中的 _last_checkpoint 对象（如果存在），以获取最近的检查点 ID。 使用 LIST 操作，其起始键是最后一个检查点 ID（如果存在），否则为 0，以查找表格日志目录中任何更新的 .json 和 .parquet 文件。 使用上一步中标识的检查点（如果存在）和后续日志记录重建表格的状态——即，具有添加记录但没有相应删除记录的数据对象集及其关联的数据统计信息。 使用统计信息识别与读取查询相关的数据对象文件集。 查询对象存储以读取相关的数据对象，可能在集群中并行进行 我们注意到，此协议设计为在每一步都 tolerate eventual consistency 第一步读到旧的，第二步可能拿到新的？ Write Transactions 写入数据的事务通常会根据事务中的操作分为最多五个步骤： 省略细节了，这篇论文图太少了，很不清晰 请注意，第五步，即写入检查点然后更新 _last_checkpoint 对象，仅影响性能，客户端在此步骤中的任何地方失败都不会损坏数据。例如，如果客户端未能写入检查点，或者写入了检查点 Parquet 对象但没有更新 _last_checkpoint ，那么其他客户端仍然可以使用较早的检查点读取表格。如果步骤 4 成功，事务将原子提交。 Adding Log Records Atomically 步骤 4，即创建 r + 1 的.json 日志记录对象，需要是原子的：只有一个客户端应该成功创建具有该名称的对象。不幸的是，并非所有大规模存储系统都具有原子的“不存在则放置”操作，但我们能够为不同的存储系统以不同的方式实现此步骤： 不同 cloud provider 不同实现 AWS S3, Azure Blob, google cloud storage ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#write-transactions"},{"categories":["Database"],"collections":null,"content":"Available Isolation Levels Given Delta Lake’s concurrency control protocols, all transactions that perform writes are serializable, leading to a serial schedule in increasing order of log record IDs 这是由于写入事务的提交协议，其中只有一个事务可以写入每个记录 ID 的记录。 读取事务可以实现快照隔离或序列化。 snapshot isolation 快照隔离 写的事务隔离级别很高，是序列化 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#available-isolation-levels"},{"categories":["Database"],"collections":null,"content":"Transaction Rates Delta Lake 的写入事务速率受限于写入新日志记录的“不存在则放置”操作的延迟，如第 3.2.2 节所述。与任何乐观并发控制协议一样，高写入事务速率将导致提交失败。 我们相信一个自定义的 LogStore，类似于我们的 S3 提交服务，可以通过协调对日志的访问来提供显著更快的提交时间（例如，通过在低延迟 DBMS 中持久化日志的末尾，并异步将其写入对象存储）。当然，快照隔离级别的读取事务不会产生争用，因为它们只读取对象存储中的对象，因此可以同时运行任意数量的这些事务。 概念太多了这里， 首先，在提交时，如果多个事务尝试更新同一资源，就会发生冲突。由于每个事务都试图写入相同的日志记录位置，这些冲突会导致某些事务提交失败，需要回滚和重试。 LogSotre 用于管理事务日志记录。它负责日志的持久化和存取，确保写入操作的原子性和一致性。 如果需要更高的写入速率，可以通过定制的 LogStore 进行优化，如在低延迟数据库中持久化日志结尾，并异步写入对象存储。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#transaction-rates"},{"categories":["Database"],"collections":null,"content":"HIGHER-LEVEL FEATURES IN DELTA ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#higher-level-features-in-delta"},{"categories":["Database"],"collections":null,"content":"Time Travel and Rollbacks 由于 Delta Lake 的数据对象和日志是不可变的，Delta Lake 使得查询数据的过去快照变得简单，就像典型的 MVCC 实现一样。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#time-travel-and-rollbacks"},{"categories":["Database"],"collections":null,"content":"Efficient UPSERT, DELETE andMERGE 在传统的数据湖存储格式中，例如 S3 上的 Parquet 文件目录，很难在不停止并发读取器的情况下执行这些更新。即使如此，更新作业也必须小心执行，因为作业期间的失败将使表格处于部分更新的状态。使用 Delta Lake，所有这些操作都可以事务性地执行，通过 Delta 日志中的新添加和删除记录替换任何更新的对象。Delta Lake 支持标准的 SQL UPSERT、DELETE 和 MERGE 语法。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#efficient-upsert-delete-andmerge"},{"categories":["Database"],"collections":null,"content":"Streaming Ingest and Consumption 许多数据团队希望部署流式管道以实时 ETL 或聚合数据，但传统的云数据湖难以用于此目的。 Write Compaction. Exactly-Once Streaming Writes. Efficient Log Tailing. 这部分需要再仔细看看原文 https://www.vldb.org/pvldb/vol13/p3411-armbrust.pdf ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#streaming-ingest-and-consumption"},{"categories":["Database"],"collections":null,"content":"Data Layout Optimization OPTIMIZE Command。用户可以手动对表格运行 OPTIMIZE 命令，该命令在不影响正在进行的事务的情况下压缩小对象，并计算任何缺失的统计信息。默认情况下，此操作旨在使每个数据对象的大小为 1 GB，我们发现此值适用于许多工作负载，但用户可以自定义此值。 Z-Ordering by Multiple Attributes AUTO OPTIMIZE. ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#data-layout-optimization"},{"categories":["Database"],"collections":null,"content":"Caching 缓存是安全的，因为 Delta Lake 表格中的数据、日志和检查点对象是不可变的。正如我们在第 6 节所示，从缓存中读取可以显著提高查询性能。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:5","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#caching"},{"categories":["Database"],"collections":null,"content":"Audit Logging 略 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:6","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#audit-logging"},{"categories":["Database"],"collections":null,"content":"Schema Evolution and Enforcement Delta Lake 可以事务性地执行模式更改，并在需要时更新底层对象（例如，删除用户不再希望保留的列）。 Schema Evolution + 事务，应该比较安全？ ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:7","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#schema-evolution-and-enforcement"},{"categories":["Database"],"collections":null,"content":"Connectors to Query and ETL Engines Delta Lake 通过 Apache Spark 的数据源 API [16] 提供了对 Spark SQL 和结构化流的全功能连接器。此外，它目前还提供了对多个其他系统的只读集成：Apache Hive、Presto、AWS Athena、AWS Redshift 和 Snowflake，使用户可以使用熟悉的工具查询 Delta 表格，并将它们与这些系统中的数据进行连接 数据存储层 Iceberg/Hudi/Delta Lake 然后接入查询层 是否是一种存算分离？ ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:6:8","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#connectors-to-query-and-etl-engines"},{"categories":["Database"],"collections":null,"content":"DELTA LAKE USE CASES 在这些使用案例中，我们发现客户经常使用 Delta Lake 来简化其企业数据架构，通过直接在云对象存储上运行更多工作负载，并创建一个兼具数据湖和事务功能的“湖仓”系统。例如，考虑一个典型的数据管道，它从多个来源加载记录——例如，来自 OLTP 数据库的 CDC 日志和来自设施的传感器数据——然后通过 ETL 步骤传递，以使派生表格可用于数据仓库和数据科学工作负载（如图 1 所示）。传统的实现需要结合消息队列（如 Apache Kafka [11]）来计算需要实时计算的任何结果，数据湖用于长期存储，以及数据仓库（如 Redshift [3]）用于需要利用索引和快速节点附加存储设备（例如 SSD）进行快速分析查询的用户。这需要多个数据副本，并持续将数据导入每个系统。通过 Delta Lake，可以根据工作负载将其中几个存储系统替换为对象存储表格，利用 ACID 事务、流式 I/O 和 SSD 缓存等功能来恢复每个专用系统中的一些性能优化。尽管 Delta Lake 显然不能替代我们列出的所有系统功能，但我们发现，在许多情况下，它至少可以替代其中一些。Delta 的连接器（§4.8）还支持从许多现有引擎查询它。 为什么就不需要消息队列了呢？因为 ACID 可以做流批？ 一般 delta lake 有高吞吐和低延迟的保证吗 而且本身就当作 data warehouse 了，比如 redshift, bigquery, snowflake 等等 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#delta-lake-use-cases"},{"categories":["Database"],"collections":null,"content":"Data Engineering and ETL augmenting traditional enterprise data sources (e.g., pointof-sale events in OLTP system) ML workloads ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#data-engineering-and-etl"},{"categories":["Database"],"collections":null,"content":"Data Warehousing and BI business intelligence The key technical features to support these workloads are usually efficient storage formats (e.g. columnar formats), data access optimizations such as clustering and indexing, fast storage hardware, and a suitably optimized query engine ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#data-warehousing-and-bi"},{"categories":["Database"],"collections":null,"content":"Compliance and Reproducibility MLflow 是 Databricks 开发的开源模型管理平台，可以自动记录用于训练 ML 模型的数据集版本，并让开发人员重新加载它。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:7:3","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#compliance-and-reproducibility"},{"categories":["Database"],"collections":null,"content":"Specialized Use Cases 记录了广泛的计算机系统事件，例如网络上的 TCP 和 UDP 流、身份验证请求、SSH 登录等，并将其记录到一组跨度达数 PB 的 Delta Lake 表格中。多个程序化 ETL、SQL、图分析和机器学习作业随后在这些表格上运行，以搜索已知模式，这些模式表明存在入侵（例如，来自用户的可疑登录事件，或一组服务器导出大量数据）。其中许多是流式作业，以最小化检测问题的时间。此外，超过 100 名分析师直接查询源和派生的 Delta Lake 表格，以调查可疑警报或设计新的自动化监控作业。 因此该组织使用 Delta Lake 的 ZORDER BY 功能重新排列 Parquet 对象中的记录，以提供跨多个维度的聚类。 尽管传统的生物信息学工具使用了自定义数据格式（如 SAM、BAM 和 VCF [34, 24]），但许多组织现在将这些数据存储在数据湖格式（如 Parquet）中。Big Data Genomics 项目 [37] 开创了这种方法。Delta Lake 通过启用快速的多维查询（通过 Z-ordering）、ACID 事务和高效的 UPSERT 和 MERGE，进一步增强了生物信息学工作负载。 使用 Delta Lake 管理多媒体数据集，例如上传到网站的一组图像 ML 这里到底怎么做的，图片二进制编码，怎么变成 parquet ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:7:4","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#specialized-use-cases"},{"categories":["Database"],"collections":null,"content":"PERFORMANCE EXPERIMENTS 研究了（1）具有大量对象或分区的表格对开源大数据系统的影响，这促使 Delta Lake 决定将元数据和统计信息集中到检查点中，以及（2）Z-ordering 对来自大型 Delta Lake 使用案例的选择性查询工作负载的影响。我们还展示了 Delta 在 TPC-DS 上的查询性能优于 Parquet，并且对写入工作负载没有显著的开销。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#performance-experiments"},{"categories":["Database"],"collections":null,"content":"Impact of Many Objects or Partitions Delta Lake 的许多设计决策源于云对象存储中列出和读取对象的高延迟 小文件在 HDFS [36] 中也是一个问题，但在云存储中的性能影响更严重。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#impact-of-many-objects-or-partitions"},{"categories":["Database"],"collections":null,"content":"Impact of Z-Ordering Z-Ordering 是一种用于多维数据的空间填充曲线优化技术，旨在提高数据读取性能，特别是在大数据环境中。它通过将数据按多维空间中的 Z 形顺序重新排序，最大限度地提高数据的局部性，从而提高查询效率。Z-Ordering 在处理具有多列过滤条件的查询时特别有效 多维查询：当查询包含多个过滤条件（如 age 和 income）时，Z-Ordering 可以显著提高查询性能。 大数据处理：在处理大量数据时，通过 Z-Ordering 可以减少 I/O 操作，提高数据读取效率。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#impact-of-z-ordering"},{"categories":["Database"],"collections":null,"content":"TPC-DS Performance 为了评估 Delta Lake 在标准 DBMS 基准测试中的端到端性能，我们在 Databricks Runtime（我们的 Apache Spark 实现）上使用 Delta Lake 和 Parquet 文件格式，以及在流行云服务中的 Spark 和 Presto 实现上运行了 TPC-DS 功率测试 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:8:3","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#tpc-ds-performance"},{"categories":["Database"],"collections":null,"content":"Write Performance ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:8:4","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#write-performance"},{"categories":["Database"],"collections":null,"content":"DISCUSSION AND LIMITATIONS 我们在 Delta Lake 上的经验表明，许多企业数据处理工作负载可以在云对象存储上实现 ACID 事务，并且它们可以支持大规模的流式、批处理和交互式工作负载。Delta Lake 的设计特别有吸引力，因为它不需要任何其他重量级系统来调解对云存储的访问，使其易于部署，并且可以直接从支持 Parquet 的广泛查询引擎访问。Delta Lake 对 ACID 的支持然后启用了其他强大的性能和管理功能。 尽管如此，Delta Lake 的设计和当前实现有一些限制，这些限制是未来工作的有趣途径。首先，Delta Lake 目前仅在单个表格内提供序列化事务，因为每个表格都有自己的事务日志。跨多个表格共享事务日志将消除这一限制，但可能会增加通过乐观并发追加日志记录的争用。对于非常高的事务量，协调器也可以在不成为数据对象的读写路径一部分的情况下调解对日志的写访问。 其次，对于流式工作负载，Delta Lake 受限于底层云对象存储的延迟。例如，使用对象存储操作很难实现毫秒级的流式延迟。然而，我们发现，对于希望运行并行作业的大规模企业工作负载，使用 Delta Lake 表格的秒级延迟是可以接受的。 第三，Delta Lake 目前不支持二级索引（除了每个数据对象的最小/最大统计信息），但我们已经开始原型化基于 Bloom 过滤器的索引。Delta 的 ACID 事务允许我们与基础数据的更改一起事务性地更新此类索引。 ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#discussion-and-limitations"},{"categories":["Database"],"collections":null,"content":"RELATED WORK ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#related-work"},{"categories":["Database"],"collections":null,"content":"CONCLUSION ","date":"2024-11-11","objectID":"/posts/paper-delta-lake/:11:0","tags":["Paper Reading"],"title":"Paper Reading: Delta Lake High-Performance ACID Table Storage over Cloud Object Stores","uri":"/posts/paper-delta-lake/#conclusion"},{"categories":["Distributed"],"collections":null,"content":"Ownership: A Distributed Futures System for Fine-Grained Tasks 分布式系统中的一些任务调度的论文，本来是要看 Ray 的，同样都是 UCB 的研究（分布式机器学习框架） Ownership 我一开始还以为是 Rust 里的所有权概念，弄混了，论文也提到许多 Futures, distributed futures interface 之类的第一次接触，实际上是 RPC 的扩展 最重要的概念是 Distributed Memory + Ref + Future 不再需要经典的 RPC 值传递， A call worker1 得到 o1 就返回引用而不是 o1 A call worker2 得到 o2 返回引用 A call worker2 o1+o2，worker2 解引用去 w1 找 o1 future 就是异步，但是 解引用的时候就会有很多问题，所以有了这篇论文，但看下来感觉主题不太清晰，效果也不太优秀，不太令人信服。 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#ownership-a-distributed-futures-system-for-fine-grained-tasks"},{"categories":["Distributed"],"collections":null,"content":"Abstract 分布式 Futures 接口 distributed futures interface Distributed futures are an extension of RPC that combines futures and distributed memory 分布式 futures 是一个引用，其最终值可能存储在远程节点上。 然后，应用程序可以在不指定执行时间或数据移动位置的情况下表达分布式计算。 最近的分布式 Futures 应用程序需要执行细粒度计算的能力，即在毫秒级运行的任务。与粗粒度任务相比，细粒度任务难以以可接受的系统开销执行。 在本文中，我们提出了一种用于细粒度任务的分布式 Futures 系统，该系统在不牺牲性能的情况下提供容错能力。我们的解决方案基于一种称为 Ownership 的全新概念，该概念为每个对象分配一个系统操作的leader。我们表明，这种去中心化的架构可以实现水平扩展、每任务 1 毫秒的延迟和快速故障处理。 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#abstract"},{"categories":["Distributed"],"collections":null,"content":"Introduction RPC 是构建分布式应用程序的标准，最初的提案使用同步调用 synchronous，将返回值复制回调用者 最近的一些系统 [4, 34, 37, 45] 扩展了 RPC，使得系统不仅可以管理分布式通信，还可以代表应用程序管理数据移动和并行性。 PyTorch - Remote Reference Protocol. Ray 这些分布式训练框架使用 rpc，A 节点调用 B 节点的方法，传入参数，返回结果是一个远程引用 rref，可以通过移到本地而不是返回值 Data movement: 按值传递语义要求所有 RPC 参数通过直接复制到请求体中发送给执行者 为了减少数据复制，一些 RPC 系统使用分布式内存，这允许通过引用传递大参数，而小参数仍然可以通过值传递。在最理想的情况下，如果参数已经位于执行者所在的同一节点上，则通过引用传递的 RPC 参数不需要复制。请注意，与传统的 RPC 一样，我们使所有值不可变，以简化一致性模型和实现。 值不可以变？类似函数式编程？缺点是内存使用增加 Parallelism: 传统的 RPC 是阻塞的，因此只有在收到回复后才会将控制权返回给调用者（图 2a）。Futures（Futures）是一种流行的方法，用于通过异步性扩展 RPC [8, 29]，允许系统在彼此之间以及与调用者并行执行函数。通过组合 [29, 37]，即将 Futures 作为参数传递给另一个 RPC，应用程序还可以表达 Futures RPC 的并行性和依赖关系。例如，在图 2c 中，add 在程序开始时被调用，但只有在计算出 a 和 b 之后，系统才会执行。 Distributed futures: RPC 的扩展，结合了 Futures 和分布式内存：分布式 Futures 是一个引用，其最终值可能存储在远程节点上（图 2d）。然后，应用程序可以在不指定执行时间或数据移动位置的情况下表达分布式计算。这对于开发处理大量数据的分布式应用程序来说是一个越来越受欢迎的接口。 这个 Future 太迷惑了，我总把 Rust 里的 Future 异步编程弄混 可能这种异步计算都可以叫 Future / Promises Futures are a popular method for extending RPC with asynchrony 不幸的是，现有的分布式 Futures 系统仅限于粗粒度任务 在本文中，我们提出了一种用于细粒度任务的分布式 Futures 系统。虽然其他人 [34, 37, 45] 之前已经实现了分布式 Futures，但我们的贡献在于识别并解决了在不牺牲性能的情况下为细粒度任务提供容错能力的挑战。 34 Ray, CIEL, Dask 这些都实现了分布式 Future 主要挑战是分布式 Futures 在进程之间引入了共享状态。特别是，一个对象及其元数据由其引用持有者、创建对象的 RPC 执行者以及其物理位置共享。为了确保每个引用持有者能够解引用值，进程必须协调 coordinate，这是一个在存在故障时难以解决的问题。相比之下，传统的 RPC 没有共享状态，因为数据是通过值传递的，并且自然避免了协调，这对于可扩展性和低延迟至关重要。 例如，在图 2a 中，一旦 worker 1 将 a 复制到 driver，它 (worker1) 就不需要参与下游 add 任务的执行。相比之下，worker 1 在图 2d 中存储了 a，因此两个 worker 必须协调以确保 a 在 worker 2 读取时足够长时间可用。此外，worker 1 必须在 worker 2 执行 add 并且没有其他引用时对 a 进行垃圾回收。最后，进程必须协调以检测和恢复另一个进程的故障。 以前系统中的常见解决方案是使用集中式主节点来存储系统状态并协调这些操作，确保容错的一种简单方法是将与操作相关的元数据同步记录和复制到主节点。 Ray 使用了主节点 Head Node https://docs.ray.io/en/latest/cluster/key-concepts.html 只要保证 a 不会在调用 add 前被垃圾回收？ray 用 head node 来任务调度、资源管理 因此，分散系统状态对于可扩展性是必要的。问题是如何在不复杂化协调的情况下做到这一点。我们工作的关键见解是利用应用程序结构：分布式 Futures 可以通过引用传递共享，但大多数分布式 Futures 在调用者的范围内共享。例如，在图 1 中，a_future 被创建然后传递给同一范围内的 add。 因此，我们提出了所有权，一种在 RPC executors 之间分散系统状态的方法。特别是，任务的调用者是返回的 Futures 及其相关元数据的拥有者。在图 2d 中，driver 拥有 a、b 和 c。 这个解决方案有三个优点。首先，对于 horizontal scaling，应用程序可以使用嵌套任务将系统状态“分片”到 worker 上。 其次，由于 Futures 的拥有者是任务的调用者，任务延迟较低，因为所需的元数据写入虽然是同步的，但却是本地的。这与一致性哈希等应用程序无关的分片方法形成对比。第三，每个 worker 实际上成为了它所拥有的分布式 Futures 的集中式主节点，简化了故障处理。 水平扩展就是加机器 和一致性哈希有什么区别？写入元数据写本地，与其他节点无关？而一致性哈希需要所有节点都知道？ 系统保证如果 Futures 的拥有者存活，任何持有该 Futures 引用的任务最终都可以解引用该值。这是因为拥有者将协调系统操作，如引用计数（用于内存安全）和 lineage reconstruction（用于恢复）。当然，如果拥有者失败，这还不够。 在这里，我们依赖于 lineage reconstruction 和对应用程序结构的第二个关键见解：在许多情况下，对分布式未来的引用由失败拥有者的后代任务持有。失败的任务可以通过其拥有者的血统重建重新创建，后代任务也将在此过程中重新创建。因此，与 futures 的拥有者共享任何持有分布式未来引用的任务是安全的。由于我们预计故障相对较少，我们认为这种减少系统开销和复杂性的好处超过了故障时额外重新执行的成本。 总之，我们的贡献是： 一种具有 transparent recovery 和 automatic memory management 分布式未来的去中心化系统。 一种基于 lineage reconstruction and fate sharing 的轻量级透明恢复技术。 在 Ray 系统 [34] 中的实现，提供了高吞吐量、低延迟和快速恢复。 分布式数据库应该比较常用 checkpoint + log 来做故障恢复把？ ","date":"2024-11-10","objectID":"/posts/paper-ownership/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#introduction"},{"categories":["Distributed"],"collections":null,"content":"Distributed Futures ","date":"2024-11-10","objectID":"/posts/paper-ownership/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#distributed-futures"},{"categories":["Distributed"],"collections":null,"content":"API 分布式未来的主要优势在于系统可以 transparently manage parallelism 和 data movement f 是调用函数，传入 x 引用 get(x) 是解引用，del(x) 删除引用 Actor.f(x) 带状态调用 f shared(x) 返回 shared 状态可以传 x 到其他 worker，而不用解引用 f(shared x) 传送 x 当作 first-class ，会解引用 x Distributed Futures 缩写 DFut To spawn a task, 调用者调用一个远程函数，该函数立即返回一个 DFut（表 1）。启动的任务包括函数及其参数、资源需求等。返回的 DFut 引用由函数返回的对象的值。调用者可以通过 get 解引用 DFut，这是一个阻塞调用，返回对象的副本。调用者可以删除 DFut，将其从作用域中移除，并允许系统回收该值。与其他系统 [34, 37, 45] 一样，所有对象都是不可变的。 通过任务调用创建 DFut 后，调用者可以通过两种方式创建其他引用。首先，调用者可以将 DFut 作为参数传递给另一个任务。DFut 任务参数由系统隐式解引用。因此，任务只有在所有上游任务完成后才会开始，执行者只能看到 DFut 的值。 其次，DFut 可以作为 first-class 传递或返回 [21]，即在不解引用的情况下传递给另一个任务。表 1 展示了如何将 DFut 转换为 SharedDFut，以便系统可以区分何时解引用参数。我们将接收 DFut 的进程称为借用者，以区别于原始调用者。与原始调用者一样，借用者可以通过传递 DFut 或再次转换为 SharedDFut（创建更多的借用者）来创建其他引用。 与最近的系统 [4, 34, 45] 类似，我们支持带有 actor 的有状态计算。调用者通过调用远程构造函数来创建一个 actor。这立即返回对 actor 的引用（ARef），并在远程进程上异步执行构造函数。ARef 可以用于启动绑定到同一进程的任务。与 DFut 类似，ARef 是一等值，即调用者可以将 ARef 返回或传递给另一个任务，系统会在所有 ARef 超出作用域后自动收集 actor 进程。 Actor 管理状态计算 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#api"},{"categories":["Distributed"],"collections":null,"content":"Applications 分布式未来的典型应用是那些需要 RPC 的灵活性以及数据移动和并行性优化的应用。 分布式未来之前已被探索用于数据密集型应用，这些应用无法有效地表达或执行为数据并行程序。Ciel 识别了在执行过程中动态指定任务的关键能力，例如基于先前的结果，而不是预先指定整个图 [37]。这使得新的工作负载成为可能，例如动态规划，它本质上具有递归性 任务调度？ Model serving: 图 3a 展示了一个基于 GPU 的图像分类管道的示例。每个客户端将其输入图像传递给一个 Preprocess 任务，例如调整大小，然后将返回的 DFut 与一个 Router actor 共享。Router 实现了调度策略，并通过引用将 DFut 传递给选定的 Model actor。然后，Router 将结果返回给客户端。 actor 通过两种方式提高性能：(1) 每个 Model 在其本地 GPU 内存中保持权重预热 keeps weights warm，(2) Router 缓冲预处理的 DFut，直到它有一批请求传递给 Model，以利用 GPU 并行性提高吞吐量。通过动态任务，Router 还可以选择在超时时刷新其缓冲区，以减少批处理带来的延迟。 First-class distributed futures 对于减少路由开销非常重要。它们允许 Router 将预处理图像的引用传递给 Model actor，而不是复制这些图像。这避免了在 Router 处形成瓶颈，我们在图 15a 中对此进行了评估。虽然应用程序可以使用中间存储系统来存储预处理图像，但它必须管理额外的关注点，如垃圾回收和故障。 Online video processing: 视频处理算法通常具有复杂的数据依赖关系，这些依赖关系不受 Apache Spark 等数据并行系统的良好支持 [22, 43]。例如，视频稳定（图 3b）通过跟踪帧之间的对象（Flow），对这些轨迹进行累积求和（CumSum），然后应用移动平均（Smooth）来工作。帧与帧之间的依赖关系很常见，例如图 3b 中存储在 actor 中的视频解码状态。每个阶段每帧运行 1-10 毫秒。 Spark 为什么不行？是 GPU 限制还是并行关系？ 论文 Scanner: Efficient video analysis at scale, Lightdb: A DBMS for virtual reality video. 在这种设置中，安全和及时的垃圾回收可能具有挑战性，因为单个对象（例如视频帧）可能被多个任务引用。实时视频处理也对延迟敏感：输出必须以与输入相同的帧速率生成。低延迟依赖于帧之间的流水线并行性，因为应用程序无法等待多个输入帧出现才开始执行 DFut 的错误检测和错误恢复 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#applications"},{"categories":["Distributed"],"collections":null,"content":"Overview ","date":"2024-11-10","objectID":"/posts/paper-ownership/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#overview"},{"categories":["Distributed"],"collections":null,"content":"Requirements 系统保证每个 DFut 都可以解引用到其值。这涉及三个问题：automatic memory management, failure detection, and failure recovery. 就是把值传递改成了引用传递 可以减少网络传递的大小，但是带来的问题却很多，生命周期，错误检测，错误恢复 Automatic memory management: 引用计数？ Failure detection: 系统检测到 DFut 由于 worker 故障而无法解引用的时间。在没有未来但有分布式内存的情况下，这是直接的，因为在创建引用时已知值的位置。 未来的加入使故障检测变得复杂，因为可以在值之前创建引用。 因此，在图 4b 中，当 worker 2 接收到 add RPC 时，可能没有 a 的位置。然后，worker 2 必须决定 f 是否仍在执行，或者是否已经失败。如果是前者，那么 worker 2 应该等待。但如果发生故障，系统必须恢复 a。为了解决这个问题，系统必须记录所有任务的位置，即待处理对象，而不仅仅是已创建的对象。 异步 Failure recovery: 系统还必须提供一种从失败的 DFut 中恢复的方法。最低要求是如果应用程序尝试解引用失败的 DFut，则抛出错误。我们进一步提供透明恢复的选项，即系统将恢复失败的 DFut 的值。 在没有分布式内存但有 Future 的情况下，如果一个进程失败，我们将失去该进程上任何待处理任务的回复。假设幂等性，这可以通过重试来恢复，这是按值传递 RPC 的常见方法。例如，在图 5a 中，driver 通过重新提交 add(a, b) 来恢复。故障恢复很简单，因为所有数据都是按值传递的。 然而，在分布式内存的情况下，任务还可以包含通过引用传递的参数。因此，节点故障可能导致仍然被引用的对象值丢失，如图 4b 中的 b。解决这个问题的一个常见方法是记录每个对象的 lineage，即在运行时生成对象的子图 [17, 30, 56]。然后，系统遍历丢失对象的血统，并通过任务重新执行递归地重建对象及其依赖关系。这种方法减少了日志记录的运行时开销，因为数据本身没有记录，并且部分失败后必须重做的工作，因为分布式内存中缓存的对象不需要重新计算。尽管如此，实现低运行时开销仍然很困难，因为血统本身必须在运行时记录和收集，并且必须能够承受故障。 请注意，我们特别关注 object recovery，并且与之前的系统 [34, 37, 56] 一样，假设幂等性以确保正确性。因此，我们的技术直接适用于幂等函数和具有只读、 checkpointable 或瞬态状态的 actor，正如我们在图 15c 中评估的那样。虽然这不是我们的重点，但这些技术也可以与已知的 actor 状态恢复技术结合使用 [17, 34]，例如 nondeterministic execution 的恢复 [52]。 Metadata requirements: 在正常操作期间，系统必须至少记录以下内容：(1) 每个对象值的位置，以便引用持有者可以检索它，(2) 对象是否仍然被引用，以确保安全的垃圾回收。对于故障检测和恢复，系统还必须分别记录 (3) 每个 pending 对象的位置，即任务位置，以及 (4) 对象血统。 关键问题是何时以及在哪里记录这些系统元数据，以确保其一致性和容错性。 在某些情况下，元数据的异步更新是安全的，即系统元数据与系统状态之间存在暂时不一致 另一方面，故障处理所需的元数据理想情况下应同步更新。 所以到底是异步还是同步更新？ ","date":"2024-11-10","objectID":"/posts/paper-ownership/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#requirements"},{"categories":["Distributed"],"collections":null,"content":"Existing solutions Centralized master: 使用同步更新的集中式主节点进行故障处理，但这种设计也会增加显著的运行时开销，故障检测要求主节点在调度之前记录任务的计划位置，这使得主节点成为可扩展性和延迟的瓶颈。主节点可以通过分片来提高可扩展性，但这会使协调多个对象的操作（如垃圾回收和血统重建）变得复杂。此外，延迟开销是根本性的。每个任务调用必须首先联系主节点，即使不复制元数据以实现容错，也会在执行的关键路径上增加至少一次 RTT。 Distributed leases: 这类似于异步更新的集中式主节点，这种设计通过分片实现水平可扩展性，并减少任务延迟，因为元数据是异步写入的。然而，依赖时间来协调系统状态会减慢恢复速度（图 14）。此外，这种去中心化方法引入了一个新问题：工作节点还必须在谁应该恢复对象（即重新执行创建任务）上进行协调。这在集中式方案中很简单，因为主节点协调所有恢复操作。 在 Kubernetes 中，租约机制用于节点心跳和组件级别的领导者选举 分布式原理介绍 https://lrita.github.io/images/posts/distribution/%E5%88%86%E5%B8%83%E5%BC%8F%E5%8E%9F%E7%90%86%E4%BB%8B%E7%BB%8D.pdf 分布式系统中的 lease（租约）机制 https://lrita.github.io/2018/10/29/lease-in-distributed-system/ 基于 PAXSO 算法的，时钟漂移有上界的部分同步网络的 lease(租约)算法 https://lrita.github.io/images/posts/distribution/flease-fault-tolerant-and-decentralized-lease-coordination-for-distributed-systems.pdf 这里提到的等待租约过期，那我将租约寿命调低，不断续约能否解决呢？ 至于谁来恢复，用主节点协调其实挺好的？这也是为什么 Ray 继续用 head node 吧 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#existing-solutions"},{"categories":["Distributed"],"collections":null,"content":"Our solution: Ownership 我们工作的关键见解是“shard”集中式主节点以提高可扩展性，但基于应用程序结构来实现低运行时开销和简单的故障处理。 在所有权中，调用任务的工作节点存储与返回的 DFut 相关的元数据。与集中式主节点一样，它协调操作，如任务调度，以确保它知道任务位置，并进行垃圾回收。例如，在图 6d 中，worker 1 拥有 X 和 Y。 选择任务的调用者作为所有者的原因是，通常情况下，它是访问元数据最频繁的工作节点。调用者通过任务调用参与 DFut 的初始创建，并通过将 DFut 传递给其他 RPC 来创建其他引用。因此，任务调用延迟最小，因为计划位置是本地写入的。类似地，如果 DFut 保持在所有者的作用域内，垃圾回收的开销很低，因为当所有者将 DFut 传递给另一个 RPC 时，可以在本地更新 DFut 的引用计数。对于小对象，这些开销可以进一步减少，这些对象可以按值传递，就像没有分布式内存一样（参见第 4.2 节）。 当然，如果所有任务都由单个驱动程序提交，就像在 BSP 程序中那样，所有权机制将无法扩展到驱动程序的吞吐量之外。任何动态任务系统也是如此。然而，通过所有权机制，应用程序可以通过将控制逻辑分布在多个嵌套任务中来水平扩展，而不是像一致性哈希（图 12e）这样的应用程序无关的方法。此外，worker 进程持有系统元数据的大部分。这与之前的解决方案形成对比，之前的解决方案将所有元数据推送到系统的集中式或每个节点的进程中，限制了单个节点在拥有许多 worker 进程时的垂直可扩展性（图 12）。 单个节点，single failure？ 然而，有些问题是假设性能足够的情况下，使用完全集中式设计更容易解决的： First-class futures 第一类未来（第 2 节）允许非所有者进程引用 DFut。虽然许多应用程序可以在没有第一类未来的情况下编写（图 3b），但它们在某些情况下对性能至关重要。例如，图 3a 中的模型服务应用程序使用第一类未来将任务调用委托给嵌套任务，而无需解引用和复制参数。第一类 DFut 可能会离开所有者的范围，因此我们必须考虑在垃圾回收期间的情况。我们避免在所有者处集中引用计数，因为这将违背委托的目的。相反，我们使用分布式分层引用计数协议（第 4.2 节）。每个借用者代表所有者存储 DFut 的本地引用计数（表 2），并在本地引用计数达到零时通知所有者。所有者决定何时可以安全地回收对象。我们使用引用计数方法而不是追踪 [42] 来避免全局暂停。 Owner recovery: 如果一个 worker 失败，那么我们也会丢失其拥有的元数据。为了实现透明恢复，系统必须在新进程上恢复 worker 的状态，并重新关联与之前拥有的 DFut 相关的状态，包括任何值的副本、引用持有者和挂起的任务。我们选择了一种最小化的方法，保证进度，但可能会在失败时增加额外的重新执行成本：我们与所有者共享对象和任何引用持有者的命运，然后使用行踪重建来恢复对象和所有者的任何命运共享的子任务（第 4.3 节）。这种方法增加了最小的运行时开销，并且是正确的，即应用程序将恢复到之前的状态，并且系统保证不会发生资源泄漏。未来的扩展是持久化所有者的状态，以最小化恢复时间，但代价是增加恢复复杂性和运行时开销 Ray 文档说目前不支持 owner recovery 但为什么论文这里说集中式设计容易解决呢 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#our-solution-ownership"},{"categories":["Distributed"],"collections":null,"content":"Ownership Design 集群中的每个节点托管一个或多个 worker（通常每个核心一个），一个调度器和一个对象存储 worker 使用一个所有权表来记录它所处理的任务和数据 The distributed memory layer (Section 4.2) consists of an immutable distributed object store (Figure 7d) with Locations stored at the owner 具体细节看论文 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#ownership-design"},{"categories":["Distributed"],"collections":null,"content":"Task scheduling Owner 如何协调任务调度 在较高层次上，所有者将每个任务分派到分布式调度器选择的位置。这确保了所有权表中的任务位置与分派同步更新。我们假设一个抽象的调度策略，该策略接收资源请求并返回应分配资源节点的 ID。该策略还可能更新其决策，例如由于资源可用性的变化。 图 8c 展示了分派任务的协议。在任务调用时，调用者，即返回的 DFut 的所有者，首先从其本地调度器请求资源。请求是一个元组，包含任务所需的资源（例如，{\u0026ldquo;CPU\u0026rdquo;: 1}）和分布式内存中的参数。如果策略选择本地节点，调度器接受请求：它获取参数，分配资源，然后将本地 worker 租给所有者。否则，调度器拒绝请求并将所有者重定向到策略选择的节点。 在这两种情况下，调度器都会向所有者返回新位置：要么是租用的 worker 的 ID，要么是另一个节点的 ID。所有者在将任务分派到该位置之前，将其存储在其本地所有权表中。如果请求被接受，所有者直接将任务发送给租用的 worker 执行；否则，它在下一个调度器重复该协议。 因此，所有者总是将任务分派到其下一个位置，确保任务的挂起位置（表 2）同步更新。这也允许所有者在任务的资源需求得到满足时，通过直接将任务分派给已租用的 worker 来绕过调度器。例如，在图 8d 中，worker 1 重用了从节点 2 租用的资源来执行 C。所有者在配置的超时时间后或没有更多任务需要分派时返回租约。我们目前不重用具有不同分布式内存依赖关系的任务的资源，因为这些资源由调度器获取。我们将其他租约撤销和工作重用的策略留待未来的工作。 工作重用到底是什么意思 任务执行前的最坏情况 RTT 数量高于之前的解决方案，因为每个策略决策都返回给所有者（图 8e）。然而，之前解决方案的吞吐量受到限制（图 12），因为它们无法支持直接的 worker-to-worker 调度（图 8d）。这是因为 worker 不存储系统状态，因此所有任务必须通过主节点或每个节点的调度器来更新任务位置（图 8a 和 8b）。 这里很奇怪，说吞吐量是由于 worker2worker 调度限制了，那对于 lease 呢？ Actor scheduling 系统调度 actor 构造任务的方式与普通任务类似。然而，在完成之后，所有者持有 worker 的租约，直到 actor 不再被引用（第 4.2 节），并且 worker 只能执行通过相应 ARef 提交的 actor 任务。 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#task-scheduling"},{"categories":["Distributed"],"collections":null,"content":"Memory management Allocation 对于小对象，复制可能比通过分布式内存传递更快，后者需要更新对象目录，从远程节点获取对象等。因此，在对象创建时，系统根据大小透明地选择是按值传递还是按引用传递。 Dereferencing Reclamation Actors ","date":"2024-11-10","objectID":"/posts/paper-ownership/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#memory-management"},{"categories":["Distributed"],"collections":null,"content":"Failure recovery Failure detection. 故障通知包含 worker 或节点 ID，发布给所有 worker。Worker 不交换心跳；worker 故障由其本地调度器发布。节点故障通过节点之间交换心跳来检测，所有 worker 与其节点 fate-share。 在接收到节点或 worker 故障通知后，每个 worker 扫描其本地所有权表以检测 DFut 故障。DFut 在两种情况下被认为是失败的：1）通过比较 Location 字段，丢失拥有的对象（图 10a），或 2）通过比较 Owner 字段，丢失所有者（图 11a）。我们接下来分别使用行踪重建和命运共享来讨论这两种情况的处理 请注意，非所有者不需要检测对象的丢失。例如，在图 10a 中，节点 2 在 worker 3 接收到 C 时失败。当 worker 3 在所有者处查找 X 时，可能找不到任何位置。从 worker 3 的角度来看，这意味着节点 2 的目录写入被延迟，或者节点 2 失败。Worker 3 不需要决定是哪一种；它只需等待 X 的所有者处理故障。 Object recovery: The owner recovers a lost value through lineage reconstruction Owner recovery. 所有者故障可能导致“dangling pointer”：无法解引用的 DFut。如果对象同时从分布式内存中丢失，就会发生这种情况。例如，如果节点 2 也失败，图 11a 中的 C 将会挂起。 我们使用命运共享来确保系统在所有者失败时能够继续运行。首先，所有者及其任何引用持有者持有的所有资源都被回收。具体来说，在接收到所有者失败的通知后，分布式对象存储释放对象（如果存在）或调度层回收 worker 租约（如果对象正在等待），如图 11b 所示。所有引用持有者，即借用者和依赖任务，也与所有者共享命运 然后，为了恢复共享命运的状态，我们依赖于行踪重建。特别是，在失败的所有者上执行的任务或 actor 本身必须由另一个进程拥有。该进程最终将重新提交失败的任务。当新的所有者重新执行时，它将重新创建其先前的状态，无需系统干预。例如，图 11a 中 A 的所有者最终将重新提交 A（图 11b），这将再次提交 B 和 C。 为了正确性，我们证明所有先前的引用持有者都被重新创建，并带有新所有者的地址。考虑计算 DFut x 值的任务 T。T 最初在 worker W 上执行，并在恢复期间在 W0 上重新执行。API（第 2 节）提供了三种创建对 x 的另一个引用的方法：（1）将 x 作为任务参数传递，（2）将 x 转换为 SharedDFut 然后作为任务参数传递，（3）从 T 返回 x。 ","date":"2024-11-10","objectID":"/posts/paper-ownership/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#failure-recovery"},{"categories":["Distributed"],"collections":null,"content":"Evaluation 我们与三个基线进行了比较：（1）一个没有分布式内存的未来但按值传递的模型，类似于图 2c，（2）一个基于租约的去中心化系统用于分布式未来（Ray v0.7），（3）一个集中式主节点用于分布式未来（Ray v0.7 修改为在任务执行前写入集中式主节点）。 Task submission is divided across multiple intermediate drivers, either colocated on the m5.8xlarge head node or spread with one m5.8xlarge node per driver ","date":"2024-11-10","objectID":"/posts/paper-ownership/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#evaluation"},{"categories":["Distributed"],"collections":null,"content":"Microbenchmarks Throughput and scalability. We could not produce stable results for pass-by-value with large objects due to the lack of backpressure in our implementation 当驱动程序分散时（图 12b 和 12d），所有权和租约都线性扩展。在图 12b 中，所有权比租约扩展得更好，因为更多的工作被卸载到 worker 进程上。在图 12d 中，所有权和租约实现了类似的吞吐量，但所有权系统还包括内存安全（第 4.2 节）。集中式设计（2 个分片）线性扩展到 ∼60 个节点。添加更多分片会提高这个阈值，但只是常数数量。 是不是有点假这里的 small object 测试，pass by value 肯定不需要分布式内存，ownership 又调优小对象，另两个呢？ 大对象 colocated 为什么 ownership 好一点 spread 多个 driver 其实应该都差不多我觉得，因为分散了任务都是并行？这里和 lease 性能其实很接近 Scaling through borrowing Latency 该 worker 要么与驱动程序位于同一节点（“本地”），要么位于单独的 m5.16xlarge 节点上（“远程”） 首先，分布式内存在所有情况下都比按值传递实现了更好的延迟，因为这些系统避免了从驱动程序到 worker 的不必要任务参数副本。 其次，与集中式和租约相比，所有权平均实现了 1.6 倍的更低延迟。这是因为（1）能够在所有者本地写入元数据，而不是远程进程，以及（2）能够重用租用的资源，在许多情况下绕过调度层（第 4.1 节）。 如果任务的资源需求得到满足，所有者还可以绕过调度器，直接将任务分派给已租用的工人。 in Figure 8d, worker 1 reuses the resources leased from node 2 in Figure 8c to execute C. Recovery ","date":"2024-11-10","objectID":"/posts/paper-ownership/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#microbenchmarks"},{"categories":["Distributed"],"collections":null,"content":"End-to-end applications Model Serving 所有权和集中式实现了相同的中位数延迟（54ms），但集中式的尾部延迟高出 9 倍（1 秒 vs. 108ms） Online video processing. 图 15b 显示了没有故障的延迟。所有系统实现了相似的中位数延迟（∼65ms），但租约和集中式的尾部延迟较长（分别为 1208ms 和 1923ms）。 基于租约的恢复很慢，因为解码器 actor 必须重放所有任务，并且每个任务从租约到期中累积开销。由于租约实现无法安全地垃圾回收血统，因此检查点 actor 是不可行的。 例如，当资源的所有权需要转移或释放时，租约机制可能无法可靠地追踪这些变化，导致未使用的资源无法被及时回收。 etcd 租约撤销 分布式垃圾回收 DGC application-level checkpoints (O+CP) ","date":"2024-11-10","objectID":"/posts/paper-ownership/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#end-to-end-applications"},{"categories":["Distributed"],"collections":null,"content":"Conclusion 一些问题 median latency 都差不多，但是 ownership 尾部延迟很好，差别在哪？是因为高负载出现了瓶颈？ O+CP(Redis) 是不是没有控制变量啊。。O+CP; WF 呢？ Ray does not support recovery from owner failure. 你觉得是为什么？因为 metadata 丢失了，计数也丢失了，所以也是个瓶颈 fate share 是不是难以实现 Spark / Ray / MapReduce by default is using centralized architecture, master/ head node Ray \u0026lt; v0.8 is using lease manager? Ownership 假设任务都是 DAG / Tree ObjectRef 是有 scope 的可以传递 我觉得 Ownership owner recovery 太假了，所有 owner metadata 都丢了 w3 返回到一个失败节点？然后找到 owner 的 owner 来重建？这不是最坏的情况吗 那么有没有更好的办法呢，比方说不是重建所有，而是同步这个 DAG 里所有的 ownership 表，只重建那一个？ https://docs.google.com/document/d/1tBw9A4j62ruI5omIJbMxly-la5w4q_TjyJgJL_jN2fI/preview?tab=t.0 Some of the trade-offs that come with ownership are: To resolve an ObjectRef, the object’s owner must be reachable. This means that an object will fate-share with its owner. See Object failures and Object spilling for more information about object recovery and persistence. Ownership currently cannot be transferred. (Individual) The first question is: What do you think of Ownership\u0026rsquo;s design? Do you buy it? Or which one do you prefer, centralized head node, distributed lease, or Ownership, why? Personally, I prefer distributed lease design because it\u0026rsquo;s simpler and widely used. For example, Kubernetes uses leases and implements resource management and error tolerance based on leases. Previous versions of Ray utilized centralized head node design and combined with leases. However, since it is difficult to handle garbage collection under lease, and ownership provides better throughput and tail latency because it facilitates worker-to-worker task scheduling and worker reuse. I prefer the Ownership model. (Group Discussion) The purpose of Ownership is to support fault tolerance with distributed memory and distributed future, while also improving throughput and latency. Refer to some memory management papers we read before, software-defined far memory, AIFM, fastswap. Do you think that ownership can be application-aware or resources-aware? like utilizing some data structure from AIFM to allow the owner to release some cold data in advance, so we could reuse the worker? What will the potential problems be? (Group Discussion, optional if time permits) We have learned that many distributed systems utilize logs to achieve fault tolerance. For instance, Delta Lake employs Write-Ahead Logs to support consistency and ACID properties. The paper mentions some future work in section 7, it says that it might support a spectrum of application recovery requirements. For example, we could extend ownership with options to recover actor state. Do you think logs or global object store can be used to implement future like this or even time travel? What metadata do we need? (Whiteboard Question) According to Ray\u0026rsquo;s documentation, Ray does not support Owner recovery and does not support ownership transfer even though the latest version is build on top of Ownership. What do you think is the reason? Personally, I believe it is because losing all ownership record makes it difficult to rebuild lineage reconstruction. If you were to design it, do you think it could be solved through ownership transfer? For example, use the head node design and adding the feature of ownership transfer, when the owner fails, let the head node restart the owner node and transfer the ownership copy from a certain worker node to the owner node, making the node the owner again. There might be some consistency issues here, and this is an open question, you can present your solution as well as what you think might be the issue? ","date":"2024-11-10","objectID":"/posts/paper-ownership/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Ownership: A Distributed Futures System for Fine-Grained Tasks","uri":"/posts/paper-ownership/#conclusion"},{"categories":["OS"],"collections":null,"content":"为什么可用内存会远超物理内存？ 结合一些杂七杂八的文章看看 https://www.cnblogs.com/binlovetech/p/17571929.html 为什么要从操作物理内存转向操作虚拟内存？ 保护内存，每个进程使用的内存独立、隔离 多进程，内存管理，运行更多应用 局部性 ","date":"2024-11-07","objectID":"/posts/memo-class-1/:1:0","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#为什么可用内存会远超物理内存"},{"categories":["OS"],"collections":null,"content":"局部性原理 时间局部性：未来可能再次访问 空间局部性：内存位置可能再次访问 每个进程都独享 128T 的虚拟内存空间 是每个进程吗？为什么是 128T，2^64 所以随意定？内核态呢 为什么要设计那么大 128T 很多都用不完吧 虚拟内存让每个进程都有独立的、私有的内存空间，而且比可用的物理内存要大得多 虚拟内存是只有用的时候才会映射吗，所以可以任意分配？ ","date":"2024-11-07","objectID":"/posts/memo-class-1/:1:1","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#局部性原理"},{"categories":["OS"],"collections":null,"content":"虚拟内存 操作系统管理虚拟内存和物理内存映射，通过页管理 程序 malloc 的时候会发生页分配，但不一定发生映射，直到需要读写的时候才会真正分配内存页面 为什么是 128T 呢，因为 64 位机器，2^64 是很大的，只用了低 48 位也就是 2^48 = 256T，内核态和用户态各一半 至于 32 位机器，2^32 就只有 4G 了，内核态 1G，用户态 3G 虚拟地址空间 而且用户态都是低位到高位，内核从高到低 ","date":"2024-11-07","objectID":"/posts/memo-class-1/:1:2","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#虚拟内存"},{"categories":["OS"],"collections":null,"content":"页表 CPU 通过 MMU 内存管理单元映射虚拟内存到物理内存，通过操作系统设置页表 页表是由 PTE 页表项组成的数组，虚拟空间每个地址每个页都有 PTE 对应 PTE 也有一些 metadata，比如页面读写权限，是否存在的有效位、脏位等等 页表项大小是多少？4 字节？ 这样页表 1024 个页表项也是 4K 占据一个页？页大小也是 4K？ 这样一个页表项对应 4K 页，一个页表就支持 4M 页目录表由 Page Directory Entry PDE 组成，每个 PDE 对应一个页表（开始的物理位置），构成了多级页表结构。 多级页表，64 位处理器上有更多级的页表 ","date":"2024-11-07","objectID":"/posts/memo-class-1/:2:0","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#页表"},{"categories":["OS"],"collections":null,"content":"CPU - 真实地址 给定虚拟地址，如何映射到物理地址？ CPU - 寄存器 (页目录基址寄存器 CR3): 确定页目录地址（最高级页表基地址），在 MMU 找到存储着虚拟地址到物理地址转换页表项 PDE 页目录项：32 位虚拟地址拆成 10, 10, 12 位三段，页目录表及地址可以通过计算找到页目录项 页目录项 - 页表 - 页表项：（省略内部计算细节） 虚拟地址 - 物理地址：页表项记录物理地址 对于 64 位机器，使用了 48 位虚拟地址，有 4 级页表 +---------------------+ | Page Directory Base | 页目录基地址 (Page Directory Base Address) +---------------------+ | v +---------------------+ | Page Directory | 页目录表 (Page Directory Table) +---------------------+ | Page Directory Entry| 页目录项 (Page Directory Entry) +---------------------+ | v +---------------------+ | Page Table | 页表 (Page Table) +---------------------+ | Page Table Entry | 页表项 (Page Table Entry) +---------------------+ | v +---------------------+ | Physical Address | 物理地址 (Physical Address) +---------------------+","date":"2024-11-07","objectID":"/posts/memo-class-1/:3:0","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#cpu---真实地址"},{"categories":["OS"],"collections":null,"content":"页面 SWAP 不经常使用的页面，会被换出内存，存在硬盘 swap 区域，提供给新的虚拟内存 页面调度算法如何实现？ FIFO? 还是 RR? 有什么区别? ","date":"2024-11-07","objectID":"/posts/memo-class-1/:4:0","tags":["System","Memory"],"title":"阅读笔记：编程高手必学的内存知识 01","uri":"/posts/memo-class-1/#页面-swap"},{"categories":["OS"],"collections":null,"content":"AIFM: High-Performance, Application-Integrated Far Memory Disaggregated Memory 领域的论文，之前稍微看了一点，挺有意思的从应用层面用远端内存 Zhenyuan Ruan, Malte Schwarzkopf 和 Adam Belay，zhenyuan 在 osdi 20 中了不少论文，很多都和解耦内存有关，非常有意思。adam 此外还有 caladan 等，太恐怖了。 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:1:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#aifm-high-performance-application-integrated-far-memory"},{"categories":["OS"],"collections":null,"content":"Abstract application-integrated far memory (AIFM) 它通过一个简单的 API 将远程的“远端”内存提供给应用程序，并具有高性能。AIFM 实现了与本地 RAM 相同的常见情况访问延迟；它避免了基于分页的方法所遭受的读写放大；它允许数据结构工程师构建可远程访问的、混合近/远端内存数据结构；并且它使远端内存对应用程序开发者透明且易于使用。 读写放大 将 application-level semantics 暴露给高性能运行时，使得高效的远程内存成为可能。开发者使用 AIFM 的 API 来使分配可远程访问，AIFM 的运行时负责对象的交换进出、预取和内存疏散。 程序语义是什么意思 我们通过一个原型 Web 应用程序前端、一个纽约市出租车数据分析工作负载、一个类似 memcached 的键值缓存和 Snappy 压缩来评估 AIFM。将 AIFM 的远程内存添加到这些应用程序中，增加了它们的可用内存而没有性能损失。AIFM 的性能优于 Fastswap，这是一个最先进的内核集成、基于分页的远端内存系统，性能提升高达 61 倍。 实际上我觉得要对这些应用的类型做个评估，到底是 IO 密集型还是内存密集型 再就是对于云平台内存超卖，远端内存有这方面的讨论吗？ ","date":"2024-11-06","objectID":"/posts/paper-aifm/:2:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction Google [73] 和阿里巴巴 [46] 的服务器平均内存利用率为 60%，服务器之间的差异很大，而平均 CPU 利用率约为 40%。 当今的操作系统主要通过交换机制支持内存弹性，通过将未使用的物理内存页推送到较慢的内存层（如磁盘或远程内存）来释放 RAM。但操作系统的交换机制在固定且粗粒度上操作，并产生大量开销。为了交换一个页面，操作系统必须处理页面错误，这需要进入内核并等待数据到达 此外，Linux 内核在等待交换数据时会旋转，以避免上下文切换和中断处理的开销。这意味着等待时间（使用 Fastswap 的 RDMA 后端大约为 15-20k 个周期）被浪费了。 我们描述了一种根本不同的方法：应用程序集成的远端内存（AIFM），它将交换绑定到单个应用程序级别的内存对象，而不是虚拟内存（VM）页面的抽象。开发者编写可远程访问的数据结构，其支持的内存可以是本地的和“远端”的——即在远程服务器上——而不影响常见情况下的延迟或应用程序吞吐量。当 AIFM 检测到内存压力时，其运行时会交换出对象，并将所有指向该对象的指针转换为远程指针。当应用程序解引用远程指针时，一个轻量级的绿色线程运行时会将对象恢复到本地内存。运行时的低上下文切换成本允许其他绿色线程在等待周期内进行有效利用，这隐藏了远程访问延迟并保持了高吞吐量。由于这些快速的上下文切换，AIFM 在访问 4KB 对象时比基于页面的方法高出 81%的吞吐量，并且由于 AIFM 避免了放大，它在访问小对象时实现了 6.8 倍的高吞吐量（图 1）。 user level 所以还是比较好的？不过还得看看具体实现 至于这里提到的读写放大，应该是说读写小对象但仍然操作 4KB 页面的事情？ AIFM 的编程接口基于四个关键思想：一个快速、低开销的可远程访问指针抽象，一个无暂停的内存 evacuator，允许数据结构向运行时传达语义信息的运行时 API，以及一个帮助将轻量计算卸载到远程内存的远程设备接口。这些 AIFM API 允许数据结构工程师轻松构建混合本地/远程数据结构，并提供类似于 C++ 标准库数据结构的开发者体验。 无暂停的内存疏散器确保应用程序线程永远不会因交换而经历延迟峰值。因为数据结构向运行时传达了它们的语义，AIFM 支持自定义预取和缓存策略——例如，在可远程访问的列表中预取远程数据，并避免污染本地内存缓存的远程数据流。最后，AIFM 的卸载减少了数据移动，并缓解了大多数远端内存系统经历的网络瓶颈。 无暂停是怎么实现的，减少数据移动也就是减少读写放大？ Our prototype is limited to unshared far memory objects on a single memory server. Future work may add multi-server support, devise strategies for dynamic sizing of remote memory, or investigate sharing. 这限制看着很奇怪，所以没有网络通信？ ","date":"2024-11-06","objectID":"/posts/paper-aifm/:3:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#introduction"},{"categories":["OS"],"collections":null,"content":"Background and Related Work 当今操作系统主要通过将物理内存页交换到二级存储来实现内存弹性，比如磁盘 最近的研究考虑将交换到更快的内存层或远端内存，例如主机的远程内存或压缩缓存。由于交换与内核虚拟内存子系统集成，因此对用户空间应用程序是透明的。但这种透明性 transparency 也迫使交换粒度为最小的虚拟内存原语，即 4KB 页面。结合小于 4KB 的内存对象，这导致 I/O 放大：当访问一个对象时，内核必须独立于对象的实际内存大小交换一个完整的 4KB 页面。此外，提供应用程序语义信息，如预期的内存访问模式、适当的预取策略或内存热度，仅限于粗略和不灵活的接口，如 madvise。 透明性是什么意思？系统隐藏了具体原理，直接用？ AIFM 以不同于交换的方式使用远端内存，通过在对象粒度而不是页面粒度上操作——这一想法借鉴了分布式共享内存（见下文）、内存压缩[75]和 SSD 存储[1]的先前工作。这些研究都指出页面级 I/O 放大是一个关键动机。AIFM 使用智能指针和受 C++ 弱指针[69]和 Folly RCU 保护[26]启发的解引用范围，提供对远端内存的透明访问。 分布式共享内存 有意思，这一篇论文还是蛮偏向存储设计的 哈哈 RCU 还是没看 Disaggregated and distributed shared memory: 分解内存[58]指的是一种硬件架构，其中快速结构将主机连接到内存池[29, 33]，这可能由集群范围的操作系统管理[33, 66]。分解内存需要尚未投入生产的新硬件。AIFM 专注于当今硬件的软件解决方案。 分布式共享内存（DSM）通过消息传递提供共享内存的抽象。因为DSM需要一个缓存一致性协议，这会损害性能。 Technologies to access remote data: TCP/IP 是访问远程数据的主导协议，AIFM 目前使用 TCP/IP。TCP/IP 存在更快的替代方案，可以进一步改进 AIFM，但这些技术与 AIFM 的关键思想正交或互补。AIFM 不需要专门的硬件，RDMA 是一种旧技术，最近在以太网上实现了商品化[32]，引起了新的兴趣。许多工作致力于在一般[39, 51, 76]或特定应用程序（如键值存储[38, 49]或数据库系统[11]）中高效使用 RDMA 另一个好奇的点是，既然提升到 user-level，为什么不做更深入的用户网络栈呢 但 AIFM 到底用不用 RDMA？ Abstractions for remote data: 远程过程调用（RPC）广泛用于访问远程数据，包括通过 RDMA[19, 71]或 TCP/IP[37]。而远程数据的数据结构库[4, 15]为开发者提供了映射、集合、多集合、列表和其他熟悉的构造。这与持久内存的数据结构库[59, 62]的精神相似。AIFM 提供了一个更低级别的服务，帮助程序员开发这些数据结构。 I/O amplification: Garbage collection and memory evacuation: 在 AIFM 中将对象移动到远程内存（“疏散”）与托管语言中的标记-压缩垃圾收集（GC）密切相关。AIFM 旨在通过将冷但活跃的对象 cold, but live objects移动到远程内存来增加内存容量，而 GC 则专注于释放死对象的内存 AIFM没有发明新的疏散算法，而是从GC文献中借鉴了思想，并将其适应于远端内存系统。与GC类似，AIFM利用读/写屏障来维护对象热度 读写屏障也是个很有趣的技术 但 AIFM 使用一个字节的热度计数器而不是一个比特标志，允许更细粒度的替换策略。与 AIFM 类似，一些复制收集器通过在 GC 期间分离热数据和冷数据来优化数据局部性，但目标不同的内存层次结构；例如，缓存-DRAM 层次结构[34]，DRAM-NVM 层次结构[5, 79, 80]和 DRAM-磁盘层次结构[14]。最后，内存疏散会干扰用户任务并影响其性能。为了减少干扰，AIFM 采用了与托管语言中的无暂停 GC 算法[20]类似的方法，而不是停止世界的 GC 算法[36]。 pauseless GC algorithms 不知道是不是 Java 现在用的，但好像也需要一些小暂停，比如 JDK 12 Shenandoah GC 和 ZGC Cliff Click, Gil Tene, and Michael Wolf. “The pauseless GC algorithm”. In: ACM/USENIX international conference on Virtual execution environments (VEE). 2005. ","date":"2024-11-06","objectID":"/posts/paper-aifm/:4:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#background-and-related-work"},{"categories":["OS"],"collections":null,"content":"Motivation 内核分页机制在访问远端内存的基本成本上引入了大量开销。考虑图 2，它分解了 Linux（v5.0.0）从 SSD 检索交换出的页面的成本。设备的硬件延迟约为 6µs，但由于与锁定（P1，P5）、虚拟内存管理（P2，P3，P5）、记账（P4）和读取 I/O 放大（P3）相关的开销，Linux 需要超过 15µs（2.5 倍）。此外，由于上下文切换的高成本，Linux 在等待数据时会旋转（P3），浪费了 11.7µs 的可能计算时间。 这里的几个 phase 可以详细讲讲 Linux Kernel swapping P1 page fault, trap P2 Lock -\u0026gt; PTE 页表获取页面磁盘位置 -\u0026gt; allocate page frame 页面帧 -\u0026gt; swap cache entry P3 read IO 请求 -\u0026gt; spin 检查 IO 是否完成 -\u0026gt; 页面进入 LRU P4 cgroup 计数，更新内存使用情况 -\u0026gt; 回收内存如果超过了限制 P5 页面映射 PTE -\u0026gt; 解锁 但是 AIFM 直接请求 IO，进入 context switch 然后用协程等待，最后上下文切换回去 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:5:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#motivation"},{"categories":["OS"],"collections":null,"content":"AIFM Design ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#aifm-design"},{"categories":["OS"],"collections":null,"content":"Overview AIFM 面向两个群体：应用程序开发者和数据结构开发者。 对于应用程序开发者来说，使用远端内存编程应用程序应该感觉几乎与使用纯本地数据结构编程相同。特别是，开发者不应该需要知道一个对象当前是本地的还是远程的（即远端内存是透明的） 对开发者来说用法和 std 应该一样？ RemHashtable\u0026lt;key_t, int\u0026gt; hashtable; RemArray\u0026lt;data_t\u0026gt; arr;可远程访问的内存数据结构本身（上面的 RemHashtable 和 RemArray）由数据结构工程师编写，他们使用 AIFM 的运行时 API 将可远程访问的内存对象包含在他们的数据结构中 当内存变得紧张时，AIFM 的运行时会将其中一些内存对象移动到远程内存；当数据结构需要访问远程对象时，AIFM 运行时会获取它们。数据结构工程师有很大的设计自由度：他们可以完全依赖 AIFM 来获取远程对象，或者他们可以在远程端部署自定义逻辑。 这里的移动是怎么发生的？消耗是什么，怎么监控内存紧张呢？是否会被别的修改呢？什么时候回收呢 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:1","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#overview"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#remoteable-memory-abstractions"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#remoteable-pointers"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#dereference-scopes"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#evacuation-handlers"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#remote-devices"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Abstractions We designed the abstractions such that they impose minimal overheads (as low as three micro-ops) on “hot path” access to local objects, and try to ensure that the “cold path” remote access incurs little latency above hardware limits Remoteable Pointers Memory representation. 唯一可远程访问指针（对应于 C++的 std::unique_ptr）的大小与普通 64 位指针相同，而共享指针（对应于 C++的 std::shared_ptr）的宽度为 128 位。图 4 显示了可远程访问唯一指针的内存布局。根据可远程访问指针是本地还是远程，我们采用不同的格式。如果内存是本地的（图 4a），指针在其低 47 位中包含一个虚拟内存地址（足以表示用户空间地址），并在高 17 位中包含控制位，包括标准的脏位（D）和存在位（P）（参考页表）。它还包含用于跟踪指针是否热（H）以及是否正在并发疏散（E）的位。对于唯一指针，共享位（S）设置为 0。我们将 D、E 和 H 位按字节对齐，允许每个位由变异器和运行时疏散器并发且原子地访问，因为字节是最小的读/写单位。 API: 清单 1 显示了可远程访问唯一指针的 API（共享指针的 API 大致相同）。RemUniquePtr 有两个构造函数：一个用于已经本地的对象，另一个用于当前远程的对象。第二个构造函数允许数据结构形成指向当前远程对象的可远程访问指针。这有助于数据结构工程师从其数据结构中引用远程对象，而无需获取这些对象。 要将远程指针转换为本地指针，程序员通过 deref 和 deref_mut API 方法进行解引用。 Dereferencing 运行时会检查可远程访问指针的存在位。如果对象是本地的，它将设置热位并返回存储在指针中的地址。否则，运行时会从远程服务器获取对象，设置热位和脏位（在 deref_mut 中），并返回指向数据的本地指针。 Dereference Scopes 未来，AIFM 可能会利用静态分析来捕捉生命周期违规，如 Rust 编译器 Evacuation Handlers 数据结构开发者通过调用 RegisterEvacHandler 注册他们的疏散处理器。疏散处理器与唯一的数据结构 ID 绑定，每个数据结构在其构造函数中分配该 ID，数据结构工程师必须一致使用该 ID。这样，不同的数据结构或同一数据结构的实例可以在同一个应用程序中共存，而运行时会调用适当的处理器 Remote Devices Semantic Hints Hotness tracking 内存疏散器使用此热度信息来确保频繁访问的对象是本地的 Prefetching AIFM 包含一个库，数据结构可以使用该库来维护每个线程的解引用位置历史窗口，并使用有限状态机（FSM）预测未来的访问 Nontemporal Access 对于没有时间局部性的对象的可远程访问指针，限制用于存储其对象数据的本地内存是有意义的 没有时间局部性，就是未来可能不再访问？可以立即回收 虽然很详细，但我觉得这部分讲的还是不够清楚不够直观 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#semantic-hints"},{"categories":["OS"],"collections":null,"content":"AIFM Runtime AIFM 的运行时建立在“绿色”线程（轻量级用户级线程）、内核旁路 TCP/IP 网络栈和无暂停内存疏散器之上。应用程序将运行时链接到其用户空间进程中。这使我们能够与 AIFM 的抽象共同设计运行时，并提供高性能的远端内存，而不依赖于任何操作系统内核抽象。 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:7:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#aifm-runtime"},{"categories":["OS"],"collections":null,"content":"Hiding Remote Access Latency 现有的操作系统内核线程支付高上下文切换成本：例如，在 Linux 上，重新调度任务大约需要 500ns。这些成本是远程内存延迟的非平凡部分，因此 Linux 和 Fastswap 采用了一种设计，即在等待网络响应时忙等待[6]。这避免了上下文切换开销，但也浪费了几个微秒的处理时间。这种方法还对网络提供商施加了巨大压力，要求支持更低的延迟，以减少浪费的周期[9, 28]。AIFM 采用了不同的方法：它依赖于低开销的绿色线程，在等待远程数据获取时执行应用程序工作。 其实是很简单的思路，用协程，异步 IO 但是缺点呢，这些协程怎么调度呢 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:7:1","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#hiding-remote-access-latency"},{"categories":["OS"],"collections":null,"content":"Remoteable Memory Layout 对于 AIFM 管理的本地内存，其运行时采用了日志结构内存[63]的思想，将本地可远程访问内存按日志粒度分割和管理 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:7:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#remoteable-memory-layout"},{"categories":["OS"],"collections":null,"content":"Pauseless Memory Evacuator 运行时的内存疏散器将冷对象移动到远程服务器 Log Selection Phase. Concurrent Marking Phase Evacuator Waiting Phase. 好奇这种阶段要不要锁，还是说使用读写屏障 + RCU + CAS 就够了 mutator: 如果变异器线程随后解引用指向运行时正在疏散的对象的指针，变异器会看到疏散位已设置。一个简单的方法是此时阻塞变异器线程 “变异器”（mutator）是指应用程序线程? Consistent with literature on garbage collection (GC), we refer to normal application threads as mutator threads i Concurrent Evacuation Phase. ","date":"2024-11-06","objectID":"/posts/paper-aifm/:7:3","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#pauseless-memory-evacuator"},{"categories":["OS"],"collections":null,"content":"Co-design with the Thread Scheduler 当运行时面临内存压力时，疏散是一项紧急任务。使用简单的线程调度器，疏散可能会被变异器线程饿死，导致内存不足错误和应用程序崩溃。 我们需要解决两个挑战。首先，大量变异器线程可能比疏散更快地分配内存。其次，疏散有时会在变异器线程的解引用范围内阻塞，这造成了一个困境。一方面，调度器需要执行变异器线程，以便它们可以解除疏散的阻塞。另一方面，执行变异器线程可能会消耗更多内存。 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:7:4","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#co-design-with-the-thread-scheduler"},{"categories":["OS"],"collections":null,"content":"Remoteable Data Structure Examples 略 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:8:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#remoteable-data-structure-examples"},{"categories":["OS"],"collections":null,"content":"Implementation AIFM 的实现包括核心运行时库（§5）和数据结构库（§6）。核心运行时建立在 Shenango[55]之上，以利用其快速的用户级线程运行时和 I/O 栈。 怪不得说这么多线程，用了 Shenango 我们将两个远端内存后端集成到 AIFM 中：一个基于 DPDK 的 TCP 栈的远程内存服务器，以及一个使用 SPDK 存储栈的 NVMe SSD。与远程内存后端不同，SSD 后端不支持活动的远程组件（因为存储驱动器没有通用计算单元），并且由于其固定块大小的限制，存在固有的 I/O 放大。我们的评估主要集中在远程内存后端。 当前实现有一些限制。首先，我们不支持 TCP 卸载或 RDMA，这会减少我们运行时的 CPU 开销。 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:9:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#implementation"},{"categories":["OS"],"collections":null,"content":"Evaluation ","date":"2024-11-06","objectID":"/posts/paper-aifm/:10:0","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#evaluation"},{"categories":["OS"],"collections":null,"content":"End-to-end Performance Web 服务 其次，我们还移植了一个开源的 C++ DataFrame 库[16]，其接口类似于 Pandas[56]，以了解所需的移植工作量和 AIFM 对现有应用程序的性能。 我们将两种 AIFM 设置（带有和不带有数组元素的非临时解引用）与 Fastswap[6]和理想化的基线（所有 26GB 都在本地内存中）进行比较。AIFM 的良好结果将显示其性能优于 Fastswap，非临时数组访问的好处，并且性能不比将所有数据保存在本地内存中低很多。 在图左侧（5%本地内存），AIFM 在哈希表（52%）和数组（89%）中看到高缺失率，因为 AIFM 的非临时数组解引用确保大部分本地内存专用于哈希表条目。相应地，数组缺失率下降得更慢，并与可用本地内存成比例。相比之下，Fastswap（此处未显示）在两个数据结构中都有高缺失率，因为其基于页面的方法管理本地内存效率低下。 为什么这里 web 应用都是 8K 对象呢？ ","date":"2024-11-06","objectID":"/posts/paper-aifm/:10:1","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#end-to-end-performance"},{"categories":["OS"],"collections":null,"content":"DataFrame Application 我们将一个流行的开源 C++ DataFrame 库[16]移植到 AIFM 的 API 中。该库中使用的主要数据结构是一个存储 DataFrame 列和索引的 std::vector，我们将其替换为启用了 AIFM 的等效数据结构。此外，我们还增加了对卸载关键操作的支持，这些操作计算强度低但内存访问频率高。我们通过使用 AIFM 的远程设备 API（§4.2.4）卸载三个操作来实现这一点。Copy 和 Shuffle 操作复制一个向量（即 DataFrame 列），Shuffle 还通过另一个列中的索引位置重新排序行；Aggregate 计算聚合值（总和、平均值等）。这三种操作用于五个 DataFrame API 调用，包括过滤器、列创建、排序和聚合（表 1）。为了实现足以运行纽约市出租车行程分析工作负载[53]的覆盖范围，我们在 DataFrame 库（24.3k 行代码）中修改了 1,192 行代码，并编写了 233 行远程设备代码。这些修改由一位作者花费了大约五天时间。 这修改挺累的 图 7 显示了结果。即使只有 1GB 的本地内存（3.2%），AIFM 也能达到内存吞吐量的 78%，并且从大约 20%（6GB）的本地内存开始超过理想性能的 95%。相比之下，Fastswap 在 1GB 时仅达到内存性能的 20%，并且只有在超过 90%的工作集在本地内存中时才接近它。AIFM 的高性能来自于避免 Fastswap 的页面错误开销，并通过卸载计算强度低的操作来减少昂贵的网络数据移动。在没有卸载的情况下，AIFM 优于 Fastswap，直到 60%的工作集在本地，因为 Fastswap 会频繁发生次要错误。超过 60%后，Fastswap 的错误率下降到足以使大多数内存访问优于 AIFM 对计算强度低的操作的解引用时间开销（例如，内存复制）。将这些操作卸载到远程侧有助于 AIFM 避免这种成本，而高计算强度的操作摊销了解引用成本并在本地发生。我们还为 AIFM 原型化了一个批处理 API，当无法卸载时，该 API 在向量元素组之间摊销了解引用开销，并发现它将没有卸载的 AIFM 吞吐量提高到内存吞吐量的 60-80%。我们相信这可能是 AIFM API 的一个很好的未来补充，以加速必须在本地执行的计算强度低的操作。 图 8 分解了卸载的影响。卸载 Copy 贡献了最大的吞吐量增益（18%-38%）；卸载 Shuffle 贡献了 2.9%-13%；卸载 Aggregate 贡献了 4.5%-12%。这些结果表明，AIFM 在本地内存较小的情况下为实际工作负载实现了高性能，并且当工作负载包括计算强度低的操作时，AIFM 的操作卸载对于良好的性能至关重要。 AIFM 的高性能来自于避免 Fastswap 的页面错误开销，并通过卸载计算强度低的操作来减少昂贵的网络数据移动。 怎么理解？ 卸载 copy, shuffle, agg 到远端内存感觉不错 但没有启用 RDMA 还是很可惜的 ","date":"2024-11-06","objectID":"/posts/paper-aifm/:10:2","tags":["Paper Reading"],"title":"Paper Reading: AIFM: High-Performance, Application-Integrated Far Memory (OSDI 20)","uri":"/posts/paper-aifm/#dataframe-application"},{"categories":["OS"],"collections":null,"content":"Can Far Memory Improve Job Throughput? memory disaggregation 内存解耦的论文，稍微看看 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#can-far-memory-improve-job-throughput"},{"categories":["OS"],"collections":null,"content":"Abstract 随着内存需求的增加和内存技术进步的放缓，大型计算集群中主内存的可用性越来越成为瓶颈。 种解决方案是内存解耦，即作业可以远程访问其他服务器上的内存，或称为远端内存 本文首先介绍了更快的交换机制和一个支持远端内存的集群调度器，使其能够在机架 rack 规模上支持远端内存。 memory-intensive workloads 机架规模是什么，一个架子上的一堆机器？ ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 内存密集型工作负载（如机器学习应用和键值存储）的日益流行， 虽然远端内存并不减少运行单个作业所需的内存总量，也不使内存更便宜或更密集，但它确实意味着作业不必局限于本地内存，而是可以利用集群中其他位置的内存。这绕过了“内存容量墙”[48]，并增加了内存跨作业共享的效率。 google 利用 zswap 来实现远端内存，和这里提到的有什么区别？他们的 bigtable 表现好像也一般，没有降低太多的成本 没有给其他的性能参数，本篇倒是很在意吞吐量，不知道是不是动态负载或者突发负载 使远端内存实用化面临两大障碍。 第一个障碍在于如何设计访问远端内存所需的交换机制，因为现有系统通过 RDMA 进行交换时，由于 head-of-line blocking, and to handling interrupts and page reclamation on the critical path of page fault resolution, 导致延迟和吞吐量不佳。我们设计了一个名为 Fastswap 的 Linux 交换系统，该系统通过 RDMA 优化使用远端内存。 头阻塞，HOL blocking 队列头部处理缓慢，RDMA 使用队列管理，容易发生头阻塞 与其他交换系统一样，它对应用程序和开发者都是透明的。此外，它直接与 Linux 控制组[5]交互，允许 Fastswap 强制执行本地内存分配。Fastswap 通过将关键路径上的页面获取引导到单独的队列来防止头阻塞。此外，它通过轮询关键页面操作的完成情况，并将内存回收卸载到专用 CPU，从而减少关键路径上的延迟。因此，Fastswap 实现了\u0026lt;5 微秒的远程页面访问延迟，使应用程序能够以 10 Gbps 的速度（单线程）和 25 Gbps 的速度（多线程）访问远端内存。Infiniswap[34]是最接近的相关工作，Fastswap 的带宽在单线程时比 Infiniswap 高 1.51 倍，在多线程时高 2.54 倍（禁用备份磁盘时） critical path 是决定整个任务完成时间的最长路径？比如 page fault, interrupts, reclaim? 第二个障碍在于如何决定将每个作业的内存需求在本地内存和远端内存之间进行分配。 使用远端内存在某种程度上是一个装箱问题：如何在每个服务器上给定数量的本地内存和大量远端内存的情况下，以最快的速度处理工作负载，并且每个作业必须分配满足其需求总量的内存（本地和远程）？ 为什么不是肯定优先本地呢？ 为此，我们设计了一个远端内存感知的集群调度器，利用远端内存来提高作业吞吐量。当一个新作业到达时，调度器可以将作业放置在一个初始可用本地内存不足以处理所有分配给它的作业的服务器上。然后，我们的调度器减少该服务器上一些现有作业使用的本地内存，并使用远端内存确保所有作业都能访问足够的总内存。这种策略是否有益尚不明确，因为使用远端内存不可避免地会减慢单个作业的速度（因为访问远端内存比访问本地内存慢得多）。然而，使用远端内存也可以使更多作业在单个服务器上同时运行，尽管速度较慢，这可能会提高整体吞吐量。我们广泛研究了这种权衡，并报告了在何种情况下使用远端内存可以提高整体吞吐量，以及这与仅增加本地内存量的比较。据我们所知，这是对这些问题的首次系统性探索。 一种 trade off 还是无意义？ 改进的交换系统 Fastswap 和集群调度器的结合提供了对 cluster-wide far memory 的支持，我们称之为 CFM，但我们使用了一个集群模拟器 We find that far memory is not a panacea 系统设计没有银弹 如果内存需求远大于可用内存，那么通过增加每个服务器的本地内存而不是将等量的内存添加到共享的远端内存服务器上，可以获得更好的性能。然而，我们发现远端内存在单个机架的两个关键场景中提供了显著的好处：（1）如果工作负载是内存密集型的（即内存可用性而不是核心可用性是瓶颈），将计算节点转换为远端内存服务器可以导致（在我们研究的情况下）与原始机架相比大约 10% 的吞吐量提升，尽管两种机架配置的内存总量相同。（2）如果操作员希望适度增加机架的内存容量，向内存服务器添加内存允许更细粒度的增加，这仍然会导致显著的性能提升，而升级每个服务器的本地内存只能以更大的（因此更昂贵的）增量进行（正如我们在下一节中讨论的那样）。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#introduction"},{"categories":["OS"],"collections":null,"content":"Context ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#context"},{"categories":["OS"],"collections":null,"content":"Memory Provisioning 重要的是要记住，本地内存只能以粗粒度进行配置。 啥意思？跟 DIMM 插槽有关？ ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#memory-provisioning"},{"categories":["OS"],"collections":null,"content":"Deployment Scenarios 我们主要不是考虑 green-field deployments 但我们的主要关注点是逐步升级现有部署。他们现有的数据中心往往已经填满了所有 DIMM 插槽。这在经济上是合理的，因为每单位内存的成本随着 DIMM 容量的增加而增加，因此配置给定数量的内存的最便宜方式是使用所有可用的 DIMM 插槽。 green-field deployments 绿场部署，是从头开始，全新的部署 虽然远端内存有可能提高集群吞吐量，但这会以单个作业运行时间变慢为代价。因此，我们认为远端内存最适合那些主要指标是作业吞吐量的应用，而不是面向客户或对延迟敏感的应用；这是本文的重点。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#deployment-scenarios"},{"categories":["OS"],"collections":null,"content":"CFM Overview CFM 的目标是通过利用专用内存服务器上的远端内存来提高集群的端到端作业吞吐量。 尽管之前的研究已经探索了启用远端内存的机制，并展示了单个作业在交换时的性能优势（例如，[34]），但据我们所知，没有先前的研究在机架规模上展示了远端内存的性能改进。我们专注于改进端到端完成时间，即完成执行作业列表所需的时间。在高层次上，CFM 的方法（§3.1）与先前的工作相似，但 CFM 克服了几个关键挑战（§3.2），这些挑战使得今天难以从远端内存中获得集群规模的收益。 这里提到单个作业性能提高的论文是 Juncheng Gu, Youngmoon Lee, Yiwen Zhang, Mosharaf Chowdhury, and Kang G. Shin. 2017. Efficient Memory Disaggregation with INFINISWAP. In Symposium on Networked Systems Design and Implementation (NSDI’17). 649–667. ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#cfm-overview"},{"categories":["OS"],"collections":null,"content":"Approach 在 CFM 中，应用程序通过 RDMA 交换来利用远端内存。CFM 使用 Linux 控制组来强制执行内存分配。 Swapping: 应用程序可以通过两种广泛的方式利用远端内存：透明地（无需应用程序修改），或通过显式且可能是自定义的 API。 相反，类似于 Infiniswap [34]，CFM 通过交换实现远端内存，这是一种现有的机制，将虚拟内存扩展到物理可用内存之外。当 CPU 访问物理内存中不存在的页面中的内存地址时，会引发页面故障，页面故障处理程序透明地将页面内容从交换空间获取到本地内存中。传统上，交换空间位于磁盘上，由此产生的毫秒级访问延迟对工作负载引入了大且难以理解的性能开销。然而，交换本身并不意味着毫秒级的延迟，并且随着今天的微秒级网络延迟，通过网络交换到远端内存有可能获得良好的性能。 Cgroups: CFM 使用 Linux 控制组（cgroups）[5]来enforces 执行每个作业的本地内存消耗限制。控制组控制分配给一组进程的物理内存量，CFM 使用交换系统将多余的内存保持在远端内存中。 Control Groups，cgroups RDMA: CFM 利用 RDMA 进行低延迟访问远程服务器上的内存。CFM 使用 one-sided read and write 操作，这些操作可以在不使用远程 CPU 的情况下访问内存,通常，RDMA 操作提交到本地队列对，然后由本地 RDMA NIC 处理。一旦操作完成，NIC 将完成情况发布到完成队列；完成队列可以配置为在完成到达时引发中断，或保持静默，期望它们将被轮询。传统上，RDMA 绕过远程和本地操作系统，但 RDMA 也提供了一个内核 API 供驱动程序使用；CFM 利用此 API 通过网络交换页面。 什么 API？ ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#approach"},{"categories":["OS"],"collections":null,"content":"Challenges and Contributions 两个主要挑战：快速交换和决定如何在本地和远端内存之间调度作业 Fast Swapping RDMA 交换设备已经在先前的研究中进行了探索，例如 Infiniswap [34]和 HPBD [47]。然而，这些方法无法维持当今应用程序所需的高性能 为了隐藏未来页面故障的 I/O 延迟，操作系统通常通过在每次页面故障时获取多个页面来实现页面预取。不幸的是，在 Linux 中，故障页面——应用程序当前所需的页面——可能位于要预取的对齐页面窗口中的任何位置。因此使用 Linux 默认的预取窗口大小 8，头阻塞可能会延迟故障处理数十微秒。 在现有的通过 RDMA 进行交换的系统中，CPU 通过中断通知 RDMA 操作（例如，读取远程页面）已完成。这种中断处理发生在关键路径上——在页面故障处理程序能够返回到应用程序之前——并且可能会增加 10 微秒或更多的页面故障处理时间[13]。 在故障页面的内容被读入本地内存后，操作系统通过增加其内存计数器将其计入其控制组。如果控制组内存限制被超过，则需要回收多余的页面。与 Linux 中的系统范围回收不同，控制组中的内存回收总是直接进行的，即在离开页面故障处理程序并返回到应用程序之前。因此，整个回收过程（查找要回收的页面，将它们写入交换设备，并将页面返回内核以供重用）会延迟页面故障解决。 CFM 引入了一个名为 Fastswap（§4）的更快的交换系统，克服了所有这三个挑战，使 CFM 能够实现比现有系统（如 Infiniswap）更低的延迟和更高的远程交换吞吐量（§6.4）。 Cluster Scheduling 许多现有的调度器通过在核心、内存和其他资源之间调度作业来实现集群资源的有效共享。然而，现有调度器不考虑远端内存；也就是说，它们不支持调度内存可以在本地和远端内存之间动态分割的作业，也不指定如何最好地在共享同一机器的多个作业之间分配本地内存。CFM 提出了一种集中的远端内存感知调度器（§5），在将作业分配给机器时考虑远端内存，并决定如何在不同作业之间划分本地内存以优化完成时间。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#challenges-and-contributions"},{"categories":["OS"],"collections":null,"content":"Challenges and Contributions 两个主要挑战：快速交换和决定如何在本地和远端内存之间调度作业 Fast Swapping RDMA 交换设备已经在先前的研究中进行了探索，例如 Infiniswap [34]和 HPBD [47]。然而，这些方法无法维持当今应用程序所需的高性能 为了隐藏未来页面故障的 I/O 延迟，操作系统通常通过在每次页面故障时获取多个页面来实现页面预取。不幸的是，在 Linux 中，故障页面——应用程序当前所需的页面——可能位于要预取的对齐页面窗口中的任何位置。因此使用 Linux 默认的预取窗口大小 8，头阻塞可能会延迟故障处理数十微秒。 在现有的通过 RDMA 进行交换的系统中，CPU 通过中断通知 RDMA 操作（例如，读取远程页面）已完成。这种中断处理发生在关键路径上——在页面故障处理程序能够返回到应用程序之前——并且可能会增加 10 微秒或更多的页面故障处理时间[13]。 在故障页面的内容被读入本地内存后，操作系统通过增加其内存计数器将其计入其控制组。如果控制组内存限制被超过，则需要回收多余的页面。与 Linux 中的系统范围回收不同，控制组中的内存回收总是直接进行的，即在离开页面故障处理程序并返回到应用程序之前。因此，整个回收过程（查找要回收的页面，将它们写入交换设备，并将页面返回内核以供重用）会延迟页面故障解决。 CFM 引入了一个名为 Fastswap（§4）的更快的交换系统，克服了所有这三个挑战，使 CFM 能够实现比现有系统（如 Infiniswap）更低的延迟和更高的远程交换吞吐量（§6.4）。 Cluster Scheduling 许多现有的调度器通过在核心、内存和其他资源之间调度作业来实现集群资源的有效共享。然而，现有调度器不考虑远端内存；也就是说，它们不支持调度内存可以在本地和远端内存之间动态分割的作业，也不指定如何最好地在共享同一机器的多个作业之间分配本地内存。CFM 提出了一种集中的远端内存感知调度器（§5），在将作业分配给机器时考虑远端内存，并决定如何在不同作业之间划分本地内存以优化完成时间。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#fast-swapping"},{"categories":["OS"],"collections":null,"content":"Challenges and Contributions 两个主要挑战：快速交换和决定如何在本地和远端内存之间调度作业 Fast Swapping RDMA 交换设备已经在先前的研究中进行了探索，例如 Infiniswap [34]和 HPBD [47]。然而，这些方法无法维持当今应用程序所需的高性能 为了隐藏未来页面故障的 I/O 延迟，操作系统通常通过在每次页面故障时获取多个页面来实现页面预取。不幸的是，在 Linux 中，故障页面——应用程序当前所需的页面——可能位于要预取的对齐页面窗口中的任何位置。因此使用 Linux 默认的预取窗口大小 8，头阻塞可能会延迟故障处理数十微秒。 在现有的通过 RDMA 进行交换的系统中，CPU 通过中断通知 RDMA 操作（例如，读取远程页面）已完成。这种中断处理发生在关键路径上——在页面故障处理程序能够返回到应用程序之前——并且可能会增加 10 微秒或更多的页面故障处理时间[13]。 在故障页面的内容被读入本地内存后，操作系统通过增加其内存计数器将其计入其控制组。如果控制组内存限制被超过，则需要回收多余的页面。与 Linux 中的系统范围回收不同，控制组中的内存回收总是直接进行的，即在离开页面故障处理程序并返回到应用程序之前。因此，整个回收过程（查找要回收的页面，将它们写入交换设备，并将页面返回内核以供重用）会延迟页面故障解决。 CFM 引入了一个名为 Fastswap（§4）的更快的交换系统，克服了所有这三个挑战，使 CFM 能够实现比现有系统（如 Infiniswap）更低的延迟和更高的远程交换吞吐量（§6.4）。 Cluster Scheduling 许多现有的调度器通过在核心、内存和其他资源之间调度作业来实现集群资源的有效共享。然而，现有调度器不考虑远端内存；也就是说，它们不支持调度内存可以在本地和远端内存之间动态分割的作业，也不指定如何最好地在共享同一机器的多个作业之间分配本地内存。CFM 提出了一种集中的远端内存感知调度器（§5），在将作业分配给机器时考虑远端内存，并决定如何在不同作业之间划分本地内存以优化完成时间。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#cluster-scheduling"},{"categories":["OS"],"collections":null,"content":"Fastswap 但我们发现，要实现更高的交换性能，需要对页面故障处理程序、交换系统和控制组内存控制器进行修改。我们通过修改大约 300 行内核代码和为 Linux 4.11 编写了一个新的 1200 行代码的设备驱动程序来实现 Fastswap 提高分页性能具有挑战性。虽然许多系统专注于在毫秒时间尺度上进行改进[35, 46]，但我们的系统力求实现微秒尺度的交换。我们在本节中讨论的大多数机制发生在程序执行暂停时。因此，我们节省的每一微秒都是返回给应用程序的计算时间。 ?? 他是怎么实现微秒级别的 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#fastswap"},{"categories":["OS"],"collections":null,"content":"RDMA Backend 在 Fastswap 中，操作系统使用 RDMA 后端与 RDMA NIC 交互 如图 2 所示，后端用于所有交换操作类型：页面故障、预取和内存回收。尽管之前的研究已经将 RDMA 后端暴露为块设备 block device 是什么？ Queue pairs: 给定队列对中的 RDMA 请求由 NIC 处理单元按顺序处理，如果不同类别的交换操作共享一个队列，关键操作——例如，故障页面的读取和被驱逐页面的写入——将排在不太紧急的预取读取之后。Fastswap 通过为每个 CPU 使用两个 RDMA 队列对来避免这种头阻塞，一个用于关键路径上的操作，一个用于预取。 Frontswap interface: Frontswap 假设其操作是同步完成的[3]，即在 Frontswap 操作完成后，执行控制才会返回到交换系统。因此，它没有提供区分关键路径上的操作和非关键操作的机制。因此，Fastswap 增强了 Frontswap 接口，以区分关键和非关键操作，使 RDMA 后端能够将请求引导到适当的队列对。 加队列就完了 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#rdma-backend"},{"categories":["OS"],"collections":null,"content":"Page Fault Handler Fastswap 通过两种关键方式修改页面故障处理程序。 首先，它指示交换系统以不同的方式处理故障页面和预取页面，如上所述。其次，Fastswap 修改交换系统，首先读取故障页面，然后读取预取窗口内的剩余页面 在发出所有读取操作后，Fastswap 轮询等待故障读取完成。通过首先发出故障读取，我们重叠了为预取读取分配物理页面的延迟和发出预取 RDMA 读取的延迟，与故障页面的 RDMA 读取。图 3 展示了 Fastswap 如何处理页面故障及其相关的预取。 分别处理故障页面和预取页面最小化了错过预取的成本。例如，假设页面故障 1 发生在地址 F1，并有一组相关的预取页面 P1。我们的交换系统将发出 F1 和 P1 的读取操作，并轮询直到 F1 的读取完成。此时，页面故障处理程序将返回到用户空间。然后，假设页面故障 2 发生在地址 F2，其中 F2 \u0026lt; P1（即预取未命中）。Fastswap 可以在不等待 P1 中的任何页面的情况下获取 F2，而之前的系统则需要等待 P1 中的所有页面完成后才能完成 F2 的读取 fastswap 就是把预读的优先级降低了？ ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#page-fault-handler"},{"categories":["OS"],"collections":null,"content":"Memory Reclaim 我们已经介绍了 Fastswap 交换系统如何将页面从远端内存带入本地内存。现在我们描述 Fastswap 如何回收内存，以确保进程不会使用超过其允许份额的本地内存。Fastswap 通过修改控制组内存控制器，将回收操作从页面故障处理的关键路径上移除。 传统上，在读取故障页面后，内存控制器将页面计入其控制组。然后，控制器检查控制组是否有超过其允许份额的内存。如果有多余的页面，它们会被直接回收，并可能被驱逐到远端内存。直接回收发生在页面故障处理程序的上下文中，因此它会阻止 CPU 返回到用户空间并继续工作负载执行。 Linux 中的内存回收出乎意料地昂贵，当我们的应用程序有 50%的内存位于远端内存时，消耗了 62-85%的内核时间。为了减少这些成本，每当节点使用远端内存时，我们修改的内存控制器将内存回收卸载到专用回收 CPU（图 2）；我们称之为卸载回收 offloaded reclaim。卸载内存回收允许导致页面故障的 CPU 在不需要花费时间进行直接回收的情况下返回到用户空间。最近的努力已经使用了类似的方法来卸载冷内存压缩[43]和数据包处理[22, 52]到专用 CPU。 无论内存回收是直接的还是卸载的，在将页面驱逐到远端内存时，我们都会轮询它们的完成情况。只有在页面写入远端内存完成后，页面才能完全回收。 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#memory-reclaim"},{"categories":["OS"],"collections":null,"content":"Far Memory-Aware Scheduler \u0026hellip; 后面都省略了，实验也跳过，怪怪的 使用了定义的几个参数，负载 / 集群配置差不多，看远端内存会调用多少，然后做了一个最优化问题，最大 (A - B) / C, A−B is the total local memory-time product saving, C is the total far memory-time product 更想看每个组件的表现而不是这种 ","date":"2024-11-05","objectID":"/posts/paper-fastswap/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Can Far Memory Improve Job Throughput? (Fastswap)","uri":"/posts/paper-fastswap/#far-memory-aware-scheduler"},{"categories":["OS"],"collections":null,"content":"Software-Defined Far Memory in Warehouse-Scale Computers zswap 压缩内存的一篇论文，使用远端内存来降低成本 需要理解什么是 cold memoery，什么是 zswap 怎么做压缩 zswap linux manual, https://www.kernel.org/doc/html/v4.18/vm/zswap.html 使用了 ML 做 Autotunner 有意思的是，ASPLOS 19 在 Providence RI，Hoard (内存管理) 是当年的 Influential Paper Winner 至于 Far Memory 作用应该是很有前途的，除了 Google 提到的 WSC 仓库级负载，我觉得一些数据库也可以应用，尤其是论文提到 Bigtable 结合 除了 zswap far memo, 也有 application-integrated far memory (Brown), Fastswap(UCB) 等等比较有意思的 还有一篇 OSDI 22 best paper 看着有意思 MemLiner: Lining up Tracing and Application for a Far-Memory-Friendly Runtime ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#software-defined-far-memory-in-warehouse-scale-computers"},{"categories":["OS"],"collections":null,"content":"Abstract 随着内存需求的增加和技术扩展速度的减缓，仓库级计算机（WSC）的 total cost of ownership（TCO）面临着重要的挑战。 一个有前景的想法是通过添加一个更便宜但速度较慢的 “far memory” 层级来降低内存的 TCO，并使用它来存储不常访问（或冷）的数据。 然而，引入远端内存层级带来了新的挑战，包括动态响应工作负载多样性和变化、最小化容量闲置以及处理 brownfield (legacy) 部署。 TCO 总拥有成本，就是总成本？比如服务器价格、维护价格什么的吧 远端内存，能理解成类似 S3 吗？ 问题就是兼容、动态负载什么的 我们提出了一种新颖的软件定义方法来实现远端内存，通过主动压缩冷内存页来在软件中有效地创建一个远端内存层级。我们的端到端系统设计包括新的方法来定义性能服务级别目标（SLO），一种在满足 SLO 的同时识别冷内存页的机制，以及我们在操作系统内核和节点代理中的实现。此外，我们还设计了基于学习的自动调优，以定期适应整个机群的变化，而无需人工干预。 自 2016 年以来，我们的系统已在谷歌的 WSC 中成功部署，服务于数千个生产服务。我们的软件定义远端内存在相对良好的访问速度（6 µs）下显著降低了成本（67%或更高的内存成本减少），并允许我们存储相当大比例的不常访问数据（平均 20%），从而在仓库规模上实现了显著的 TCO 节省。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 扩展 WSC 通常受限于在性能和成本效益方面扩展行为最弱的组件。 近年来，DRAM 已成为扩展 WSC 的关键瓶颈。设备级扩展速度的减缓（摩尔定律的终结[35]）阻止了每 GB DRAM 成本的降低[25, 27]。同时，内存计算的普及，特别是大数据工作负载，导致了 DRAM 需求的爆炸性增长。这两个趋势导致了近年来全球 DRAM 供应短缺，对 WSC 的成本效益扩展构成了严重挑战。 一个有前景的方向是引入第二级内存或远端内存，以降低内存拥有成本。远端内存是介于 DRAM 和闪存之间的一层，每 GB 成本低于 DRAM，性能高于闪存。通过在内存层次结构中引入远端内存并将不常访问（或冷）数据存储到远端内存中，系统可以在较低的 DRAM 容量下执行相同的工作，或者在每台机器上打包更多的工作，从而降低总拥有成本（TCO）。现代 WSC 及其上运行的应用程序具有以下特点，在部署第二级内存时提出了独特的需求： Near-zero tolerance to application slowdown, WSC 对性能成本比非常敏感，但应用程序的减速可能导致不可恢复的服务级别协议（SLA）违规， Heterogeneity of applications 在现代 WSC 上运行的应用程序越来越多样化 Dynamic cold memory behavior WSC 在工作负载混合和/或利用率（例如，昼夜模式）方面表现出动态变化，导致每台机器可利用远端内存技术的有效内存大小发生变化。因此，近端和远端内存之间的最佳比例不仅取决于当前运行的工作负载，而且随时间变化。因此，希望降低闲置远端内存容量的 TCO 影响，或者在供应方面具有灵活性。 在本文中，我们解决了上述 WSC 拥有成本的挑战， 我们展示了对真实世界 WSC 的机群范围纵向描述，量化了每台机器可用冷内存量的巨大变化。我们发现，冷内存量在不同集群之间从 1%到 61%不等，即使在同一集群内，根据应用程序混合和一天中的时间，冷内存量也从 1% 到 52% 不等。这种范围需要灵活的远端内存供应，而不是固定容量的远端内存。 我们展示了一种软件定义的远端内存方法，该方法易于获取，提供灵活性 improves time to market, 使内存 TCO 变得可行。 具体来说，我们展示了 zswap[1]，一个 Linux 内核机制，可以将内存压缩存储在 DRAM 中，可以用于实现软件定义的远端内存，提供 single-digit µs of latency at tail.。我们还展示了我们主动将冷页移动到较慢的远端内存的方法，在从低访问率的页中获取内存容量方面表现良好，而不是在机器内存压力下的被动方法。 我们讨论了我们的方法的设计和实现。我们的控制平面包括（1）一个收集内存访问统计信息并将冷内存页交换到远端内存的内核机制，以及（2）一个根据应用程序行为控制内核机制激进程度的节点代理。我们的设计基于明确定义的服务级别目标（SLO），并且可以推广到其他类型的远端内存设备。 我们实现了一个基于机器学习的自动调优系统，根据机群范围的行为优化控制平面。它包括一个快速远端内存模型，估计在不同配置下整个 WSC 的远端内存行为，并由称为高斯过程（GP）Bandit[17, 21, 39]的机器学习算法指导设计空间探索。这使得整个系统能够适应整个 WSC 的长期行为变化 我们展示了来自真实世界用例的评估数据，包括在生产工作负载混合中的纵向研究和与 Bigtable[10]的案例研究。我们的系统可以迁移 20-30%的不常用数据，促进 4-5%的内存 TCO 节省（在 WSC 规模下为数百万美元），同时对多样化的应用程序影响微乎其微。我们的基于机器学习的自动调优器相对于基于启发式的方法，额外提高了我们系统的效率 30%。 这个 Gaussian Process (GP) Bandit 在调优里蛮常见的，数据库调优等等 但效果有这么好吗 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#introduction"},{"categories":["OS"],"collections":null,"content":"Background and Motivation ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#background-and-motivation"},{"categories":["OS"],"collections":null,"content":"Far Memory 远端内存是介于 DRAM 和闪存之间的一层，每 GB 成本低于 DRAM，性能高于闪存。在本节中，我们概述了先前研究中探讨的远端内存技术，并从 WSC 的角度讨论了它们的特点。 Non-volatile memory（NVM）是一种新兴的内存技术，其实现了比 DRAM 更高的密度（因此每 GB 成本更低）和基于新材料的数据持久性。迄今为止，相对于 DRAM，大多数 NVM 技术显示出更高的延迟（从数百纳秒到数十微秒）、更低的带宽（单数 GB/s）以及读/写不对称性（即写入比读取慢）。在访问接口方面，市场上主要有两种类型的 NVM 设备：内存总线（例如，NVDIMM-P [37]，Intel Optane DC 持久内存 [20] 等）和 PCIe 总线（例如，Intel Optane DC SSD [20]，三星 Z-SSD [38] 等）。两者的主要区别在于，前者允许以缓存块粒度对 NVM 进行加载/存储访问，而后者则通过类似于存储设备的页面粒度访问接口，数据在访问之前必须从 NVM 复制到主内存。因此，前者通常提供对存储在 NVM 中的数据的更快访问，但需要 CPU 端的硬件支持。许多当前的 NVM 设备仅以固定预设大小提供。这可能会在 WSC 的上下文中导致资源闲置。 Remote memory. 内存分解[30]是一种使用远程机器的内存作为交换设备的方法。它通过利用远程机器中的未使用内存[18, 29]，或通过构建仅用于为许多机器提供共享内存池的内存设备[30, 31]来实现。这两种实现方式都通过在集群中的机器之间平衡内存使用，减少了每台机器过度配置内存容量的需求。访问远程页面需要一到数十微秒的时间，具体取决于集群大小和网络结构速度。在 WSC 的上下文中，远程内存有一些有趣的挑战需要解决，然后才能部署以实现内存 TCO 节省[6]。首先，将内存页面交换到远程机器会扩大每台机器的故障域，使集群更容易受到灾难性故障的影响。其次，在离开机器之前，必须对正在交换的页面进行加密，以符合 WSC 应用程序处理敏感信息时通常设定的严格安全要求。第三，许多 WSC 应用程序对尾部延迟敏感[7]，但对于集群或机架来说，限制尾部延迟比对单台机器更难。 远端内存：容错性？安全性？ ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#far-memory"},{"categories":["OS"],"collections":null,"content":"Far Memory in a Real-World WSC: Opportunities and Challenges 有许多方法可以定义内存页的冷度。我们关注基于以下两个原则的定义：（1）通过将超过 T 秒未访问的内存页分类为冷页，来体现时间局部性的价值；（2）通过测量对冷内存页的访问速率（称为提升率），作为远端内存对应用程序影响的代理。这两个原则是我们冷页识别机制的基石（在第 4 节中解释）。 图 1 显示了在不同 T 值下，WSC 中运行的每个作业的冷内存百分比和提升率的机群范围平均值。由于较低的 T 值会在内存页生命周期的较早阶段将其分类为冷页，因此它会识别更多的冷页。在最激进的设置 T = 120 秒时，我们观察到平均 32% 的内存使用是冷的。这一大比例的冷内存展示了远端内存在真实世界 WSC 中的巨大潜力。 另一方面，随着系统在识别冷内存方面变得更加激进，远端内存的性能开销也会增加。在 T = 120 秒时，应用程序平均每分钟访问其总冷内存的 15%。根据近端内存和远端内存之间的相对性能差异，这一访问率可能会显著降低应用程序性能，抵消远端内存的 TCO 节省。 冷内存百分比与性能开销之间的这种权衡关系促使需要一个强大的控制算法，该算法可以在最大化前者的同时最小化后者。在本小节的其余部分，为了简单起见，我们将假设 T = 120 秒。 图 2 展示了在 10 个集群中每台机器的冷内存百分比分布（即冷内存总大小除以每台机器的内存使用量），**每个集群最多包含数万台机器。**我们发现，即使在同一集群内，冷内存百分比也从 1% 到 52% 不等。如果我们将每台机器的远端内存容量配置为总内存容量的 20%，那么一些机器将有比可用远端内存容量多 30%的冷内存。另一方面，配置为 50% 将导致一些机器的近端内存容量不足，导致过多的性能开销。从 TCO 的角度来看，这两种方法都不理想。 此外，应用程序行为增加了另一个维度的可变性。图 3 描述了每个作业中冷内存百分比的分布（在作业执行过程中平均）。对于前 10%的作业，至少 43%的内存使用是冷的；对于后 10%的作业，这一比例降至低于 9%。这种异质性，加上现代 WSC 上运行的大量作业，使得为每个应用程序优化远端内存变得不切实际。 总之，将冷内存存储到更便宜但速度较慢的远端内存具有在 WSC 中节省 TCO 的巨大潜力。但要实现这一点，系统必须（1）能够准确控制其激进程度，以最小化对应用程序性能的影响；（2）能够适应不同机器、集群和作业之间冷内存行为的可变性。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#far-memory-in-a-real-world-wsc-opportunities-and-challenges"},{"categories":["OS"],"collections":null,"content":"Software-defined Far Memory We show a software-defined approach to far memory implementation, which we have adopted and deployed at Google. 特别是，我们提议采用 zswap [1]作为现成的远端内存解决方案。在 Linux 内核（3.11+）中，zswap 作为一个交换设备，旨在避免机器内存耗尽。 zswap 在手机端使用比较常见，经常用于内存压缩保留更多的 RAM，比如一个应用最近很少使用的页面，可以被压缩释放，节省内存 除了压缩，也用于 swap，压缩页到压缩池而不是放回磁盘，等下次访问解压缩放回内存，减少磁盘 IO 使用 lzo, zstd 等压缩算法， 用时间换空间和成本的意思？ 相反，我们采取了一种主动的方法，使用 zswap 在内存中存储冷压缩页，并在软件中实现远端内存。压缩内存页允许我们在内存中打包更多数据（即每 GB 成本更低），代价是增加访问时间。从 TCO 的角度来看，这与任何其他远端内存实现没有本质区别。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#software-defined-far-memory"},{"categories":["OS"],"collections":null,"content":"Advantages of Software-defined Far Memory Reliability zswap 将故障域限制在一台机器内，将灾难性故障限制在单台机器上，同时避免了远程内存的安全性和可靠性挑战。此外，它不引入额外的硬件组件，简化了系统设计、实现、测试、部署和监控。 Time to deployment zswap 作为一种软件方法，可以以更短的时间和更低的成本部署，并且不需要跨供应商合作，因为不需要任何特殊硬件（例如 NVM） 请注意，部署速度对于 WSC 至关重要 No new hardware cost 与其他远端内存技术不同，zswap 通过用 CPU 周期（用于压缩和解压缩）换取内存节省。 Adaptive to dynamic application behavior WSC 中工作负载混合和内存访问模式的变化（第 2.2 节）导致每台机器和跨机器的冷内存大小发生变化。zswap 可以通过动态调整分配给作业的内存容量（通过压缩更多或更少的内存）来适应这种变化。即使在如此动态的情况下，zswap 仍然可以实现内存 CapEx 节省，因为平均跨数万台机器使内存节省在集群级别稳定，这是我们配置容量的方式（第 6.1 节）。这使得 zswap 与那些一旦部署容量就难以改变的远端内存解决方案有所不同。此外，它还使得在不改变硬件配置的情况下，更快地尝试不同设置成为可能。 缺点呢？ 平常空闲的机器无法应对突发负载？稳定性？ ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#advantages-of-software-defined-far-memory"},{"categories":["OS"],"collections":null,"content":"Challenges in Software-defined Far Memory WSC 中远端内存的控制机制需要（1）严格控制性能减速以满足定义的 SLO，以及（2）低 CPU 开销，以便最大化从远端内存获得的 TCO 节省。尽管 zswap 在 WSC 的远端内存设计中表现出有利的特性，但其控制平面并不满足上述标准。这是因为 Linux 内核中的 zswap 在启用时仅在直接回收（即当主机内存节点耗尽内存时）时触发，并尝试压缩页面直到有足够的空间避免内存不足的情况，从而暂停应用程序的分配。这种机制存在以下缺点：（1）由于 zswap 解压缩导致的性能开销是无限的，（2）最后一刻的突发压缩开销对尾部延迟产生负面影响，损害 WSC 应用程序的服务级别指标（SLI），以及（3）内存节省直到机器完全饱和时才实现。事实上，我们在部署过程中确实评估了这种方法，但观察到应用程序性能的显著下降，对 TCO 产生了负面影响。 此外，WSC 的 DRAM 中的每一条数据并不都适合通过压缩来节省，当 zswap 选择压缩这些数据时，会导致浪费的周期机会成本。例如，内存中的视频数据可能不像文本数据那样可压缩。 有些不能压缩？ 因此，在本文中，我们设计了一个端到端的仓库级系统，该系统识别冷页并主动将其迁移到远端内存，同时将性能视为首要约束。关键问题是冷页有多冷？或者冷页的定义是什么？冷页识别算法的质量将影响内存节省和应用程序影响。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#challenges-in-software-defined-far-memory"},{"categories":["OS"],"collections":null,"content":"Cold Page Identification Mechanism 我们的目标是设计一个强大且有效的控制平面，用于大规模部署 zswap。与 Linux 内核中的 zswap 或其他交换机制一样，我们的系统在迁移近端内存（例如，DRAM）和远端内存（例如，zswap）之间的页面时，以操作系统页面粒度工作。这使得无需硬件修改即可采用远端内存。 与现有 zswap 机制的主要区别在于何时压缩页面，或何时将页面从近端内存迁移到远端内存。与 Linux 内核中的 zswap 不同，我们的系统在后台识别冷内存页并主动压缩它们，以便额外的空闲内存可以用于调度更多作业到机器上。一旦访问压缩页面，zswap 会解压缩页面并将其保持在解压缩状态，以避免反复产生解压缩开销。这些页面在将来变冷时再次有资格进行压缩。 压缩的是冷页面， 多出来的内存给谁用呢？ ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#cold-page-identification-mechanism"},{"categories":["OS"],"collections":null,"content":"Definition of Cold Pages 我们的系统根据每个页面上次访问后的时间（简称为 age）来识别冷页。当页面超过 T 秒未被访问时，它被认为是冷的。我们称 T 为 cold age threshold，它决定了系统识别冷内存页的激进程度。我们基于先前的工作[28, 42, 46]设计了这一机制。 冷年龄阈值对内存节省和性能开销有直接影响。过早地将页面分类为冷页可能会将它们映射到远端内存，导致性能下降。我们的系统试图找到满足给定性能约束的最低冷年龄阈值，以便在明确定义的 SLO 下最大化内存节省，我们将在下文讨论这一点。 T 怎么找，和 SLO 有关？ ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#definition-of-cold-pages"},{"categories":["OS"],"collections":null,"content":"Performance SLO for Far Memory 在 WSC 环境中，直接关联冷内存阈值对应用程序性能的影响是具有挑战性的，因为不同应用程序的性能指标具有多样性，这些指标本身可能从延迟敏感（例如，面向用户的网页前端）到吞吐量导向（例如，机器学习训练管道）不等。 Promotion rate 远端内存的性能开销来自于访问存储在远端内存中的页面（我们称这种操作为提升），由于远端内存中的页面一旦被访问就会迁移到近端内存，提升率等同于单位时间内访问的远端内存中唯一页面的数量。 Target promotion rate 不同的应用程序对提升率的性能敏感度不同。例如，在相同的绝对提升率水平下，小作业比大作业更有可能经历更高的性能开销，因为前者可能具有更高的远端内存访问比例。这需要一种方法来通过代表每个作业“大小”的指标来归一化绝对提升率。 为什么小作业可能更高性能开销？为什么具有更高的远端内存访问比例，是个 rate？ 因此，我们设计我们的系统，使提升率保持在每分钟应用程序工作集大小的 P% 以下，这作为远端内存性能的服务级别目标（SLO）。 我们将应用程序的工作集大小定义为在最小冷年龄阈值（在我们的系统中为 120 秒）内访问的总页面数。每分钟的工作集大小作为作业内存带宽使用的代理，根据我们的评估，这与作业对远端内存访问的性能敏感度相关。我们的 SLO 确保应用程序的工作集不超过 P%来自远端内存，从而限制远端内存的性能开销。P 的确切值取决于近端内存和远端内存之间的性能差异。对于我们的部署，我们进行了为期数月的 A/B 测试，并在生产工作负载中通过经验确定 P 为 0.2% / min。在这个目标提升率水平下，作业的压缩/解压缩率足够低，不会干扰同一机器上的其他共存作业。 cold memory 越大 promotion rate 越大， promotion rate：访问远端 cold memory 的频率 强制提升率低于目标值可以防止应用程序的突发解压缩，因为从定义上限制了解压缩速率。在极少数情况下，如果激进或相关的解压缩突发导致机器因解压缩压缩页面而耗尽内存，我们通过杀死并重新调度其他机器上的低优先级作业来选择性 eviction 。我们的 WSC 控制平面[40]为用户提供了一个 eviction SLO，在 18 个月的实际生产中从未被违反，同时我们实现了内存节省。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#performance-slo-for-far-memory"},{"categories":["OS"],"collections":null,"content":"Controlling the Cold Age Threshold 为了确定满足提升率 SLO 的最低 Cold Age Threshold，我们估计应用程序在不同冷年龄阈值下的提升率。为此，我们在操作系统内核中为每个作业构建一个 a promotion histogram ，其中，对于每个冷页阈值 T，我们记录总提升率，这些页面比阈值 T 更冷。 作一个例子，假设一个应用程序有两个内存页 A 和 B，分别在 5 分钟和 10 分钟前被访问，并且两个页面都在 1 分钟前再次被访问。在这种情况下，提升直方图返回 T = 8 分钟的 1 次提升/分钟，因为只有 B 在 T = 8 分钟时会被认为是冷的，当两个页面在一分钟前被访问时。类似地，它返回 T = 2 分钟的 2 次提升/分钟，因为现在 A 和 B 在 T = 2 分钟时都会被认为是冷的。我们在第 5.1 节中讨论了我们的实现。 为什么 T 一定要低呢 虽然提升直方图让我们选择了过去最好的冷年龄阈值，但它不一定是未来的最佳阈值。Ideally, the control algorithm has to give us a stable threshold over time so as to reduce unnecessary compression and decompression costs. 同时，系统必须对应用程序活动的突然峰值做出响应，并避免长时间过度压缩内存。因此，我们的系统基于以下原则控制阈值： 它跟踪过去每个 1 分钟周期内的最佳冷年龄阈值，并使用它们的 K 百分位数作为下一个 1 分钟的阈值。通过这样做，它在稳定状态下将违反 SLO 约(100 - K)% 的时间。 如果最后一分钟的冷年龄阈值高于过去的 K 百分位数（即作业在过去一分钟的冷内存访问量高于过去行为的 K 百分位数），我们使用前者，以便系统能够快速响应应用程序活动的突然增加。 由于算法依赖于每个作业的历史记录，我们在作业执行的前 S 秒内禁用 zswap，以避免基于不充分的信息做出决策。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#controlling-the-cold-age-threshold"},{"categories":["OS"],"collections":null,"content":"Calculating the Size of Cold Memory 我们机制的最后一部分是估计不同冷年龄阈值下的冷内存大小。为此，我们的系统为给定的一组预定义冷年龄阈值构建每个作业的冷页直方图。在这个直方图中，对于每个冷年龄阈值 T，我们记录至少 T 秒未被访问的页面数量。这些信息用于（1）估计作业的工作集大小（用于归一化提升率；见第 4.2 节）和（2）对不同冷年龄阈值下的潜在内存节省进行离线分析（第 5.3 节）。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#calculating-the-size-of-cold-memory"},{"categories":["OS"],"collections":null,"content":"System Implementation 在本节中，我们讨论了在操作系统内核和节点代理中实现的冷页识别机制，以及用于它的自动调优系统。我们基于 zswap（一个 readily available Linux feature）展示了我们的系统设计并进行了评估，但我们的设计可以推广到其他类型的远端内存技术，因为我们的控制平面不依赖于任何特定的远端内存设备。 图 4 展示了我们系统设计的高级图。在每台生产机器上，我们的定制 Linux 内核（第 5.1 节）和节点代理（第 5.2 节）调整冷年龄阈值并收集每个作业的统计信息。使用作业的历史数据，我们使用机器学习来调整节点代理的参数（第 5.3 节）。 不依赖硬件 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#system-implementation"},{"categories":["OS"],"collections":null,"content":"Kernel 我们使用 Linux 的内存 cgroup（memcg）[2]来隔离 WSC 中的作业。软件定义的远端内存构建在 zswap 之上。我们运行两个全机范围的内核守护进程，即 kstaled 和 kreclaimd，以收集远端内存统计信息并将页面移动到远端内存。我们首先讨论我们的 zswap 实现以及在我们的生产环境中进行非破坏性部署所需的修改。 cgroup https://tech.meituan.com/2015/03/31/cgroups.html 美团的文章特别好 stale d reclaim d zswap 我们通过几个针对 WSC 部署定制的功能增强了上游 Linux 的 zswap 实现。我们使用 lzo 算法来实现低 CPU 开销的压缩和解压缩。 一旦 zswap 压缩了一个页面，它就会分配内存来存储压缩的有效载荷。我们使用 zsmalloc 作为压缩数据区域。我们为每台机器维护一个全局的 zsmalloc 区域，并在需要时由节点代理触发显式压缩接口。虽然每个 memcg 的 zsmalloc 区域看起来更直观，因为我们用 memcg 封装了作业，但它会导致每个 zsmalloc 区域不同程度的外部碎片化，因为 WSC 通常每台机器打包数十或数百个作业。我们最初使用每个 memcg 的 zsmalloc 区域的研究发现，每天有数千个实例的区域碎片化到负收益的程度。 zsmalloc 是一个基于 slab 的内存分配器，旨在有效地存储各种压缩级别的页面。 它以最少的碎片量实现了最高的存储密度。 在低内存条件下工作良好。zsmalloc 只能分配大小不超过 PAGE_SIZE 的对象 https://docs.kernel.org/mm/zsmalloc.html memcg 是 Linux 操作系统中的内存控制组（memcg, memory control group），cgroup 为什么是 lzo？快 We compared several compression algorithms, including lzo, lz4, and snappy, and concluded that lzo shows the best trade-off between compression speed and efficiency. 根据经验，存储大于 2990 字节的 zsmalloc 有效载荷（4 KiB x86 页面的 73%）没有收益，其中元数据开销变得高于压缩页面的节省。当尝试压缩产生大于该有效载荷的页面时，我们将其标记为不可压缩并拒绝它。不可压缩状态阻止 zswap 尝试重新压缩该页面，并在 kstaled（见下文）检测到与该页面关联的任何 PTE 变脏时清除。 只压缩小页面？ 当作业达到其内存限制时，我们关闭 zswap 而不是将其用作交换设备。这是因为 WSC 应用程序更喜欢“快速失败”并在集群中的其他地方重新启动，依靠其容错特性[7]，而不是在内核模式下浪费 CPU 周期试图避免作业抢占。它还使 zswap 与集群范围调度器的典型行为一致，当它们耗尽内存时，调度器会杀死尽力而为的作业。 当机器耗尽内存时，内核将在故障进程的上下文中使用直接内存回收。节点代理为每个 memcg 维护一个“软”限制，相当于其工作集大小（使用第 4.2 节中的方法确定），内核不会在此阈值以下回收。这保护了高优先级作业的工作集，并防止回收作业线程花费过多的周期进行 zswap，同时强化了低优先级作业“快速失败”的偏好。 kstaled 我们定期扫描页表项中存在的访问位，以推断页面是否在给定时间段内被访问[4, 19]。我们利用 kstaled，一个内核守护进程 a kernel daemon，基于访问位信息跟踪所有有资格进行内存回收的物理页面的年龄[28]。 在每个扫描周期内，kstaled 遍历进程页表以读取每个物理页面的访问位。如果页面的访问位被设置，kstaled 将相应页面的年龄设置为零；否则，它增加年龄。它还清除访问位以检测页面的任何未来访问。如果物理页面映射在多个页表中，kstaled 仅在任何页表中访问位未设置时增加年龄 我们的 kstaled 版本将页面的年龄存储在 perpage metadata structure 中，以便其他使用页面访问模式的内核组件（如直接回收）可以利用 kstaled 已经收集的信息。我们使用每页 8 位来编码页面的年龄。由于我们将这些位打包在 Linux 内核中已经维护的 struct page 元数据结构中，因此我们不会为跟踪年龄产生任何存储开销。我们以 120 秒的频率运行 kstaled。使用 8 位年龄，我们可以跟踪长达 8.5 小时（= 255 × 120 秒）的年龄。 每当 kstaled 更新页面的年龄时，它还会更新两个每个作业的直方图：（1）冷年龄直方图，一个页面年龄的直方图，跟踪页面未被访问的时间 T；（2）提升直方图，记录页面被访问时的年龄。这些直方图导出到节点代理，并用于确定冷页年龄阈值，如第 4.3 节所述。 To minimize the CPU overhead of kstaled, we empirically tune its scan period while trading off for finer-grained page access information.平均而言，kstaled 作为优先级较低的后台任务运行时，消耗不到一个逻辑 CPU 核心的 11%。 empirically? kreclaimd 一旦节点代理使用 kstaled 构建的年龄直方图并设置冷年龄阈值，kreclaimd 将每个页面的年龄与页面所属作业的冷年龄阈值进行比较，并回收所有年龄超过阈值的页面。 我们通过将冷页面移动到 zswap 来回收 DRAM 中的冷页面，从而释放 DRAM 容量以服务热应用程序页面。访问时，压缩页面会被解压缩。kreclaimd 在任何其他应用程序未使用的空闲周期中运行，作为一个不引人注目的后台任务实现内存收益。 请注意，我们只考虑最近最少使用（LRU）列表[3]中的页面映射到远端内存。例如，如果页面被标记为不可驱逐或锁定在内存中（mlocked），我们不会将其映射到远端内存。这有助于我们避免浪费 CPU 周期在不可移动的页面上。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#kernel"},{"categories":["OS"],"collections":null,"content":"Kernel 我们使用 Linux 的内存 cgroup（memcg）[2]来隔离 WSC 中的作业。软件定义的远端内存构建在 zswap 之上。我们运行两个全机范围的内核守护进程，即 kstaled 和 kreclaimd，以收集远端内存统计信息并将页面移动到远端内存。我们首先讨论我们的 zswap 实现以及在我们的生产环境中进行非破坏性部署所需的修改。 cgroup https://tech.meituan.com/2015/03/31/cgroups.html 美团的文章特别好 stale d reclaim d zswap 我们通过几个针对 WSC 部署定制的功能增强了上游 Linux 的 zswap 实现。我们使用 lzo 算法来实现低 CPU 开销的压缩和解压缩。 一旦 zswap 压缩了一个页面，它就会分配内存来存储压缩的有效载荷。我们使用 zsmalloc 作为压缩数据区域。我们为每台机器维护一个全局的 zsmalloc 区域，并在需要时由节点代理触发显式压缩接口。虽然每个 memcg 的 zsmalloc 区域看起来更直观，因为我们用 memcg 封装了作业，但它会导致每个 zsmalloc 区域不同程度的外部碎片化，因为 WSC 通常每台机器打包数十或数百个作业。我们最初使用每个 memcg 的 zsmalloc 区域的研究发现，每天有数千个实例的区域碎片化到负收益的程度。 zsmalloc 是一个基于 slab 的内存分配器，旨在有效地存储各种压缩级别的页面。 它以最少的碎片量实现了最高的存储密度。 在低内存条件下工作良好。zsmalloc 只能分配大小不超过 PAGE_SIZE 的对象 https://docs.kernel.org/mm/zsmalloc.html memcg 是 Linux 操作系统中的内存控制组（memcg, memory control group），cgroup 为什么是 lzo？快 We compared several compression algorithms, including lzo, lz4, and snappy, and concluded that lzo shows the best trade-off between compression speed and efficiency. 根据经验，存储大于 2990 字节的 zsmalloc 有效载荷（4 KiB x86 页面的 73%）没有收益，其中元数据开销变得高于压缩页面的节省。当尝试压缩产生大于该有效载荷的页面时，我们将其标记为不可压缩并拒绝它。不可压缩状态阻止 zswap 尝试重新压缩该页面，并在 kstaled（见下文）检测到与该页面关联的任何 PTE 变脏时清除。 只压缩小页面？ 当作业达到其内存限制时，我们关闭 zswap 而不是将其用作交换设备。这是因为 WSC 应用程序更喜欢“快速失败”并在集群中的其他地方重新启动，依靠其容错特性[7]，而不是在内核模式下浪费 CPU 周期试图避免作业抢占。它还使 zswap 与集群范围调度器的典型行为一致，当它们耗尽内存时，调度器会杀死尽力而为的作业。 当机器耗尽内存时，内核将在故障进程的上下文中使用直接内存回收。节点代理为每个 memcg 维护一个“软”限制，相当于其工作集大小（使用第 4.2 节中的方法确定），内核不会在此阈值以下回收。这保护了高优先级作业的工作集，并防止回收作业线程花费过多的周期进行 zswap，同时强化了低优先级作业“快速失败”的偏好。 kstaled 我们定期扫描页表项中存在的访问位，以推断页面是否在给定时间段内被访问[4, 19]。我们利用 kstaled，一个内核守护进程 a kernel daemon，基于访问位信息跟踪所有有资格进行内存回收的物理页面的年龄[28]。 在每个扫描周期内，kstaled 遍历进程页表以读取每个物理页面的访问位。如果页面的访问位被设置，kstaled 将相应页面的年龄设置为零；否则，它增加年龄。它还清除访问位以检测页面的任何未来访问。如果物理页面映射在多个页表中，kstaled 仅在任何页表中访问位未设置时增加年龄 我们的 kstaled 版本将页面的年龄存储在 perpage metadata structure 中，以便其他使用页面访问模式的内核组件（如直接回收）可以利用 kstaled 已经收集的信息。我们使用每页 8 位来编码页面的年龄。由于我们将这些位打包在 Linux 内核中已经维护的 struct page 元数据结构中，因此我们不会为跟踪年龄产生任何存储开销。我们以 120 秒的频率运行 kstaled。使用 8 位年龄，我们可以跟踪长达 8.5 小时（= 255 × 120 秒）的年龄。 每当 kstaled 更新页面的年龄时，它还会更新两个每个作业的直方图：（1）冷年龄直方图，一个页面年龄的直方图，跟踪页面未被访问的时间 T；（2）提升直方图，记录页面被访问时的年龄。这些直方图导出到节点代理，并用于确定冷页年龄阈值，如第 4.3 节所述。 To minimize the CPU overhead of kstaled, we empirically tune its scan period while trading off for finer-grained page access information.平均而言，kstaled 作为优先级较低的后台任务运行时，消耗不到一个逻辑 CPU 核心的 11%。 empirically? kreclaimd 一旦节点代理使用 kstaled 构建的年龄直方图并设置冷年龄阈值，kreclaimd 将每个页面的年龄与页面所属作业的冷年龄阈值进行比较，并回收所有年龄超过阈值的页面。 我们通过将冷页面移动到 zswap 来回收 DRAM 中的冷页面，从而释放 DRAM 容量以服务热应用程序页面。访问时，压缩页面会被解压缩。kreclaimd 在任何其他应用程序未使用的空闲周期中运行，作为一个不引人注目的后台任务实现内存收益。 请注意，我们只考虑最近最少使用（LRU）列表[3]中的页面映射到远端内存。例如，如果页面被标记为不可驱逐或锁定在内存中（mlocked），我们不会将其映射到远端内存。这有助于我们避免浪费 CPU 周期在不可移动的页面上。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#zswap"},{"categories":["OS"],"collections":null,"content":"Kernel 我们使用 Linux 的内存 cgroup（memcg）[2]来隔离 WSC 中的作业。软件定义的远端内存构建在 zswap 之上。我们运行两个全机范围的内核守护进程，即 kstaled 和 kreclaimd，以收集远端内存统计信息并将页面移动到远端内存。我们首先讨论我们的 zswap 实现以及在我们的生产环境中进行非破坏性部署所需的修改。 cgroup https://tech.meituan.com/2015/03/31/cgroups.html 美团的文章特别好 stale d reclaim d zswap 我们通过几个针对 WSC 部署定制的功能增强了上游 Linux 的 zswap 实现。我们使用 lzo 算法来实现低 CPU 开销的压缩和解压缩。 一旦 zswap 压缩了一个页面，它就会分配内存来存储压缩的有效载荷。我们使用 zsmalloc 作为压缩数据区域。我们为每台机器维护一个全局的 zsmalloc 区域，并在需要时由节点代理触发显式压缩接口。虽然每个 memcg 的 zsmalloc 区域看起来更直观，因为我们用 memcg 封装了作业，但它会导致每个 zsmalloc 区域不同程度的外部碎片化，因为 WSC 通常每台机器打包数十或数百个作业。我们最初使用每个 memcg 的 zsmalloc 区域的研究发现，每天有数千个实例的区域碎片化到负收益的程度。 zsmalloc 是一个基于 slab 的内存分配器，旨在有效地存储各种压缩级别的页面。 它以最少的碎片量实现了最高的存储密度。 在低内存条件下工作良好。zsmalloc 只能分配大小不超过 PAGE_SIZE 的对象 https://docs.kernel.org/mm/zsmalloc.html memcg 是 Linux 操作系统中的内存控制组（memcg, memory control group），cgroup 为什么是 lzo？快 We compared several compression algorithms, including lzo, lz4, and snappy, and concluded that lzo shows the best trade-off between compression speed and efficiency. 根据经验，存储大于 2990 字节的 zsmalloc 有效载荷（4 KiB x86 页面的 73%）没有收益，其中元数据开销变得高于压缩页面的节省。当尝试压缩产生大于该有效载荷的页面时，我们将其标记为不可压缩并拒绝它。不可压缩状态阻止 zswap 尝试重新压缩该页面，并在 kstaled（见下文）检测到与该页面关联的任何 PTE 变脏时清除。 只压缩小页面？ 当作业达到其内存限制时，我们关闭 zswap 而不是将其用作交换设备。这是因为 WSC 应用程序更喜欢“快速失败”并在集群中的其他地方重新启动，依靠其容错特性[7]，而不是在内核模式下浪费 CPU 周期试图避免作业抢占。它还使 zswap 与集群范围调度器的典型行为一致，当它们耗尽内存时，调度器会杀死尽力而为的作业。 当机器耗尽内存时，内核将在故障进程的上下文中使用直接内存回收。节点代理为每个 memcg 维护一个“软”限制，相当于其工作集大小（使用第 4.2 节中的方法确定），内核不会在此阈值以下回收。这保护了高优先级作业的工作集，并防止回收作业线程花费过多的周期进行 zswap，同时强化了低优先级作业“快速失败”的偏好。 kstaled 我们定期扫描页表项中存在的访问位，以推断页面是否在给定时间段内被访问[4, 19]。我们利用 kstaled，一个内核守护进程 a kernel daemon，基于访问位信息跟踪所有有资格进行内存回收的物理页面的年龄[28]。 在每个扫描周期内，kstaled 遍历进程页表以读取每个物理页面的访问位。如果页面的访问位被设置，kstaled 将相应页面的年龄设置为零；否则，它增加年龄。它还清除访问位以检测页面的任何未来访问。如果物理页面映射在多个页表中，kstaled 仅在任何页表中访问位未设置时增加年龄 我们的 kstaled 版本将页面的年龄存储在 perpage metadata structure 中，以便其他使用页面访问模式的内核组件（如直接回收）可以利用 kstaled 已经收集的信息。我们使用每页 8 位来编码页面的年龄。由于我们将这些位打包在 Linux 内核中已经维护的 struct page 元数据结构中，因此我们不会为跟踪年龄产生任何存储开销。我们以 120 秒的频率运行 kstaled。使用 8 位年龄，我们可以跟踪长达 8.5 小时（= 255 × 120 秒）的年龄。 每当 kstaled 更新页面的年龄时，它还会更新两个每个作业的直方图：（1）冷年龄直方图，一个页面年龄的直方图，跟踪页面未被访问的时间 T；（2）提升直方图，记录页面被访问时的年龄。这些直方图导出到节点代理，并用于确定冷页年龄阈值，如第 4.3 节所述。 To minimize the CPU overhead of kstaled, we empirically tune its scan period while trading off for finer-grained page access information.平均而言，kstaled 作为优先级较低的后台任务运行时，消耗不到一个逻辑 CPU 核心的 11%。 empirically? kreclaimd 一旦节点代理使用 kstaled 构建的年龄直方图并设置冷年龄阈值，kreclaimd 将每个页面的年龄与页面所属作业的冷年龄阈值进行比较，并回收所有年龄超过阈值的页面。 我们通过将冷页面移动到 zswap 来回收 DRAM 中的冷页面，从而释放 DRAM 容量以服务热应用程序页面。访问时，压缩页面会被解压缩。kreclaimd 在任何其他应用程序未使用的空闲周期中运行，作为一个不引人注目的后台任务实现内存收益。 请注意，我们只考虑最近最少使用（LRU）列表[3]中的页面映射到远端内存。例如，如果页面被标记为不可驱逐或锁定在内存中（mlocked），我们不会将其映射到远端内存。这有助于我们避免浪费 CPU 周期在不可移动的页面上。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#kstaled"},{"categories":["OS"],"collections":null,"content":"Kernel 我们使用 Linux 的内存 cgroup（memcg）[2]来隔离 WSC 中的作业。软件定义的远端内存构建在 zswap 之上。我们运行两个全机范围的内核守护进程，即 kstaled 和 kreclaimd，以收集远端内存统计信息并将页面移动到远端内存。我们首先讨论我们的 zswap 实现以及在我们的生产环境中进行非破坏性部署所需的修改。 cgroup https://tech.meituan.com/2015/03/31/cgroups.html 美团的文章特别好 stale d reclaim d zswap 我们通过几个针对 WSC 部署定制的功能增强了上游 Linux 的 zswap 实现。我们使用 lzo 算法来实现低 CPU 开销的压缩和解压缩。 一旦 zswap 压缩了一个页面，它就会分配内存来存储压缩的有效载荷。我们使用 zsmalloc 作为压缩数据区域。我们为每台机器维护一个全局的 zsmalloc 区域，并在需要时由节点代理触发显式压缩接口。虽然每个 memcg 的 zsmalloc 区域看起来更直观，因为我们用 memcg 封装了作业，但它会导致每个 zsmalloc 区域不同程度的外部碎片化，因为 WSC 通常每台机器打包数十或数百个作业。我们最初使用每个 memcg 的 zsmalloc 区域的研究发现，每天有数千个实例的区域碎片化到负收益的程度。 zsmalloc 是一个基于 slab 的内存分配器，旨在有效地存储各种压缩级别的页面。 它以最少的碎片量实现了最高的存储密度。 在低内存条件下工作良好。zsmalloc 只能分配大小不超过 PAGE_SIZE 的对象 https://docs.kernel.org/mm/zsmalloc.html memcg 是 Linux 操作系统中的内存控制组（memcg, memory control group），cgroup 为什么是 lzo？快 We compared several compression algorithms, including lzo, lz4, and snappy, and concluded that lzo shows the best trade-off between compression speed and efficiency. 根据经验，存储大于 2990 字节的 zsmalloc 有效载荷（4 KiB x86 页面的 73%）没有收益，其中元数据开销变得高于压缩页面的节省。当尝试压缩产生大于该有效载荷的页面时，我们将其标记为不可压缩并拒绝它。不可压缩状态阻止 zswap 尝试重新压缩该页面，并在 kstaled（见下文）检测到与该页面关联的任何 PTE 变脏时清除。 只压缩小页面？ 当作业达到其内存限制时，我们关闭 zswap 而不是将其用作交换设备。这是因为 WSC 应用程序更喜欢“快速失败”并在集群中的其他地方重新启动，依靠其容错特性[7]，而不是在内核模式下浪费 CPU 周期试图避免作业抢占。它还使 zswap 与集群范围调度器的典型行为一致，当它们耗尽内存时，调度器会杀死尽力而为的作业。 当机器耗尽内存时，内核将在故障进程的上下文中使用直接内存回收。节点代理为每个 memcg 维护一个“软”限制，相当于其工作集大小（使用第 4.2 节中的方法确定），内核不会在此阈值以下回收。这保护了高优先级作业的工作集，并防止回收作业线程花费过多的周期进行 zswap，同时强化了低优先级作业“快速失败”的偏好。 kstaled 我们定期扫描页表项中存在的访问位，以推断页面是否在给定时间段内被访问[4, 19]。我们利用 kstaled，一个内核守护进程 a kernel daemon，基于访问位信息跟踪所有有资格进行内存回收的物理页面的年龄[28]。 在每个扫描周期内，kstaled 遍历进程页表以读取每个物理页面的访问位。如果页面的访问位被设置，kstaled 将相应页面的年龄设置为零；否则，它增加年龄。它还清除访问位以检测页面的任何未来访问。如果物理页面映射在多个页表中，kstaled 仅在任何页表中访问位未设置时增加年龄 我们的 kstaled 版本将页面的年龄存储在 perpage metadata structure 中，以便其他使用页面访问模式的内核组件（如直接回收）可以利用 kstaled 已经收集的信息。我们使用每页 8 位来编码页面的年龄。由于我们将这些位打包在 Linux 内核中已经维护的 struct page 元数据结构中，因此我们不会为跟踪年龄产生任何存储开销。我们以 120 秒的频率运行 kstaled。使用 8 位年龄，我们可以跟踪长达 8.5 小时（= 255 × 120 秒）的年龄。 每当 kstaled 更新页面的年龄时，它还会更新两个每个作业的直方图：（1）冷年龄直方图，一个页面年龄的直方图，跟踪页面未被访问的时间 T；（2）提升直方图，记录页面被访问时的年龄。这些直方图导出到节点代理，并用于确定冷页年龄阈值，如第 4.3 节所述。 To minimize the CPU overhead of kstaled, we empirically tune its scan period while trading off for finer-grained page access information.平均而言，kstaled 作为优先级较低的后台任务运行时，消耗不到一个逻辑 CPU 核心的 11%。 empirically? kreclaimd 一旦节点代理使用 kstaled 构建的年龄直方图并设置冷年龄阈值，kreclaimd 将每个页面的年龄与页面所属作业的冷年龄阈值进行比较，并回收所有年龄超过阈值的页面。 我们通过将冷页面移动到 zswap 来回收 DRAM 中的冷页面，从而释放 DRAM 容量以服务热应用程序页面。访问时，压缩页面会被解压缩。kreclaimd 在任何其他应用程序未使用的空闲周期中运行，作为一个不引人注目的后台任务实现内存收益。 请注意，我们只考虑最近最少使用（LRU）列表[3]中的页面映射到远端内存。例如，如果页面被标记为不可驱逐或锁定在内存中（mlocked），我们不会将其映射到远端内存。这有助于我们避免浪费 CPU 周期在不可移动的页面上。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#kreclaimd"},{"categories":["OS"],"collections":null,"content":"Node Agent 每台机器上运行的节点代理（在我们的集群管理系统中称为 Borglet [40]）动态控制每个作业的冷年龄阈值。使用第 4.3 节中描述的算法，它通过每分钟读取内核统计信息并计算过去一分钟的最低冷年龄阈值（不违反目标提升率），构建过去最佳冷年龄阈值的池。然后，它在每个作业执行开始后的 S 秒内启用 zswap，将阈值设置为池中下一个一分钟的 K 百分位数。节点代理定期将每个作业的冷内存统计信息从内核导出到外部数据库，以促进离线分析和监控。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#node-agent"},{"categories":["OS"],"collections":null,"content":"ML-based Autotuner 我们的系统将前面讨论的 K 和 S 作为可调参数，以调整控制平面的激进程度。然而，手动一次性调整这些参数涉及在生产系统中进行多次迭代和 A/B 测试，这既冒险又耗时，并且容易受到工作负载行为随时间变化的影响。 据我们所知，这是首次将 GP Bandit 用于优化 WSC。 K 代表冷年龄阈值的百分位数 S 代表作业执行开始后的等待时间（以秒为单位），在这段时间内禁用 zswap。 省略 ML 内容，不过用 Bandit 调优 WSC 的想法真的很厉害，这一篇论文非常精彩，都是很简单的想法，但是效果很不错 不论是 lzo, zswap, cgroup 都是现有的，还是 bandit 调优，也都是存在很久的方法 估计也就 google 团队能遇到 wsc 中的现实问题，特定问题 特定解法，很 practical ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:7:3","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#ml-based-autotuner"},{"categories":["OS"],"collections":null,"content":"Evaluation cold memory coverage as a metric 我们将冷内存覆盖率定义为存储在远端内存（即压缩）中的总内存大小除以在最低可能冷年龄阈值（在我们的系统中为 120 秒）下的总冷内存大小。从概念上讲，这是存储在远端内存中的冷内存的百分比，并暗示我们的系统与上界有多接近，即所有在 120 秒或更长时间内未被访问的页面都可以存储在远端内存中而不会导致性能下降。 图 5 显示了随时间变化的冷内存覆盖率的机群范围平均值，并标注了相关的时间线。第一阶段（A 到 B）部署了 zswap，其静态参数值由有限的小规模实验提供。然后，在第二阶段（C 到 D），我们推出了我们的自动调优器及其参数值建议。 cold mem coverage 越高，意味着越多内存能释放出来提供调用 图 6 显示了前 10 个最大集群中机器的冷内存覆盖率分布。与第 2.2 节中的冷内存分析一样，我们观察到不同机器之间的冷内存覆盖率范围很大，即使在同一集群内也是如此。这展示了 zswap 在远端内存容量方面的灵活性优势。 不同任务的集群，coverage 表现也不错 虽然冷内存覆盖率随机器和时间变化，但集群级别的比率一直稳定，这使我们能够将 zswap 的冷内存覆盖率转换为更低的内存配置。在冷内存覆盖率为 20%，冷内存比率的上限为 32%（图 1），以及压缩页面的成本降低 67%（第 6.3 节）的情况下，我们的系统以透明的方式实现了 4-5%的 DRAM TCO 减少。这些节省是在性能 SLI 没有差异的情况下实现的，我们将在下文讨论。 SLI, indicator，指标 SLO, objective 基于 SLI，承诺质量，目标 SLA, agreegment 客户协议 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#evaluation"},{"categories":["OS"],"collections":null,"content":"Cold Memory Coverage ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#cold-memory-coverage"},{"categories":["OS"],"collections":null,"content":"Performance Impact 我们使用两个指标来衡量我们系统的性能影响：提升率和 CPU 开销。前者是远端内存的性能 SLI（第 4 节），可以推广到其他类型的远端内存设备。 CPU 开销显示了使用 zswap 作为远端内存所消耗的周期。 promotion rate 在我看来倒是很难直观理解 我们通过几个月的 A/B 实验手动确定了 K 和 S 的值，这些实验来自我们根据经验猜测的几个候选配置。我们观察到两种情况下的提升率都非常低；提升率的第 98 百分位数每分钟低于工作集大小的 0.2%。这表明我们的冷页识别机制准确地将不常访问的页面分类为远端内存的有效候选者。 This demonstrates that our cold page identification mechanism accurately classifies infrequently accessed pages to be effective candidates for far memory 维持提升率低，就说明是真的压缩了那些 least 访问的 图的纵坐标很奇怪是 CDF of Job count，横坐标反而是 promotion rate 或者 CPU overhead 而且同样 cdf，promotion rate ML 方法增加了，说明还是手选好？但提升不多，说明训练拟合还行 压缩带来的 cpu overhead 也不是很多，都在 0.10% 以下 图 8 还表明，我们系统的机器级 CPU 开销也非常低，从 TCO 的角度来看，与整个 WSC 平均 20%的冷内存覆盖率（图 5）相比，这种 CPU 开销可以忽略不计。 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#performance-impact"},{"categories":["OS"],"collections":null,"content":"Compression Characteristics zswap 给系统带来了两种额外的成本。首先，压缩页面仍然存储在 DRAM 中，这使得实际内存节省取决于数据的压缩比。其次，压缩页面按需解压缩，这会在访问压缩页面时产生性能开销。本小节根据从整个 WSC 收集的统计数据量化这两个方面。图 9a 展示了每个作业中压缩页面的平均压缩比分布，不包括不可压缩页面，这些页面平均占冷内存的 31%。尽管 zswap 使用轻量级压缩算法来最小化其 CPU 开销，但它在中位数上实现了 3 倍的压缩比（即 67%的内存节省）。压缩比从 2 倍到 6 倍不等，这取决于应用程序特性。例如，多媒体数据和加密的终端用户内容即使在冷状态下也是不可压缩的。 视频处理的集群呢？ Decomp. Latency (μs) 不是很高，都是个位的 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:8:3","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#compression-characteristics"},{"categories":["OS"],"collections":null,"content":"Case Study with Bigtable 最后，我们通过一个案例研究来量化我们的远端内存系统对应用程序级别性能指标（例如，每周期指令数[45]）的影响。我们的目标应用程序是 Bigtable[10]，这是我们 DRAM 的最大消费者之一，它将 PB 级的数据存储在存储中，并以内存缓存的方式为许多生产服务提供每秒数百万次操作的服务。 找个时间看 bigtable 但没懂这里和 bigtable 结合有什么显著区别， IPC 几乎不变，没有影响？ 但是冷内存覆盖率还有 5-15% 表示也有提升，但是我想看更多 metrics，延迟，成本，为什么没有呢 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:8:4","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#case-study-with-bigtable"},{"categories":["OS"],"collections":null,"content":"Related Work ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#related-work"},{"categories":["OS"],"collections":null,"content":"Conclusion 不需要硬件、不需要硬件远端内存、不需要可持久化内存 用 age 来追踪，而不是用数位等等 优点蛮多的，非常厉害的一篇论文 google 可能更注重不影响 SLA、性能和可靠性，这都是很 practical 的 至于为什么混入一个 ML 挺奇怪的其实。。可能那时候流行吧 ","date":"2024-10-30","objectID":"/posts/paper-far-memory/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Software-Defined Far Memory in Warehouse-Scale Computers","uri":"/posts/paper-far-memory/#conclusion"},{"categories":["OS"],"collections":null,"content":"Beyond malloc efficiency to fleet efficiency: a hugepage-aware memory allocator tcmalloc 是 google 开发的高性能内存分配器 thread-caching malloc，而 temeraire 是本文提出的，改进 tcmalloc，改进大页分配减少内存碎片。https://github.com/google/tcmalloc 简单来说，内存管理如 malloc 在堆上分配内存，使用一个内存池（通常是链表数据结构）管理已分配和未分配的内存块 分配时，找一个足够大的空闲块分配给用户，否则向系统申请更多内存，malloc 一般会返回对齐的地址的指针 但 malloc 存在内存碎片的问题，内部和外部碎片，比如 malloc(1) 但其实会分配更大的内存块（对齐），所以有内部碎片 外部碎片：10k(free) | 5k (busy) | 100k (free) 这时候申请 15k 内存，第一段不连续用不了 至于 brk() 之类的系统调用可以看一些经典八股理解 小林 coding： https://xiaolincoding.com/os/3_memory/malloc.html 【操作系统】malloc、free 实现原理： https://imageslr.com/2020/malloc.html malloc 存在不少问题，不仅是内存碎片的问题，malloc 在多线程环境下表现也不佳，需要用到锁来保护内存分配和释放并避免竞争。而 tcmalloc 采用了线程本地缓存 (Thread Local Cache) 来减少锁竞争，对于小内存块，直接从缓存分配。 Go 就采用了类似 TCmalloc 的设计来管理内存 http://legendtkl.com/2015/12/11/go-memory/ tcmalloc: https://goog-perftools.sourceforge.net/doc/tcmalloc.html 图解：https://zhuanlan.zhihu.com/p/29216091 【性能】tcmalloc 使用和原理：https://www.cnblogs.com/bandaoyu/p/16752421.html 至于 jemalloc, ptmalloc, tcmalloc 的区别，可以看看 https://wenfh2020.com/2021/11/14/question-design-memory-pool/ tcmalloc 使用自旋锁分配大内存，但这也有问题，大内存较多的情况下？所以本文提出 TEMERAIRE 来优化。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#beyond-malloc-efficiency-to-fleet-efficiency-a-hugepage-aware-memory-allocator"},{"categories":["OS"],"collections":null,"content":"Abstract Memory allocation represents significant compute cost at the warehouse scale 一种经典的优化方法是提高分配器的效率，以最小化分配器代码所消耗的周期。 然而，内存分配决策还通过数据布局影响整体应用程序性能，提供了通过使用更少的硬件资源完成更多应用程序工作来提高整个 fleetwide 生产力的机会。在此，我们聚焦于大页覆盖率 hugepage coverage。我们提出了 TEMERAIRE，这是 TCMALLOC 的一个大页感知增强版本，旨在减少应用程序代码中的 CPU 开销。我们讨论了 TEMERAIRE 的设计与实现，包括大页感知内存布局的策略，以最大化大页覆盖率并最小化碎片 fragmentation 开销。我们展示了针对 8 个应用程序的研究，将每秒请求数（RPS）提高了 7.7%，并将 RAM 使用量减少了 2.4%。 我们还展示了在机群 fleet 规模上进行的 1% 实验结果以及在谷歌仓库 warehouse 规模计算机中的长期部署情况。这带来了 6% 的 TLB 未命中停滞减少，以及 26% 的由于碎片导致的内存浪费减少。最后，我们讨论了改进分配器开发过程的额外技术以及未来内存分配器的潜在优化策略。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction datacenter tax: within a warehouse-scale computer (WSC) is the cumulative time spent on common service overheads, such as serialization, RPC communication, compression, copying, and memory allocation WSC 工作负载的多样性[23]意味着我们通常无法通过优化单个应用程序来显著提高整个系统的效率，因为成本分散在许多独立的工作负载上。 相比之下，专注于数据中心税的组成部分可以在整体上实现显著的性能和效率提升，因为这些优化可以应用于整个类别的应用程序。过去几年中，我们的团队专注于minimizing the cost of memory allocation decisions，取得了显著成效 通过大幅减少内存分配所花费的时间，实现了整个系统的收益。但我们不仅能够优化这些组成部分的成本。通过改变 allocator 来提高应用程序代码的效率，也能带来显著的收益。本文中，我们考虑如何通过提高内存分配器提供的 hugepage coverage 来优化应用程序性能。 Cache and Translation Lookaside Buffer (TLB) misses 是现代系统中主要的性能开销。在 WSC 中，内存墙[44]问题显著：一项分析[23]显示，50% cycles 因内存停滞。我们自己的工作负载分析发现，大约 20% 的周期因 TLB 未命中而停滞。 Hugepages are a processor feature, 可以显著减少 TLB 未命中的次数及其成本 大页的增加大小使得相同数量的 TLB 条目能够映射更大范围的内存。在所研究的系统中，大页还减少了未命中+填充的总停滞时间，因为它们的页表表示需要遍历的层级少了一层。 大页，如果 TLB 映射 2MB 的页面，1G 只需要 512 个页面，减少了映射数量 虽然分配器无法修改用户代码访问的内存量，甚至无法改变访问对象的模式，但它可以与操作系统合作并控制新分配的内存布局。通过优化大页覆盖率，分配器可以减少 TLB 未命中。在 C 和 C++等语言中，内存布局决策还必须处理其决策是最终的后果：对象一旦分配就无法移动[11]。分配布局决策只能在分配时进行优化。这种方法与我们在该领域的先前工作相反，因为我们可能会增加分配的 CPU 成本，增加数据中心税，但通过减少其他地方的处理器停滞来弥补。这提高了应用程序指标，如每秒请求数（RPS）。 TEMERAIRE 的设计：我们设计了 TEMERAIRE，这是 TCMALLOC 的一个大页感知增强版本，旨在减少应用程序代码中的 CPU 开销。我们提出了大页感知内存布局的策略，以最大化大页覆盖率并最小化碎片开销。 在复杂真实世界应用和 WSC 规模中的评估：我们在 WSC 中对 TEMERAIRE 进行了评估，测量了在我们的基础设施中运行的 8 个应用程序的样本，观察到每秒请求数（RPS）增加了 7.7%，RAM 使用量减少了 2.4%。将这些技术应用于谷歌 WSC 中的所有应用程序，带来了 6%的 TLB 未命中停滞减少，以及 26%的由于碎片导致的内存浪费减少。 优化内存分配器改进开发过程的策略：我们使用跟踪、遥测和在仓库规模上的实验相结合的方法，提出了优化内存分配器改进开发过程的策略。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#introduction"},{"categories":["OS"],"collections":null,"content":"The challenges of coordinating Hugepages 虚拟内存需要通过称为 Translation Looka- side Buffers (TLBs) 的缓存将用户空间地址转换为物理地址 TLB 的条目数量有限，对于许多应用程序，整个 TLB 仅覆盖使用默认页面大小的总内存占用的一小部分。现代处理器通过支持 TLB 中的大页来增加这种覆盖范围。一个对齐的大页（在 x86 上通常为 2MiB）仅占用一个 TLB 条目。大页通过增加 TLB 的有效容量并减少 TLB 未命中来减少停滞 传统的分配器 manage memory in page-sized chunks。 对于将内存释放回操作系统的分配器（在仓库规模中，我们拥有长时间运行的具有动态工作周期的负载，这是必要的）来说，面临更大的挑战。Transparent Huge Pages (THP) 为内核提供了机会，使其能够在页表中使用大页覆盖连续的页面。表面上看，内存分配器只需分配对齐且大小为大页的内存块即可利用这种支持。 非大页对齐的内存区域的返回需要内核使用较小的页面来表示剩余部分，从而削弱了内核提供大页的能力，并对剩余使用的页面施加了性能成本。 比如用户申请 3MB，其中大页可以给 2MB 剩下的非对齐，用小页 或者，分配器可以在整个大页变为空闲之前不将其返回给操作系统。这保留了大页覆盖，但相对于实际使用情况，可能会导致显著的放大效应，使内存闲置。DRAM 是部署 WSC 的显著成本[27]。分配器在管理外部碎片（即块中太小而无法用于请求分配的未使用空间）方面在这个过程中非常重要。例如，考虑图 1 中的分配。经过这一系列分配后，有 2 个单位的空闲空间。选择是使用小页，这会导致较低的碎片化但 TLB 条目使用效率较低，或者使用大页，这具有 TLB 效率但碎片化较高。 图一，大页带来的碎片 通过密集地将分配与大页边界对齐，优先使用分配的大页，并（理想情况下）以相同的对齐方式返回未使用的内存，来与其结果合作。大页感知的分配器有助于在用户级别管理内存连续性。目标是最大限度地将分配打包到接近满的大页上，反之亦然，最小化在空（或较空）大页上使用的空间，以便它们可以作为完整的大页返回给操作系统。这有效地使用内存，并与内核的透明大页支持良好交互。此外，更一致地分配和释放大页形成了一个积极的反馈循环：减少内核级别的碎片化，并提高未来分配由大页支持的可能性。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#the-challenges-of-coordinating-hugepages"},{"categories":["OS"],"collections":null,"content":"Overview of TCMALLOC TCMALLOC 是一种在大规模应用程序中使用的内存分配器，常见于 WSC 环境中。它展示了强大的性能[21]。我们的设计直接建立在 TCMALLOC 的结构上。 图 2 展示了 TCMALLOC 中内存的组织方式。对象按大小进行隔离。首先，TCMALLOC 将内存划分为对齐 page size 的 spans。 TCMALLOC 的结构由其对驱动任何内存分配器的两个相同问题的回答所定义。 我们如何选择对象大小并组织元数据以最小化空间开销和碎片化？ 我们如何可扩展地支持并发分配？ 足够大的分配由仅包含分配对象的 span 来满足。其他 span 包含多个相同大小（sizeclass）的较小对象。“小”对象大小的界限是 256 KiB。在这个“小”阈值内，分配请求被向上舍入到 100 个 sizeclass 之一。 spans 大小怎么定义？小对象是什么 这里的 对象 object 指的是内存吗 TCMALLOC 将对象存储在一系列缓存中，如图 3 所示。span 从简单的 pageheap 中分配，pageheap 跟踪所有未使用的页面并进行最佳适应分配。 pageheap 还负责在可能的情况下将不再需要的内存返回给操作系统。 与其在 free()路径上执行此操作，不如定期调用一个专用的 release-memory 方法，旨在维持一个可配置的稳定释放速率（MB/s）。这是一种 heuristic 方法，TCMALLOC 希望在稳态下尽可能少地使用内存，避免使用先前提供的内存来省略昂贵的系统分配。 定期释放，GC？那需要 stop the world 吗 理想情况下，TCMALLOC 会返回用户代码将不再需要的所有内存。内存需求变化不可预测，这使得在同时保留内存以避免系统调用和页面错误的情况下，返回将未使用的内存变得具有挑战性。关于内存返回策略的更好决策具有很高的价值，并在第 7 节中进行了讨论。 TCMALLOC 首先尝试从“本地”缓存中提供分配，类似于大多数现代分配器[9,12,20,39]。最初这些是所谓的每个线程缓存（per-Thread Caches），为每个 sizeclass 存储一个空闲对象列表。为了减少孤立内存并提高高度线程化应用程序的重用率，TCMALLOC 现在使用每个超线程本地缓存。 当本地缓存没有适当 sizeclass 的对象来满足请求（或在尝试释放后有太多对象）时，请求会路由到该 sizeclass 的单个中央缓存。这有两个组成部分——一个小的快速、受互斥锁保护的传输缓存 transfer cache（包含该 sizeclass 的扁平数组对象）和一个大的、受互斥锁保护的中央空闲列表 central freelist，包含分配给该 sizeclass 的所有 span ；可以从这些 span 中获取对象，也可以将对象返回给这些 span 。当一个 span 中的所有对象都返回到中央空闲列表中的 span 时，该 span 将返回到 pageheap。 Sizeclass 是指内存分配器根据不同大小的内存块对内存进行分类的方法。在 TCMALLOC 中，sizeclass 用于将内存块划分为特定大小的类别，每个类别对应一定范围的内存块大小。 举个例子： Sizeclass 1: 管理 8 字节的内存块。 Sizeclass 2: 管理 16 字节的内存块。 Sizeclass 3: 管理 32 字节的内存块。 Sizeclass 4: 管理 64 字节的内存块。 在我们的 WSC 中，大多数分配都很小（50%的分配空间是 ≤ 8192 字节的对象），如图 4 所示。这些对象随后被聚合到跨度中。pageheap 主要分配 1 页或 2 页的跨度，如图 5 所示。80%的跨度小于一个大页。 “stack” 缓存的设计使系统具有有用的模块化特性，TCMALLOC 的 pageheap 有一个简单的接口来管理内存，New(N)，Delete(S)，Release(N) ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#overview-of-tcmalloc"},{"categories":["OS"],"collections":null,"content":"TEMERAIRE’s approach TEMERAIRE 是本文对 TCMALLOC 的贡献，它用一个旨在最大限度填充（和清空）大页的设计替换了 pageheap。 我们开发了启发式方法，将分配密集地打包到高使用率的大页上，并同时形成完全未使用的大页以返回给操作系统。 Slack 是指分配请求的大小与下一个完整大页之间的差距。从操作系统分配的虚拟地址空间在没有 reserving physical memory 时是 unbacked 的。在使用时，它由操作系统支持，通过物理内存映射。我们可以再次将内存释放给操作系统，使其再次 unbacked。我们主要在大页 boundaries 内打包，但使用大页 regions 来跨大页边界打包分配。 slack 比如申请 5MB 得到 3 个 2MB 大页，gap 就是 1MB 申请完成后，物理内存还没有分配，处于 unbacked 状态，当用的时候才映射 Total memory demand varies unpredictably with time, but not every allocation is released. Completely draining hugepages implies packing memory at hugepage granularity. Draining hugepages gives us new release decision points Mistakes are costly, but work is not. 我们的分配器通过委托给几个子组件来实现其接口，如图 6 所示。每个组件都牢记上述原则，并针对其处理的最佳分配类型进行专门化。根据原则#4，we emphasize smart placement over speed 虽然 TEMERAIRE 的具体实现与 TCMALLOC 内部结构相关，但大多数现代分配器共享类似的页面（或更高）粒度的大后备分配，如 TCMALLOC 的跨度：比较 jemalloc 的“extents”[20]，Hoard 的“superblocks”[9]，和 mimalloc 的“pages”[29]。Hoard 的 8KB superblocks 直接通过‘mmap‘分配，防止大页连续性。这些 superblocks 可以改为密集地打包到大页上。mimalloc 将其 64KiB+的“pages”放置在“segments”中，但这些是按线程维护的，这阻碍了进程段之间的密集打包。急切地将页面返回给操作系统可以最小化这里的 RAM 成本，但会分解大页。这些分配器也可以从类似 TEMERAIRE 的大页感知分配器中受益。 jemalloc, hoard 都是很好的内存分配器 jemalloc 也采用了 temeraire 的策略，采用了启发式的避免碎片，但不完全一样 https://github.com/jemalloc/jemalloc/issues/2301 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#temeraires-approach"},{"categories":["OS"],"collections":null,"content":"The overall algorithm 图 7 分配算法， 如果大于 1G，调用 HugeCache，slack 不重要 如果小于 1MB 采用 HugeFiller，调用大页 如果 1M-2M 之间，尝试 HugeFiller 复用内存，处理小请求，打包到 hugepage 2M 之上的用 HugeRegion 来分配，如果不够就 HugeCache 分配大页 我们的目标是最大限度地减少生成的 slack，如果我们确实生成了 slack，则将其重用于其他分配（如同任何页面级别的碎片化一样）。 在所有组件背后是 HugeAllocator，它处理虚拟内存和操作系统。它为其他组件提供未支持的内存，这些组件可以支持并传递下去。我们还维护一个backed 完全空的大页缓存，称为 HugeCache HugeCache 处于 backed，原因是什么？ 我们保持一个部分填充的单个大页列表（HugeFiller），这些大页可以通过后续的小分配密集填充。在沿大页边界打包分配效率低下的地方，我们实现了一个专门的分配器（HugeRegion）。 对于 exact multiple of hugepage size 或那些足够大以至于 slack 无关紧要的分配，我们直接转发到 HugeCache。 中等大小的分配（介于 1MiB 和 1GiB 之间）通常也从 HugeCache 分配，最后一步是捐赠 slack。例如，从 HugeCache 分配的 4.5 MiB 会产生 1.5 MiB 的 slack，这是一个不可接受的高开销比率。TEMERAIRE 通过假装 the last hugepage of the request has a single “leading” allocation on it（图 8），将该 slack 捐赠给 HugeFiller。 当这样的大跨度被释放时，分配器还将标记为虚构的前导分配为空闲。如果 slack 未被使用，它将与剩余部分一起返回到尾部大页。否则，尾部大页将留在 HugeFiller 中，只有前 N-1 个大页返回到 HugeCache。 对于某些分配模式，中等大小的分配产生的 slack 比我们可以在严格的 2MiB 箱子中用较小分配填充的更多。例如，许多 1.1MiB 的分配每个大页将产生 0.9MiB 的 slack（见图 12）。当我们检测到这种模式时，HugeRegion 分配器跨大页边界放置分配，以最小化这种开销。 小请求（\u0026lt;= 1MiB）总是从 HugeFiller 提供。对于介于 1MiB 和大页之间的分配，我们评估几个选项： HugeFiller：如果我们有可用空间，我们使用它并乐于填充一个大部分为空的大页。 HugeFiller 无法满足这些请求，我们接下来考虑 HugeRegion；如果我们有可以满足请求的分配区域，我们就这样做。如果没有区域存在（或者它们都太满了），我们考虑分配一个，但只有在测量到高比例的 slack 与小分配时才这样做（如下所述）。 否则，我们从 HugeCache 分配一个完整的大页。这会产生 slack，但我们预计它将被未来的分配填充。 我们在 TEMERAIRE 中做出一个设计选择，即关心外部碎片化到大页级别，但基本上不关心超过这个级别（但见第 4.5 节中的例外）。例如，一个具有单个 1 GiB 空闲范围的系统和另一个具有 512 个不连续空闲大页的系统在 TEMERAIRE 中处理得同样好。在这两种情况下，分配器通常会将所有未使用的空间返回给操作系统；一个 1 GiB 的新分配将需要在任一情况下引入内存错误。在碎片化的情况下，我们将在新的虚拟内存上这样做。未被活动分配占用且不消耗物理内存的虚拟地址范围的浪费不是一个问题，因为对于 64 位地址空间，虚拟内存实际上是免费的。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#the-overall-algorithm"},{"categories":["OS"],"collections":null,"content":"HugeAllocator HugeAllocator 跟踪映射的虚拟内存。所有操作系统映射都在这里进行。它存储大页对齐的未支持范围（即没有关联物理内存的范围）。虚拟内存几乎是免费的，因此我们追求简单和合理的速度。我们的实现使用 treap[40]跟踪未使用的范围。我们通过其最大包含范围增强子树，这使我们能够快速选择一个近似最佳适应。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#hugeallocator"},{"categories":["OS"],"collections":null,"content":"HugeCache HugeCache 跟踪以完整大页粒度支持的内存范围。HugeFiller 填充和清空整个大页的一个结果是，我们需要决定何时将空大页返回给操作系统。我们会后悔返回我们将再次需要的内存，同样也会后悔不返回将在缓存中闲置的内存。急切地返回内存意味着我们进行系统调用以返回内存，并在重新使用时引入页面错误。仅在 TCMALLOC 的定期释放线程请求的速率下释放内存意味着内存被闲置。 考虑图 9 中的人工程序，没有额外的堆分配。在循环的每次迭代中，‘New‘需要一个新的大页并将其放置在 HugeFiller 中。‘Delete‘移除分配，现在大页完全空闲。急切地返回将要求每次迭代都进行系统调用，对于这个简单但病态的程序来说。 512K 直接走 Filler， 我们跟踪 2 秒滑动窗口内的需求周期性，并计算所见的最小值和最大值（demandmin，demandmax）。每当内存返回到 HugeCache 时，如果缓存将大于 demandmax − demandmin，我们将大页返回给操作系统。我们还尝试了其他算法，但这个算法简单且足以捕捉我们看到的经验动态。只要我们的窗口需求看到了新大小的需求，缓存就可以增长。在振荡使用中，这将（错误地）释放内存一次，然后（正确地）从那时起保持它。图 10 显示了 Tensorflow 工作负载的缓存大小，该工作负载通过大幅振荡使用；我们紧密跟踪实际需要的内存。 没懂 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#hugecache"},{"categories":["OS"],"collections":null,"content":"HugeFiller HugeFiller 满足每个适合单个大页的小分配。这满足了大多数分配（78% of the pageheap is backed by the HugeFiller on average across the fleet），并且是我们系统中最重要的——也是最优化的——组件。在给定的大页内，我们使用一个**简单（且快速）**的 best-fit algorithm 来放置分配；挑战在于决定在哪个大页上放置分配。 这个组件解决了我们的 binpacking problem：我们的目标是将大页分成一些保持最大填充，而其他一些保持空或近空。最空的大页可以被回收（可能根据需要分解大页），同时最小化对大页覆盖的影响，因为密集填充的页面覆盖了大部分使用内存的大页。但清空大页具有挑战性，因为我们不能依赖任何特定的分配消失。 次要目标是最大限度地减少每个大页内的碎片化，以使新请求更有可能被满足。如果系统需要一个新的 K 页跨度，并且没有 ≥ K 页的空闲范围可用，我们需要从 HugeCache 获取一个大页。这会创建(2MiB − K ∗ pagesize)的 slack，浪费空间。 这给我们两个优先考虑的目标。因为我们希望最大化大页完全空闲的概率，近空的大页是宝贵的。因为我们需要最小化碎片化，具有长空闲范围的大页也是宝贵的。两个优先级都通过保留具有最长空闲范围的大页来满足，因为较长的空闲范围必须具有较少的在使用块。我们相应地将大页组织成排序列表，利用每个大页的统计数据。 在每个大页内，我们跟踪使用页的位图；为了从某个大页填充请求，我们从该位图中进行最佳适应搜索。 次要目标才是减少大页内部碎片？ the longest free range (L), the number of contiguous pages not already allocated, the total number of allocations (A), the total number of used pages (U). 这三个统计数据决定了大页的优先顺序以放置分配。我们选择具有最低足够 L 和最高 A 的大页。对于 K 页的分配，我们首先只考虑最长空闲范围足够的大页（L ≥ K）。这决定了一个大页是否是可能的分配目标。 在 L ≥ K 的大页中，我们按 fullness 优先排序。大量的实验使我们选择了 A，而不是 U。 实验证明，优先分配数量？ This choice is motivated by a radioactive decay-type allocation model 其中每个分配，无论大小，都有可能变得空闲（具有某种概率 p）。在这个模型中，具有 5 个分配的大页有 p^5 \u0026lt;\u0026lt; p 的概率变得空闲；因此，我们应该非常强烈地避免从分配非常少的大页中分配。特别是，这个模型预测 A 是一个比 U 更好的“空闲度”模型：大小为 M 的一个分配比大小为 1 的 M 个分配更有可能被释放。 看不懂，略过概率模型 L ≥ K：首先选择具有足够空闲页的巨页。 按 A 排序：在符合条件的巨页中，优先选择已分配数较多的巨页（因为在放射性衰变模型下，这样的巨页更有可能完全变为空闲页）。 我们的策略与最佳适应不同。考虑一个大页 X 有一个 3 页的间隙和一个 10 页的间隙，另一个大页 Y 有一个 5 页的间隙。最佳适应会选择 X。我们的策略选择 Y。这个策略有效，因为我们正在寻找分配在最碎片化的页面上，因为碎片化的页面不太可能完全空闲。如果我们需要，比如 3 页，那么最多包含 3 个可用页的间隙的页面更有可能是碎片化的，因此是好的分配候选者。在放射性衰变模型下，靠近大间隙的分配与任何其他分配一样可能变得空闲，这可能导致这些间隙显著增长；然后它们可以用于大分配。我们将 10 页的间隙视为宝贵，并避免在其附近分配，除非没有其他选择，这允许它增长。 令人惊讶的是，这个简单的策略显著优于全局最佳适应算法——将请求放置在任何大页中最接近其大小的单个间隙中。最佳适应将非常昂贵——我们不能为每个请求搜索 10-100K 大页，但令人非常反直觉的是，它也会产生更高的碎片化。最佳适应对于一般碎片化问题远非最佳结果并不是新发现[36]，但看到它在这里有多糟糕是很有趣的。 best fit 不一定是最优的，是因为局部不一定全局？反而会有更多的碎片 图 11 证明了 LFR 随着需求内存降低也在降低 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#hugefiller"},{"categories":["OS"],"collections":null,"content":"HugeRegion HugeCache（及其背后的 HugeAllocator）足以处理大分配，其中舍入到完整大页的成本很小。HugeFiller 对于可以打包到单个大页的小分配效果很好。HugeRegion 则介于两者之间。 考虑一个请求 1.1 MiB 内存的情况。我们从 HugeFiller 中提供它，留下 2 MiB 大页中未使用的 0.9 MiB 内存：slack 空间。HugeFiller 假设 slack 将被未来的小分配（\u0026lt;1MiB）填充，通常确实如此：我们观察到的机群范围内小分配与 slack 的字节比率为 15:1。在极限情况下，我们可以想象一个二进制文件只请求 1.1 MiB 跨度，如图 12 所示。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:5","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#hugeregion"},{"categories":["OS"],"collections":null,"content":"Memory Release ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:6:6","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#memory-release"},{"categories":["OS"],"collections":null,"content":"Evaluation of TEMERAIRE Google’s WSC workloads. 因此，我们检查了 IPC 指标（作为用户吞吐量的代理），并在可能的情况下，我们获得了应用程序级别的性能指标，以评估工作负载在我们服务器上的生产力（例如，每核心每秒请求数）。 ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#evaluation-of-temeraire"},{"categories":["OS"],"collections":null,"content":"Application Case Studies Tensorflow search1, search2, ads1, ads2\u0026hellip; Spanner 分布式数据库 loadbalancer redis 对于具有定期释放的 8 个应用程序，我们观察到平均 CPU 改进为 7.7%，平均 RAM 减少为 2.4%。其中两个工作负载没有看到内存减少。TEMERAIRE 的 HugeCache 设计很好地处理了 Tensorflow 的分配模式，但无法影响其突发需求。Spanner 将其缓存最大化到某个内存限制，因此减少 TCMALLOC 的开销意味着在相同的内存占用下可以缓存更多的应用程序数据。 这些应用看着挺有意思，不知道有没有开源 tensorflow 改进是最大的 内存大部分都会减少 为什么 IPC 会增加？是每周期指令数吗，可以表示性能更好，缓存命中更高？ page fault 上升了，几乎 2x 这不会有影响吗？ ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#application-case-studies"},{"categories":["OS"],"collections":null,"content":"Conclusion In warehouse scale computers, TLB lookup penalties are one of the most significant compute costs facing large applications. TEMERAIRE optimizes the whole WSC by changing the memory allocator to make hugepage-conscious placement decisions while minimizing fragmentation. Application case studies of key workloads from Google’s WSCs show RPS/CPU increased by 7.7% and RAM usage decreased by 2.4%. Experiments at fleet scale and longitudinal data during the rollout at Google showed a 6% reduction in cycles spent in TLB misses, and 26% reduction in memory wasted due to fragmentation. Since the memory system is the biggest bottleneck in WSC applications, there are further opportunities to accelerate application performance by improving how the allocator organizes memory and interacts with the OS. 没看到 temeraire 是怎么处理高并发场景的，还是说沿用 tcmalloc 的设计？ ","date":"2024-10-27","objectID":"/posts/paper-hugepage/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Temeraire, Hugepage aware memory allocator","uri":"/posts/paper-hugepage/#conclusion"},{"categories":["OS"],"collections":null,"content":"Making Kernel Bypass Practical for the Cloud with Junction NSDI 24 的论文 motivation 还是和以前的类似，OS kernel 比如 TCP 栈带来了 IO overheads，在 cloud/datacenter 有很多 OS bypass 技术，但目前 cloud 很难实现 原因主要是 density 和 compatibility 只有少量应用可以 Github Repo: https://github.com/JunctionOS/junction ","date":"2024-10-21","objectID":"/posts/paper-junction/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#making-kernel-bypass-practical-for-the-cloud-with-junction"},{"categories":["OS"],"collections":null,"content":"Abstract 内核旁路系统相对于传统的操作系统（OSes）在网络密集型应用的吞吐量和尾部延迟方面展示了数量级的改进。然而，为了实现如此卓越的性能，它们依赖于专用资源（例如，spinning cores, pinned memory），并需要应用程序的重写。这对于云运营商来说是不可取的， 由于这两个原因，现有的内核旁路技术在云环境中是不切实际的。在本文中，我们展示了这些妥协并非解锁内核旁路全部好处的必要条件。我们提出了 Junction，这是第一个能够在机器上密集打包数千个实例，同时提供与未修改的 Linux 应用程序兼容性的内核旁路系统。 Junction 的扩展能力比现有的内核旁路系统高出 19-62 倍，并且可以在不进行代码更改的情况下实现相似或更好的性能。此外，Junction 为以前不支持内核旁路的应用程序带来了显著的性能提升，包括那些依赖于 Go、Java、Node 和 Python 等运行时系统的应用程序。在与原生 Linux 的比较中，Junction 在七个应用程序中将吞吐量提高了 1.6-7.0 倍，同时使用的核心数量减少了 1.2-3.8 倍。 性能很夸张 ","date":"2024-10-21","objectID":"/posts/paper-junction/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 网络密集型应用从内核旁路系统中获得了显著的性能提升（即尾部延迟和吞吐量的数量级改善）。其核心思想是将网络队列映射到用户空间，使得应用程序可以直接与网卡通信，从而避免内核开销。 Junction 是一个针对云应用（如微服务、无服务器等）的新型内核旁路系统，与之前的内核旁路系统类似，Junction 提供了显著的性能提升，包括更高的吞吐量和更大的 CPU 效率，以及相对于传统操作系统的尾部延迟数量级的减少。 Junction 在保持应用程序之间严格隔离的同时，提供了比现有云隔离方案更窄的攻击面。 性能指标：吞吐量，CPU 效率，尾部延迟 之前的内核旁路系统做出了一些妥协，使得它们在云环境中不切实际。例如，它们需要专用的、繁忙旋转的核心和固定内存，因此在机器上只能打包非常少的实例。此外，它们对编程模型进行了重大更改[6, 14, 21, 33, 52]，这破坏了兼容性并牺牲了现有软件的巨大投资。最后，大多数内核旁路系统本身不提供隔离，因此必须与虚拟化结合（及其相关的开销，如 VM 退出成本、嵌套页表等）才能在云环境中安全部署。 Junction 通过一系列设计贡献，在不做出这些妥协的情况下保留了内核旁路的全部性能优势，这些贡献针对隔离、密度和兼容性。为了在避免虚拟化开销的同时实现强隔离，Junction 在每个实例内部运行一个普通的 Linux 进程，并安装一个限制系统调用访问的过滤器。在实例内部，Junction 作为一个库运行，与应用程序共享一个地址空间。由于 Junction 能够在内核旁路硬件（如网卡队列、CPU 特性等）之上构建其所有操作系统抽象，因此它只需要与内核进行最少的交互，仅足以实现资源复用（≈ 十几个系统调用）。 为了实现高密度，Junction 高效地复用了核心和内存。 为此，Junction 创新地使用了网卡硬件特性，该特性在专用队列上传递数据包到达通知，而不是要求每个接收队列单独轮询。 Junction 还安全地暴露了 Linux 页面缓存，以在实例之间共享只读内存 pull -\u0026gt; push 为了实现 Linux 兼容性，Junction 提供了 Linux 内核系统调用接口的自有实现。这在保持内核旁路性能优势的同时是一项挑战。Junction 利用其与应用程序运行在同一地址空间的事实，解锁了最小化兼容性成本的优化。例如，Junction 安全地将系统调用转换为函数调用，避免瞬态执行缓解，直接访问参数而不复制它们，利用未定义行为消除锁定，并使用向量指令而不需要保存和恢复寄存器状态。Junction 还提供了现有内核旁路系统中缺失的许多操作系统特性（如信号、线程本地存储、随机性、文件系统、定时器等），但这些特性对于支持云应用至关重要。它通过利用现代 CPU 扩展来避免陷入内核，从而保持了内核旁路的方法来提供这些特性。 和应用程序在同一地址的好处是什么 这样的云相关的设计，在个人看来是比较优秀的，保证兼容性做性能，更加实际 ","date":"2024-10-21","objectID":"/posts/paper-junction/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#introduction"},{"categories":["OS"],"collections":null,"content":"Background \u0026amp; Motivation 内核旁路系统消除了内核在网络数据路径中的角色，并将其替换为一个优化的用户级网络栈，该栈直接与网卡通信。在本节中，我们首先讨论为什么现有的内核旁路方法存在缺陷，阻碍了其在云环境中的采用，特别是在密度和兼容性至关重要的云环境中。接下来，我们讨论当前使内核旁路更具通用性的进展。最后，我们讨论缺乏安全性如何进一步加剧这些问题。 Density challenges 不幸的是，当前的内核旁路系统只能在机器上支持有限数量的实例。一个主要问题是广泛使用繁忙旋转和专用核心[30, 67]。因为这种方法要求每个实例至少一个核心（通常更多），所以实例的最大数量受限于核心数量。 新的 CPU 调度方法通过加快核心分配可以克服这一限制，并在不牺牲尾部延迟的情况下消除繁忙旋转的浪费[15, 42, 50]。然而，这些系统依赖于专用核心来进行调度决策，因此其可扩展性仍然有限。例如，Caladan 由于其调度器核心的瓶颈，无法扩展到数百个实例以上。 最后，内核旁路系统的内存占用也是实现高密度的一个重大障碍。 Compatibility challenges. 理想情况下，内核旁路系统应支持未修改的二进制文件。现有的内核旁路系统反而要求开发人员移植应用程序 Making kernel bypass general purpose. 最近有几个努力使内核旁路更具通用性。例如，通常内核旁路系统使用 run-to-completion 来优化短请求[6]，但这在请求服务时间分散时会导致高尾部延迟。Shinjuku[29]和 Perséphone[9]分别通过高效、细粒度的抢占和将短请求引导到单独的核心来解决这个问题。许多内核旁路系统还使用“无共享”设计，这在负载不平衡时会损害尾部延迟，ZygOS 通过工作窃取解决了这个问题[47]。Demikernel 在不同的硬件后端（如 RDMA 与以太网）之上提供了统一的抽象，以减少开发人员的工作量[67]。最后，Arachne[50]和 Shenango[42]展示了线程化可以变得足够快，以便与内核旁路网络一起使用。Junction 采用了这些系统中的几个想法，同时解决了之前未解决的密度和兼容性挑战。 Security challenges 大多数内核旁路系统回避隔离，必须以 root 权限运行。因此，它们依赖其他隔离机制才能在云中安全部署。最可行的选择是让每个实例在单独的虚拟机中运行，但这会增加开销，包括额外的 TLB 未命中、VM 退出成本和由客户内核引起的更大的内存占用。在大多数情况下，虚拟机也无法利用页面缓存，这进一步限制了它们的密度。 ","date":"2024-10-21","objectID":"/posts/paper-junction/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#background--motivation"},{"categories":["OS"],"collections":null,"content":"Junction Overview Junction 的概述，并突出了其主要组件。Junction 设计用于在机器上处理数千个实例。一个实例是一个隔离的容器，运行一个或多个应用程序二进制文件。从主机内核的角度来看，这个容器由一个单一进程（称为 kProc）和一组固定的线程（称为 kThreads）组成，这些线程在启动时静态初始化。kThreads 由一个集中式调度器调度到核心上（图 1 的左侧）。一个实例可以在其共享地址空间中加载和运行多个二进制文件（将每个二进制文件放置在虚拟内存的不同偏移量处）。实例中的每个二进制文件在一个称为 uProc 的用户空间进程抽象中运行。 Junction 内核的副本在每个实例内部运行，并与 uProcs 共享一个地址空间。它直接处理来自 uProcs 的系统调用，并在用户空间中提供操作系统抽象（如线程、网络、文件系统、信号等），类似于库操作系统[10, 46]。Junction 内核支持 Linux 系统调用接口，因此它可以运行现有软件而无需修改。 Junction 内核使用内核旁路硬件 网络和通信。与其他内核旁路系统类似，Junction 实例为每个 kThread 提供一对 NIC 发送和接收队列。这通过允许并发访问 NIC 而无需同步来提高性能。Junction 内核提供了一个高性能的 TCP/IP 和 UDP 网络栈，使 uProcs 能够与外界通信。同一实例中的 uProcs 可以使用标准的进程间通信（IPC）原语（如管道）相互通信，但同一主机上的不同实例只能通过 NIC 的环回网络进行通信。 线程。Junction 内核包含一个高性能的用户级线程库，该库使用工作窃取来平衡轻量级用户级线程（uThreads）在 kThreads 之间的负载。uProc 线程（即在启动时或通过 clone3()创建的线程）映射到 uThreads。uThreads 还用于各种内部任务，如网络协议处理。每个 kThread 运行一个调度循环，轮询本地队列中的数据包和超时，并运行挂起的 uThreads。 核心调度。Junction 依赖于微内核风格的调度器来进行核心分配决策[15, 23, 42, 48]。它在专用核心上运行，并繁忙轮询控制信号以决定何时以及如何将核心分配给每个实例（如图 1 中的红色双箭头所示）。实例在空闲时可以使用少至零个核心，如果需求合理，则可以使用多个核心（直至每个实例的限制）。对于每个实例，调度器监控线程和网络队列中的计时器到期和排队延迟，这些信息通过共享内存对调度器可见。当调度器将核心授予实例时，它会从中选择一个空闲的 kThread，并将其固定在该核心上，直到授予结束 除了执行核心分配外，调度器还通过发送用户 IPI（UIPI）[25]来协助线程库实现细粒度的时间片轮转，以处理运行 uThread 已超过其时间片量子的核心。这确保了所有 uThreads 都能取得进展，并且数据包队列能及时清空。当服务时间分散度高时，这对减少尾部延迟也是有利的[29]。作为一种优化，这些中断仅在有排队数据包或可运行线程等待处理时发送。 由于 Junction 旨在支持数千个实例，它采用了新颖的技术来确保控制信号能够以可扩展的方式进行监控。计时器轮跟踪每个实例的下一个超时时间，而 NIC 事件队列提供数据包到达的通知。这些减少了调度器必须轮询每个实例内部共享状态以确定其是否可以从额外核心中受益的频率。我们在第 5 节中更详细地讨论了这些优化。 ","date":"2024-10-21","objectID":"/posts/paper-junction/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#junction-overview"},{"categories":["OS"],"collections":null,"content":"Security 传统观点认为，应使用虚拟机作为云的隔离边界，以减少不受信任代码与主机内核之间的交互。然而，虚拟化仍然会执行主机内核中大量受信任的代码，从而导致显著的攻击面[3, 65]。相比之下，Junction 直接在内核旁路硬件之上提供操作系统抽象，显著减少了对主机内核的依赖。在本节中，我们将更详细地讨论 Junction 的安全设计。 没懂 ","date":"2024-10-21","objectID":"/posts/paper-junction/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#security"},{"categories":["OS"],"collections":null,"content":"Threat Model ","date":"2024-10-21","objectID":"/posts/paper-junction/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#threat-model"},{"categories":["OS"],"collections":null,"content":"Host Kernel Isolation Page cache? ","date":"2024-10-21","objectID":"/posts/paper-junction/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#host-kernel-isolation"},{"categories":["OS"],"collections":null,"content":"Optimizing for Density Junction 的目标是提供高网络吞吐量和低延迟，类似于现有的内核旁路系统，同时还能在机器上打包更多的实例。这要求我们解决与使用大量 NIC 接收队列相关的几个问题。 仍然必须为每个实例分配足够的接收队列，以便每个可能运行的 kThread 都有一个可用的队列。因此，所需的队列数量是每个实例的最大核心数乘以实例数量。 现代网卡可以轻松扩展到数千个队列，但使用它们在机器上打包许多实例仍然面临重大挑战。首先，缓冲区内存消耗是密度的关键限制，因为每个接收队列必须发布足够的缓冲区以容纳最坏情况下的数据包突发，这在核心之间的流量不均匀时会加剧。其次，核心调度器轮询每个队列的成本变得过高，缓存污染导致性能崩溃。我们现在更详细地讨论我们针对这些问题的解决方案。 队列数量很多 ","date":"2024-10-21","objectID":"/posts/paper-junction/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#optimizing-for-density"},{"categories":["OS"],"collections":null,"content":"Minimizing Buffer Memory Consumption ","date":"2024-10-21","objectID":"/posts/paper-junction/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#minimizing-buffer-memory-consumption"},{"categories":["OS"],"collections":null,"content":"Scalable Queue Polling 首先，它以新颖的方式使用一组网卡特性来避免持续轮询空闲网络队列。Junction 为调度器核心分配了一个单一的事件队列和专用的门铃页面。每次调度器观察到一个空接收队列时，它会通过标记当前头指针的索引并写入门铃来武装队列。当数据包到达一个武装队列时，网卡将一个事件写入事件队列并解除队列的武装。调度器不断轮询事件队列，并在数据包到达空闲队列时立即做出反应。此特性在现代 Mellanox 网卡上可用。 硬件限制必须用新的？ ","date":"2024-10-21","objectID":"/posts/paper-junction/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#scalable-queue-polling"},{"categories":["OS"],"collections":null,"content":"Linux Compatibility ","date":"2024-10-21","objectID":"/posts/paper-junction/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#linux-compatibility"},{"categories":["OS"],"collections":null,"content":"Adapting OS Features to Kernel Bypass ELK 加载器 vfork() ","date":"2024-10-21","objectID":"/posts/paper-junction/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#adapting-os-features-to-kernel-bypass"},{"categories":["OS"],"collections":null,"content":"Performance Optimizations seccomp 拦截 ","date":"2024-10-21","objectID":"/posts/paper-junction/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#performance-optimizations"},{"categories":["OS"],"collections":null,"content":"Implementation ","date":"2024-10-21","objectID":"/posts/paper-junction/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#implementation"},{"categories":["OS"],"collections":null,"content":"Evaluation 这里的几个例子还是挺有意思的 ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#evaluation"},{"categories":["OS"],"collections":null,"content":"Methodology 只说了应用，但是怎么没说开了多少 instances？ ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:1","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#methodology"},{"categories":["OS"],"collections":null,"content":"Comparison to other kernel bypass systems ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:2","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#comparison-to-other-kernel-bypass-systems"},{"categories":["OS"],"collections":null,"content":"Density 横坐标是 number of applications Linux 内存占用几乎不动 但是 FireCracker, Caladan 占用很高 Junction 呈线性增加，为什么会增加呢？队列？ ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:3","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#density"},{"categories":["OS"],"collections":null,"content":"Compatibility ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:4","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#compatibility"},{"categories":["OS"],"collections":null,"content":"Attack Surface Junction 使用内核旁路技术减少了相对于现有以安全性为重点的库操作系统的主机内核攻击面。 能提供 attack 安全性的测试倒是很有意思 ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:5","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#attack-surface"},{"categories":["OS"],"collections":null,"content":"Performance Analysis Performance optimizations Pinned memory ","date":"2024-10-21","objectID":"/posts/paper-junction/:10:6","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#performance-analysis"},{"categories":["OS"],"collections":null,"content":"Discussion 硬件限制，CPU、NIC ","date":"2024-10-21","objectID":"/posts/paper-junction/:11:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#discussion"},{"categories":["OS"],"collections":null,"content":"Conclusion .. 囫囵吞枣 看不太懂里面的一些设计，看完也不知道怎么实现的内核旁路，更像是 userspace 之类的 evaluation 也没比较 cpu 占用率什么的 每个核心一个 buffer queue 改成了 shared buffer queue 兼容性和 density 应该是最亮眼的，性能可能受硬件限制很严重 论文太乱了，感觉他们设计应该是很多东西可以讲的 ","date":"2024-10-21","objectID":"/posts/paper-junction/:12:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#conclusion"},{"categories":["OS"],"collections":null,"content":"Appendix UIPI Support ","date":"2024-10-21","objectID":"/posts/paper-junction/:13:0","tags":["Paper Reading"],"title":"Paper Reading: Making Kernel Bypass Practical for the Cloud with Junction","uri":"/posts/paper-junction/#appendix-uipi-support"},{"categories":["OS"],"collections":null,"content":"Firecracker: Lightweight Virtualization for Serverless Applications 开源的 VMM 不太感兴趣，看一眼 presentation 和实现细节就过了 比 QEMU 轻量级，更注重几个专用场景 比如没有 BIOS 没有 PCI 和容器化结合比较好 motivation 来自 EC2 太大，lambda 又太小，一些隔离 兼容性看着不错，和 linux 是兼容的 soft allocation 是指超卖吗，需要的时候才实际分配 解决的问题：QEMU/KVM 难以支持运行多个虚拟机？LXC 隔离性较弱？LibOS 兼容性问题？语言 VM 比如 JVM 隔离兼容性问题？ 性能比较了 QEMU 和 FC 的一些指标：boot time (serial \u0026amp;\u0026amp; 50 parallel) 后者生产环境表现一般， QD1 IO latency vs bare metal (深度为一，类似串行) QD32 IO throughput vs bare metal （吞吐量表现很差，被 QEMU 完虐，可能觉得还需要修改 iouring 之类的） 队列深度下的 IO 延迟和吞吐量 主要还是几个问题：兼容性，性能兼容性，一些包管理 rpm 的不确定性， 可以做的还有 IO 提升、scheduling (tail latency)、正确性证明 (isolation) 一些 feature 比如 snapshot rust-vmm 其他一些分享：https://draveness.me/papers-firecracker/ https://easoncao.com/firecracker-paper-reading/#%E7%B8%BD%E7%B5%90 ","date":"2024-10-16","objectID":"/posts/paper-firecracker/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Firecracker ","uri":"/posts/paper-firecracker/#firecracker-lightweight-virtualization-for-serverless-applications"},{"categories":["OS"],"collections":null,"content":"Choosing an Isolation Solution ","date":"2024-10-16","objectID":"/posts/paper-firecracker/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Firecracker ","uri":"/posts/paper-firecracker/#choosing-an-isolation-solution"},{"categories":["OS"],"collections":null,"content":"Xen and the Art of Virtualization Xen 是开源的虚拟化项目，https://xenproject.org/ 本文于 2003 年发布，已经 20 年过去了，和目前的虚拟化技术又有什么区别？比如容器化或 Intel VT-X 等等？ 直接运行在计算机硬件之上的用以替代操作系统的软件层，虚拟化又分两类、para virtualization 和 hardware virtualization 半虚拟化和完全虚拟化 半虚拟化，超虚拟化，也就是 Xen，虚拟机知道自己是 Xen Hypervisor，也能识别其他运行在相同环境的虚拟机 完全虚拟化，硬件虚拟化，虚拟机会觉得自己直接运行在硬件上 感觉目前的主流还是 KVM (QEMU)，内核态基础上做虚拟化，可以重用许多代码，比如内存管理。 事实也正是如此，目前关于虚拟化技术，大部分人都会选择 KVM 而不是 Xen，而且 Xen 能用的 Linux 版本非常古老 似乎 Linux 开发社区比如 Linus 和 Xen 开发人员也有争吵，质疑他们的代码不规范等等。 在 2017 年 Amazon EC2 也去掉了 Xen (安全问题？还是说难以修复) 关于 Xen 为什么发展出现问题，最后比不过 KVM 的相关的历史可以看一看 https://wangxu.me/translation/2009/03/22/xen-to-kernel/index.html https://fosschef.wordpress.com/2011/06/17/xen-the-road-of-the-life/ ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#xen-and-the-art-of-virtualization"},{"categories":["OS"],"collections":null,"content":"ABSTRACT 许多系统已经被设计出来，它们利用虚拟化技术来细分现代计算机的丰富资源。一些系统需要专门的硬件，或者无法支持通用操作系统。一些系统以性能为代价实现了 100% 的二进制兼容性。其他系统则为了速度而牺牲了安全性或功能性。很少有系统提供资源隔离或性能保证；大多数系统只提供尽力而为的资源分配，存在服务拒绝的风险。 This paper presents Xen, an x86 virtual machine monitor which allows multiple commodity operating systems to share conventional hardware in a safe and resource managed fashion, 同时不牺牲性能或功能。这是通过提供一个理想化的虚拟机抽象来实现的，操作系统如 Linux、BSD 和 Windows XP 可以以最小的努力移植到这个抽象上。 我们的设计目标是同时在一台现代服务器上托管多达 100 个虚拟机实例。Xen 采用的虚拟化方法非常高效：我们允许像 Linux 和 Windows XP 这样的操作系统同时托管，性能开销几乎可以忽略不计——最多比非虚拟化情况高出几个百分点。我们在一系列微基准测试和系统级测试中显著优于竞争的商业和免费解决方案。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#abstract"},{"categories":["OS"],"collections":null,"content":"INTRODUCTION 现代计算机足够强大，可以利用虚拟化技术来呈现许多较小的虚拟机（VM）的假象，每个虚拟机运行一个单独的操作系统实例。 在本文中，我们介绍了 Xen，一种高性能的资源管理虚拟机监视器（VMM），它支持诸如服务器整合[42, 8]、共置托管设施[14]、分布式 Web 服务[43]、安全计算平台[12, 16]和应用程序移动性[26, 37]等应用。 为什么要叫 Monitor 或者 Hypervisor Xen 应该是直接跑在硬件上，而 VMware 应该是跑在操作系统上 成功地将一台机器分区以支持多个操作系统的并发执行提出了几个挑战。首先，虚拟机必须相互隔离：一个虚拟机的执行不应不利地影响另一个虚拟机的性能。当虚拟机由相互不信任的用户拥有时，这一点尤为重要。其次，有必要支持各种不同的操作系统，以适应流行应用程序的异构性。第三，虚拟化引入的性能开销应该很小。 Xen 使用户能够动态实例化一个操作系统来执行他们想要的任何操作。 有多种方法可以在共享机器上构建托管多个应用程序和服务的系统。也许最简单的方法是部署一个或多个运行标准操作系统（如 Linux 或 Windows）的主机，然后允许用户安装文件并启动进程——应用程序之间的保护由传统的操作系统技术提供。经验表明，由于看似不相关的应用程序之间的复杂配置交互，系统管理可能会迅速成为一项耗时的任务。 this paper focuses on the VMM 更重要的是，这样的系统不能充分支持性能隔离；一个进程的调度优先级、内存需求、网络流量和磁盘访问会影响其他进程的性能。当有足够的资源分配和封闭的用户组（如计算网格或实验性 PlanetLab 平台[33]的情况）时，这可能是可以接受的，但在资源超额预订或用户不合作的情况下则不然。 最重要的问题就是性能隔离 解决这个问题的一种方法是向操作系统添加对性能隔离的支持。这在一定程度上已经通过资源容器[3]、Linux/RK[32]、QLinux[40]和 SILK[4]得到了证明。这种方法的一个困难是确保所有资源使用都归因于正确的进程——例如，考虑由于缓冲区缓存或页面替换算法导致的应用程序之间的复杂交互。这实际上是操作系统内部的“QoS crosstalk”问题[41]。在较低级别进行多路复用可以缓解这个问题，正如 Exokernel[23]和 Nemesis[27]操作系统所展示的那样。任务之间的无意或不希望的交互被最小化。 我们使用相同的基本方法来构建 Xen，它在整个操作系统的粒度上多路复用物理资源，并能够提供它们之间的性能隔离。与进程级多路复用相比，这也允许一系列客户操作系统优雅地共存，而不是强制执行特定的应用程序二进制接口。这种灵活性是有代价的——运行一个完整的操作系统比运行一个进程更重量级，无论是初始化（例如启动或恢复与 fork 和 exec 相比）还是资源消耗。 multiplexes physical resources at the granularity of an entire operating system 操作系统粒度上进行，多路复用物理资源听上去挺有意思 \u0026ldquo;QoS crosstalk\u0026rdquo;（服务质量串扰）是指在多任务或多用户环境中，一个任务或用户的操作对其他任务或用户的性能产生不希望的、非预期的影响。这种影响通常是由于资源共享和调度策略导致的，可能会降低整体系统的服务质量（QoS）。 比如缓存污染、资源争用 对于我们最多 100 个托管操作系统实例的目标，我们认为这个代价是值得的；它允许个人用户以资源控制的方式运行未修改的二进制文件或二进制文件集合（例如，一个 Apache 服务器和一个 PostgreSQL 后端）。此外，它提供了极高的灵活性，因为用户可以动态创建其软件所需的精确执行环境。避免了各种服务和应用程序之间不幸的配置交互（例如，每个 Windows 实例维护自己的注册表）。 这里的 binary 是什么，程序进程？ ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#introduction"},{"categories":["OS"],"collections":null,"content":"XEN: APPROACH \u0026amp; OVERVIEW 在传统的虚拟机监视器（VMM）中，暴露的虚拟硬件在功能上与底层机器相同。虽然 full virtualization 具有允许托管未修改操作系统的明显优势，但它也有许多缺点。这对于普遍的 IA-32 或 x86 架构尤为明显。 x86 架构的设计从未考虑过全虚拟化支持。某些特权指令必须由 VMM 处理以实现正确的虚拟化，但在权限不足的情况下执行这些指令会静默失败，而不是引发方便的 trap [36]。高效地虚拟化 x86 内**存管理单元（MMU）**也很困难。 VMware 的 ESX Server[10]动态重写托管机器代码的部分内容，以在可能需要 VMM 干预的任何地方插入陷阱。这种转换应用于整个客户操作系统内核（伴随着相关的转换、执行和缓存成本），因为所有非陷阱的特权指令都必须被捕获和处理。ESX Server 实现了系统结构（如页表）的影子版本，并通过捕获每次更新尝试来保持与虚拟表的一致性——这种方法对于更新密集型操作（如创建新的应用程序进程）具有高成本。 ESX Server 全虚拟化 metal Hypervisor 尽管 x86 的复杂性，全虚拟化还有其他反对理由。特别是在某些情况下，托管操作系统既看到真实资源又看到虚拟资源是可取的：提供真实和虚拟时间允许客户操作系统更好地支持时间敏感任务，并正确处理 TCP 超时和 RTT 估计，而暴露真实机器地址允许客户操作系统通过使用 superpages [30]或 page coloring [24]来提高性能。 我们通过提供一个与底层硬件相似但不完全相同的虚拟机抽象来避免全虚拟化的缺点——这种方法被称为半虚拟化[43]。这有望提高性能，尽管它确实需要对客户操作系统进行修改。然而，重要的是要注意，我们不需要更改应用程序二进制接口（ABI），因此不需要对客户应用程序进行修改。 xen 是半虚拟化，支持 x86 和 ARM 等等 应该也支持 HVM 全虚拟化 半虚拟化需要特定内核的操作系统，如基于 Linux paravirt_ops(Linux 内核的一套编译选项)框架的 Linux 内核，而 Windows 操作系统由于其封闭性则不能被 XEN 的半虚拟化所支持， 不需要修改客户应用程序是很重要的 我们将迄今为止的讨论提炼为一组设计原则： 支持未修改的应用程序二进制文件是至关重要的，否则用户不会过渡到 Xen。因此，我们必须虚拟化现有标准 ABI 所需的所有架构特性。 支持完整的多应用程序操作系统很重要，因为这允许在单个客户操作系统实例中虚拟化复杂的服务器配置。 半虚拟化对于在不合作的机器架构（如 x86）上获得高性能和强资源隔离是必要的。 即使在合作的机器架构上，完全隐藏资源虚拟化对客户操作系统的影响也会带来正确性和性能的风险。 ABI 定义应用程序如何在二进制级别与系统交互 请注意，我们半虚拟化的 x86 抽象与最近的 Denali 项目[44]提出的抽象有很大不同。Denali 旨在支持数千个运行网络服务的虚拟机，其中绝大多数是小规模且不受欢迎的。相比之下，Xen 旨在扩展到大约 100 个运行行业标准应用程序和服务的虚拟机。鉴于这些非常不同的目标，将 Denali 的设计选择与我们的原则进行对比是有益的。 首先，Denali 不针对现有的 ABI，因此可以从其虚拟机接口中省略某些架构特性。 其次，Denali 实现不解决在单个客户操作系统内支持应用程序多路复用或多地址空间的问题。类似于 Exokernel 中的 libOS 的方式显式链接到 Ilwaco 客户操作系统实例。 因此，每个虚拟机本质上托管一个单用户单应用程序的无保护“操作系统”。相比之下，在 Xen 中，单个虚拟机托管一个真实的操作系统，该操作系统本身可以安全地多路复用数千个未修改的用户级进程。尽管已经开发了一个原型虚拟 MMU，这可能有助于 Denali 在这一领域[44]，但我们不知道任何已发表的技术细节或评估。 第三，在 Denali 架构中，VMM performs all paging to and from disk，这可能与虚拟化层缺乏内存管理支持有关。VMM 中分页与我们性能隔离的目标相反：恶意的虚拟机可以鼓励抖动行为，不公平地剥夺其他虚拟机的 CPU 时间和磁盘带宽。在 Xen 中，我们期望每个客户操作系统使用其自己的保证内存预留和磁盘分配来执行自己的分页（这一想法之前已被 self-paging [20]利用） 最后，Denali 虚拟化了所有机器资源的“命名空间”，认为如果虚拟机无法命名资源，就无法访问其他虚拟机的资源分配（例如，虚拟机对硬件地址一无所知，只知道 Denali 为其创建的虚拟地址）。相比之下，我们认为在虚拟机监视器内的安全访问控制足以确保保护；此外，如前所述，有强有力的正确性和性能论据支持使物理资源直接对客户操作系统可见。 在下一节中，我们将描述 Xen 导出的虚拟机抽象，并讨论客户操作系统必须如何修改以符合这一抽象。请注意，在本文中，我们将术语“客户操作系统 guest operating system” 保留给 Xen 可以托管的操作系统之一，并使用术语“域 domain”来指代在其中执行客户操作系统的运行虚拟机； We call Xen itself the hypervisor since it operates at a higher privilege level than the supervisor code of the guest operating systems that it hosts ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#xen-approach--overview"},{"categories":["OS"],"collections":null,"content":"The Virtual Machine Interface 表 1 概述了半虚拟化 x86 接口，分为系统三大方面：内存管理、CPU 和设备 I/O。接下来，我们将依次讨论每个机器子系统，并讨论它们在我们半虚拟化架构中的呈现方式。请注意，尽管我们的实现中某些部分（如内存管理）是特定于 x86 的，但许多方面（如我们的虚拟 CPU 和 I/O 设备）可以轻松应用于其他机器架构。此外，x86 在某些方面与其他 RISC 风格处理器存在显著差异，例如，高效虚拟化硬件页表比虚拟化软件管理的 TLB 更为困难。 Memory management Virtualizing memory 无疑是半虚拟化架构中最困难的部分，无论是从虚拟机监视器所需的机制还是从移植每个客户操作系统所需的修改来看。如果架构提供软件管理的 TLB，这项任务会更容易，因为这些 TLB 可以以简单的方式高效虚拟化[13]。带标签的 TLB 是大多数服务器级 RISC 架构（包括 Alpha、MIPS 和 SPARC）支持的另一个有用功能。将地址空间标识符标签与每个 TLB 条目相关联，允许虚拟机监视器和每个客户操作系统在单独的地址空间中高效共存，因为不需要在转移执行时刷新整个 TLB。 不幸的是，x86 没有软件管理的 TLB；相反，TLB 未命中由处理器通过硬件遍历页表结构自动处理。因此，为了实现最佳性能，当前地址空间的所有有效页翻译应存在于硬件可访问的页表中。此外，由于 TLB 未带标签，地址空间切换通常需要完全刷新 TLB。鉴于这些限制，我们做出了两个决定：(i) 客户操作系统负责分配和管理硬件页表，Xen 仅在确保安全和隔离的情况下进行最小程度的参与；(ii) Xen 存在于每个地址空间的顶部 64MB 区域，从而在进入和离开虚拟机监视器时避免 TLB 刷新。 每当客户操作系统需要一个新的页表时（例如，因为正在创建一个新进程），它会从自己的内存预留中分配并初始化一个页面，并将其注册到 Xen。此时，操作系统必须放弃对页表内存的直接写权限：所有后续更新必须由 Xen 验证。这限制了更新的方式，包括只允许操作系统映射其拥有的页面，并禁止对页表的可写映射。客户操作系统可以批量更新请求，以分摊进入虚拟机监视器的开销。每个地址空间的顶部 64MB 区域，保留给 Xen，不可由客户操作系统访问或重新映射。然而，这个地址区域不被任何常见的 x86 ABI 使用，因此这个限制不会破坏应用程序兼容性。 分段以类似的方式虚拟化，通过验证对硬件段描述符表的更新。x86 段描述符的唯一限制是：(i) 它们必须比 Xen 具有更低的特权，(ii) 它们不得允许访问 Xen 保留的地址空间部分。 CPU 虚拟化 CPU 对客户操作系统有几个影响。主要的是，在操作系统之下插入一个虚拟机监视器违反了操作系统是系统中最特权实体的通常假设。为了保护虚拟机监视器免受操作系统错误行为的影响（以及域之间的相互保护），客户操作系统必须修改为在较低的特权级别上运行。 许多处理器架构只提供两个特权级别。 内核模式，用户模式 和 x86 里的 ring0-3 有什么区别？ 在 x86 上可以高效地虚拟化特权级别，因为它在硬件上支持四个不同的特权级别。x86 特权级别通常被称为环，编号从零（最高特权）到三（最低特权）。 操作系统代码通常在环 0 中执行，因为没有其他环可以执行特权指令，而环 3 通常用于应用程序代码。据我们所知，自 OS/2 以来，环 1 和环 2 没有被任何知名的 x86 操作系统使用过。任何遵循这种常见安排的操作系统都可以通过修改其在环 1 中执行来移植到 Xen。这可以防止客户操作系统直接执行特权指令，同时它仍然安全地与在环 3 中运行的应用程序隔离。 x86 可以这么干，那其他架构呢，ARM 也有 Exception Level 0-3 通常只有两种类型的异常频繁发生，足以影响系统性能：系统调用（通常通过软件异常实现）和页面错误。我们通过允许每个客户操作系统注册一个“快速”异常处理程序来提高系统调用的性能，该处理程序由处理器直接访问，无需通过环 0 间接访问；在将此处理程序安装到硬件异常表之前进行验证。不幸的是，无法将相同的技术应用于页面错误处理程序，因为只有环 0 中的代码可以从寄存器 CR2 读取故障地址；因此，页面错误必须始终通过 Xen 传递，以便可以保存此寄存器值以在环 1 中访问。 page fault 确实是个问题 通过在向 Xen 呈现异常处理程序时验证它们来确保安全性。唯一需要的检查是处理程序的代码段不指定在环 0 中执行。由于没有客户操作系统可以创建这样的段，因此将指定的段选择器与 Xen 保留的一小部分静态值进行比较就足够了。除此之外，任何其他处理程序问题都在异常传播期间修复——例如，如果处理程序的代码段不存在或处理程序未分页到内存中，则在 Xen 执行返回处理程序的 iret 指令时将发生适当的故障。Xen 通过检查故障程序计数器值来检测这些“双重故障”：如果地址位于异常虚拟化代码中，则终止有问题的客户操作系统。 由 Xen 来验证，比如检查代码段，检查特权级别、静态值 异常传播修复，是什么意思？ 请注意，即使对于直接系统调用处理程序，这种“懒惰”检查也是安全的：当 CPU 尝试直接跳转到客户操作系统处理程序时，将发生访问故障。在这种情况下，故障地址将在 Xen 之外（因为 Xen 永远不会执行客户操作系统的系统调用），因此故障以正常方式虚拟化。如果故障的传播导致进一步的 “double fault”，则如上所述终止客户操作系统。 Device I/OS 与通常在完全虚拟化环境中模拟现有硬件设备不同，Xen 暴露了一组干净且简单的设备抽象。这使我们能够设计一个既高效又满足我们对保护和隔离要求的接口。为此，I/O 数据通过 Xen 在每个域之间传输，使用共享内存、异步缓冲区描述符环。这些环提供了一种高性能的通信机制，用于在系统中垂直传递缓冲区信息，同时允许 Xen 高效地执行验证检查（例如，检查缓冲区是否包含在域的内存预留中）。 类似于硬件中断，Xen 支持一种轻量级的事件传递机制，用于向域发送异步通知。这些通知通过更新待处理事件类型的位图来实现，并且可以选择性地调用由客户操作系统指定的事件处理程序。这些回调可以根据客户操作系统的判断“暂停”——例如，为了避免频繁唤醒通知带来的额外开销。 domain 到底是一个虚拟机实例还是什么 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#the-virtual-machine-interface"},{"categories":["OS"],"collections":null,"content":"The Virtual Machine Interface 表 1 概述了半虚拟化 x86 接口，分为系统三大方面：内存管理、CPU 和设备 I/O。接下来，我们将依次讨论每个机器子系统，并讨论它们在我们半虚拟化架构中的呈现方式。请注意，尽管我们的实现中某些部分（如内存管理）是特定于 x86 的，但许多方面（如我们的虚拟 CPU 和 I/O 设备）可以轻松应用于其他机器架构。此外，x86 在某些方面与其他 RISC 风格处理器存在显著差异，例如，高效虚拟化硬件页表比虚拟化软件管理的 TLB 更为困难。 Memory management Virtualizing memory 无疑是半虚拟化架构中最困难的部分，无论是从虚拟机监视器所需的机制还是从移植每个客户操作系统所需的修改来看。如果架构提供软件管理的 TLB，这项任务会更容易，因为这些 TLB 可以以简单的方式高效虚拟化[13]。带标签的 TLB 是大多数服务器级 RISC 架构（包括 Alpha、MIPS 和 SPARC）支持的另一个有用功能。将地址空间标识符标签与每个 TLB 条目相关联，允许虚拟机监视器和每个客户操作系统在单独的地址空间中高效共存，因为不需要在转移执行时刷新整个 TLB。 不幸的是，x86 没有软件管理的 TLB；相反，TLB 未命中由处理器通过硬件遍历页表结构自动处理。因此，为了实现最佳性能，当前地址空间的所有有效页翻译应存在于硬件可访问的页表中。此外，由于 TLB 未带标签，地址空间切换通常需要完全刷新 TLB。鉴于这些限制，我们做出了两个决定：(i) 客户操作系统负责分配和管理硬件页表，Xen 仅在确保安全和隔离的情况下进行最小程度的参与；(ii) Xen 存在于每个地址空间的顶部 64MB 区域，从而在进入和离开虚拟机监视器时避免 TLB 刷新。 每当客户操作系统需要一个新的页表时（例如，因为正在创建一个新进程），它会从自己的内存预留中分配并初始化一个页面，并将其注册到 Xen。此时，操作系统必须放弃对页表内存的直接写权限：所有后续更新必须由 Xen 验证。这限制了更新的方式，包括只允许操作系统映射其拥有的页面，并禁止对页表的可写映射。客户操作系统可以批量更新请求，以分摊进入虚拟机监视器的开销。每个地址空间的顶部 64MB 区域，保留给 Xen，不可由客户操作系统访问或重新映射。然而，这个地址区域不被任何常见的 x86 ABI 使用，因此这个限制不会破坏应用程序兼容性。 分段以类似的方式虚拟化，通过验证对硬件段描述符表的更新。x86 段描述符的唯一限制是：(i) 它们必须比 Xen 具有更低的特权，(ii) 它们不得允许访问 Xen 保留的地址空间部分。 CPU 虚拟化 CPU 对客户操作系统有几个影响。主要的是，在操作系统之下插入一个虚拟机监视器违反了操作系统是系统中最特权实体的通常假设。为了保护虚拟机监视器免受操作系统错误行为的影响（以及域之间的相互保护），客户操作系统必须修改为在较低的特权级别上运行。 许多处理器架构只提供两个特权级别。 内核模式，用户模式 和 x86 里的 ring0-3 有什么区别？ 在 x86 上可以高效地虚拟化特权级别，因为它在硬件上支持四个不同的特权级别。x86 特权级别通常被称为环，编号从零（最高特权）到三（最低特权）。 操作系统代码通常在环 0 中执行，因为没有其他环可以执行特权指令，而环 3 通常用于应用程序代码。据我们所知，自 OS/2 以来，环 1 和环 2 没有被任何知名的 x86 操作系统使用过。任何遵循这种常见安排的操作系统都可以通过修改其在环 1 中执行来移植到 Xen。这可以防止客户操作系统直接执行特权指令，同时它仍然安全地与在环 3 中运行的应用程序隔离。 x86 可以这么干，那其他架构呢，ARM 也有 Exception Level 0-3 通常只有两种类型的异常频繁发生，足以影响系统性能：系统调用（通常通过软件异常实现）和页面错误。我们通过允许每个客户操作系统注册一个“快速”异常处理程序来提高系统调用的性能，该处理程序由处理器直接访问，无需通过环 0 间接访问；在将此处理程序安装到硬件异常表之前进行验证。不幸的是，无法将相同的技术应用于页面错误处理程序，因为只有环 0 中的代码可以从寄存器 CR2 读取故障地址；因此，页面错误必须始终通过 Xen 传递，以便可以保存此寄存器值以在环 1 中访问。 page fault 确实是个问题 通过在向 Xen 呈现异常处理程序时验证它们来确保安全性。唯一需要的检查是处理程序的代码段不指定在环 0 中执行。由于没有客户操作系统可以创建这样的段，因此将指定的段选择器与 Xen 保留的一小部分静态值进行比较就足够了。除此之外，任何其他处理程序问题都在异常传播期间修复——例如，如果处理程序的代码段不存在或处理程序未分页到内存中，则在 Xen 执行返回处理程序的 iret 指令时将发生适当的故障。Xen 通过检查故障程序计数器值来检测这些“双重故障”：如果地址位于异常虚拟化代码中，则终止有问题的客户操作系统。 由 Xen 来验证，比如检查代码段，检查特权级别、静态值 异常传播修复，是什么意思？ 请注意，即使对于直接系统调用处理程序，这种“懒惰”检查也是安全的：当 CPU 尝试直接跳转到客户操作系统处理程序时，将发生访问故障。在这种情况下，故障地址将在 Xen 之外（因为 Xen 永远不会执行客户操作系统的系统调用），因此故障以正常方式虚拟化。如果故障的传播导致进一步的 “double fault”，则如上所述终止客户操作系统。 Device I/OS 与通常在完全虚拟化环境中模拟现有硬件设备不同，Xen 暴露了一组干净且简单的设备抽象。这使我们能够设计一个既高效又满足我们对保护和隔离要求的接口。为此，I/O 数据通过 Xen 在每个域之间传输，使用共享内存、异步缓冲区描述符环。这些环提供了一种高性能的通信机制，用于在系统中垂直传递缓冲区信息，同时允许 Xen 高效地执行验证检查（例如，检查缓冲区是否包含在域的内存预留中）。 类似于硬件中断，Xen 支持一种轻量级的事件传递机制，用于向域发送异步通知。这些通知通过更新待处理事件类型的位图来实现，并且可以选择性地调用由客户操作系统指定的事件处理程序。这些回调可以根据客户操作系统的判断“暂停”——例如，为了避免频繁唤醒通知带来的额外开销。 domain 到底是一个虚拟机实例还是什么 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#memory-management"},{"categories":["OS"],"collections":null,"content":"The Virtual Machine Interface 表 1 概述了半虚拟化 x86 接口，分为系统三大方面：内存管理、CPU 和设备 I/O。接下来，我们将依次讨论每个机器子系统，并讨论它们在我们半虚拟化架构中的呈现方式。请注意，尽管我们的实现中某些部分（如内存管理）是特定于 x86 的，但许多方面（如我们的虚拟 CPU 和 I/O 设备）可以轻松应用于其他机器架构。此外，x86 在某些方面与其他 RISC 风格处理器存在显著差异，例如，高效虚拟化硬件页表比虚拟化软件管理的 TLB 更为困难。 Memory management Virtualizing memory 无疑是半虚拟化架构中最困难的部分，无论是从虚拟机监视器所需的机制还是从移植每个客户操作系统所需的修改来看。如果架构提供软件管理的 TLB，这项任务会更容易，因为这些 TLB 可以以简单的方式高效虚拟化[13]。带标签的 TLB 是大多数服务器级 RISC 架构（包括 Alpha、MIPS 和 SPARC）支持的另一个有用功能。将地址空间标识符标签与每个 TLB 条目相关联，允许虚拟机监视器和每个客户操作系统在单独的地址空间中高效共存，因为不需要在转移执行时刷新整个 TLB。 不幸的是，x86 没有软件管理的 TLB；相反，TLB 未命中由处理器通过硬件遍历页表结构自动处理。因此，为了实现最佳性能，当前地址空间的所有有效页翻译应存在于硬件可访问的页表中。此外，由于 TLB 未带标签，地址空间切换通常需要完全刷新 TLB。鉴于这些限制，我们做出了两个决定：(i) 客户操作系统负责分配和管理硬件页表，Xen 仅在确保安全和隔离的情况下进行最小程度的参与；(ii) Xen 存在于每个地址空间的顶部 64MB 区域，从而在进入和离开虚拟机监视器时避免 TLB 刷新。 每当客户操作系统需要一个新的页表时（例如，因为正在创建一个新进程），它会从自己的内存预留中分配并初始化一个页面，并将其注册到 Xen。此时，操作系统必须放弃对页表内存的直接写权限：所有后续更新必须由 Xen 验证。这限制了更新的方式，包括只允许操作系统映射其拥有的页面，并禁止对页表的可写映射。客户操作系统可以批量更新请求，以分摊进入虚拟机监视器的开销。每个地址空间的顶部 64MB 区域，保留给 Xen，不可由客户操作系统访问或重新映射。然而，这个地址区域不被任何常见的 x86 ABI 使用，因此这个限制不会破坏应用程序兼容性。 分段以类似的方式虚拟化，通过验证对硬件段描述符表的更新。x86 段描述符的唯一限制是：(i) 它们必须比 Xen 具有更低的特权，(ii) 它们不得允许访问 Xen 保留的地址空间部分。 CPU 虚拟化 CPU 对客户操作系统有几个影响。主要的是，在操作系统之下插入一个虚拟机监视器违反了操作系统是系统中最特权实体的通常假设。为了保护虚拟机监视器免受操作系统错误行为的影响（以及域之间的相互保护），客户操作系统必须修改为在较低的特权级别上运行。 许多处理器架构只提供两个特权级别。 内核模式，用户模式 和 x86 里的 ring0-3 有什么区别？ 在 x86 上可以高效地虚拟化特权级别，因为它在硬件上支持四个不同的特权级别。x86 特权级别通常被称为环，编号从零（最高特权）到三（最低特权）。 操作系统代码通常在环 0 中执行，因为没有其他环可以执行特权指令，而环 3 通常用于应用程序代码。据我们所知，自 OS/2 以来，环 1 和环 2 没有被任何知名的 x86 操作系统使用过。任何遵循这种常见安排的操作系统都可以通过修改其在环 1 中执行来移植到 Xen。这可以防止客户操作系统直接执行特权指令，同时它仍然安全地与在环 3 中运行的应用程序隔离。 x86 可以这么干，那其他架构呢，ARM 也有 Exception Level 0-3 通常只有两种类型的异常频繁发生，足以影响系统性能：系统调用（通常通过软件异常实现）和页面错误。我们通过允许每个客户操作系统注册一个“快速”异常处理程序来提高系统调用的性能，该处理程序由处理器直接访问，无需通过环 0 间接访问；在将此处理程序安装到硬件异常表之前进行验证。不幸的是，无法将相同的技术应用于页面错误处理程序，因为只有环 0 中的代码可以从寄存器 CR2 读取故障地址；因此，页面错误必须始终通过 Xen 传递，以便可以保存此寄存器值以在环 1 中访问。 page fault 确实是个问题 通过在向 Xen 呈现异常处理程序时验证它们来确保安全性。唯一需要的检查是处理程序的代码段不指定在环 0 中执行。由于没有客户操作系统可以创建这样的段，因此将指定的段选择器与 Xen 保留的一小部分静态值进行比较就足够了。除此之外，任何其他处理程序问题都在异常传播期间修复——例如，如果处理程序的代码段不存在或处理程序未分页到内存中，则在 Xen 执行返回处理程序的 iret 指令时将发生适当的故障。Xen 通过检查故障程序计数器值来检测这些“双重故障”：如果地址位于异常虚拟化代码中，则终止有问题的客户操作系统。 由 Xen 来验证，比如检查代码段，检查特权级别、静态值 异常传播修复，是什么意思？ 请注意，即使对于直接系统调用处理程序，这种“懒惰”检查也是安全的：当 CPU 尝试直接跳转到客户操作系统处理程序时，将发生访问故障。在这种情况下，故障地址将在 Xen 之外（因为 Xen 永远不会执行客户操作系统的系统调用），因此故障以正常方式虚拟化。如果故障的传播导致进一步的 “double fault”，则如上所述终止客户操作系统。 Device I/OS 与通常在完全虚拟化环境中模拟现有硬件设备不同，Xen 暴露了一组干净且简单的设备抽象。这使我们能够设计一个既高效又满足我们对保护和隔离要求的接口。为此，I/O 数据通过 Xen 在每个域之间传输，使用共享内存、异步缓冲区描述符环。这些环提供了一种高性能的通信机制，用于在系统中垂直传递缓冲区信息，同时允许 Xen 高效地执行验证检查（例如，检查缓冲区是否包含在域的内存预留中）。 类似于硬件中断，Xen 支持一种轻量级的事件传递机制，用于向域发送异步通知。这些通知通过更新待处理事件类型的位图来实现，并且可以选择性地调用由客户操作系统指定的事件处理程序。这些回调可以根据客户操作系统的判断“暂停”——例如，为了避免频繁唤醒通知带来的额外开销。 domain 到底是一个虚拟机实例还是什么 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#cpu"},{"categories":["OS"],"collections":null,"content":"The Virtual Machine Interface 表 1 概述了半虚拟化 x86 接口，分为系统三大方面：内存管理、CPU 和设备 I/O。接下来，我们将依次讨论每个机器子系统，并讨论它们在我们半虚拟化架构中的呈现方式。请注意，尽管我们的实现中某些部分（如内存管理）是特定于 x86 的，但许多方面（如我们的虚拟 CPU 和 I/O 设备）可以轻松应用于其他机器架构。此外，x86 在某些方面与其他 RISC 风格处理器存在显著差异，例如，高效虚拟化硬件页表比虚拟化软件管理的 TLB 更为困难。 Memory management Virtualizing memory 无疑是半虚拟化架构中最困难的部分，无论是从虚拟机监视器所需的机制还是从移植每个客户操作系统所需的修改来看。如果架构提供软件管理的 TLB，这项任务会更容易，因为这些 TLB 可以以简单的方式高效虚拟化[13]。带标签的 TLB 是大多数服务器级 RISC 架构（包括 Alpha、MIPS 和 SPARC）支持的另一个有用功能。将地址空间标识符标签与每个 TLB 条目相关联，允许虚拟机监视器和每个客户操作系统在单独的地址空间中高效共存，因为不需要在转移执行时刷新整个 TLB。 不幸的是，x86 没有软件管理的 TLB；相反，TLB 未命中由处理器通过硬件遍历页表结构自动处理。因此，为了实现最佳性能，当前地址空间的所有有效页翻译应存在于硬件可访问的页表中。此外，由于 TLB 未带标签，地址空间切换通常需要完全刷新 TLB。鉴于这些限制，我们做出了两个决定：(i) 客户操作系统负责分配和管理硬件页表，Xen 仅在确保安全和隔离的情况下进行最小程度的参与；(ii) Xen 存在于每个地址空间的顶部 64MB 区域，从而在进入和离开虚拟机监视器时避免 TLB 刷新。 每当客户操作系统需要一个新的页表时（例如，因为正在创建一个新进程），它会从自己的内存预留中分配并初始化一个页面，并将其注册到 Xen。此时，操作系统必须放弃对页表内存的直接写权限：所有后续更新必须由 Xen 验证。这限制了更新的方式，包括只允许操作系统映射其拥有的页面，并禁止对页表的可写映射。客户操作系统可以批量更新请求，以分摊进入虚拟机监视器的开销。每个地址空间的顶部 64MB 区域，保留给 Xen，不可由客户操作系统访问或重新映射。然而，这个地址区域不被任何常见的 x86 ABI 使用，因此这个限制不会破坏应用程序兼容性。 分段以类似的方式虚拟化，通过验证对硬件段描述符表的更新。x86 段描述符的唯一限制是：(i) 它们必须比 Xen 具有更低的特权，(ii) 它们不得允许访问 Xen 保留的地址空间部分。 CPU 虚拟化 CPU 对客户操作系统有几个影响。主要的是，在操作系统之下插入一个虚拟机监视器违反了操作系统是系统中最特权实体的通常假设。为了保护虚拟机监视器免受操作系统错误行为的影响（以及域之间的相互保护），客户操作系统必须修改为在较低的特权级别上运行。 许多处理器架构只提供两个特权级别。 内核模式，用户模式 和 x86 里的 ring0-3 有什么区别？ 在 x86 上可以高效地虚拟化特权级别，因为它在硬件上支持四个不同的特权级别。x86 特权级别通常被称为环，编号从零（最高特权）到三（最低特权）。 操作系统代码通常在环 0 中执行，因为没有其他环可以执行特权指令，而环 3 通常用于应用程序代码。据我们所知，自 OS/2 以来，环 1 和环 2 没有被任何知名的 x86 操作系统使用过。任何遵循这种常见安排的操作系统都可以通过修改其在环 1 中执行来移植到 Xen。这可以防止客户操作系统直接执行特权指令，同时它仍然安全地与在环 3 中运行的应用程序隔离。 x86 可以这么干，那其他架构呢，ARM 也有 Exception Level 0-3 通常只有两种类型的异常频繁发生，足以影响系统性能：系统调用（通常通过软件异常实现）和页面错误。我们通过允许每个客户操作系统注册一个“快速”异常处理程序来提高系统调用的性能，该处理程序由处理器直接访问，无需通过环 0 间接访问；在将此处理程序安装到硬件异常表之前进行验证。不幸的是，无法将相同的技术应用于页面错误处理程序，因为只有环 0 中的代码可以从寄存器 CR2 读取故障地址；因此，页面错误必须始终通过 Xen 传递，以便可以保存此寄存器值以在环 1 中访问。 page fault 确实是个问题 通过在向 Xen 呈现异常处理程序时验证它们来确保安全性。唯一需要的检查是处理程序的代码段不指定在环 0 中执行。由于没有客户操作系统可以创建这样的段，因此将指定的段选择器与 Xen 保留的一小部分静态值进行比较就足够了。除此之外，任何其他处理程序问题都在异常传播期间修复——例如，如果处理程序的代码段不存在或处理程序未分页到内存中，则在 Xen 执行返回处理程序的 iret 指令时将发生适当的故障。Xen 通过检查故障程序计数器值来检测这些“双重故障”：如果地址位于异常虚拟化代码中，则终止有问题的客户操作系统。 由 Xen 来验证，比如检查代码段，检查特权级别、静态值 异常传播修复，是什么意思？ 请注意，即使对于直接系统调用处理程序，这种“懒惰”检查也是安全的：当 CPU 尝试直接跳转到客户操作系统处理程序时，将发生访问故障。在这种情况下，故障地址将在 Xen 之外（因为 Xen 永远不会执行客户操作系统的系统调用），因此故障以正常方式虚拟化。如果故障的传播导致进一步的 “double fault”，则如上所述终止客户操作系统。 Device I/OS 与通常在完全虚拟化环境中模拟现有硬件设备不同，Xen 暴露了一组干净且简单的设备抽象。这使我们能够设计一个既高效又满足我们对保护和隔离要求的接口。为此，I/O 数据通过 Xen 在每个域之间传输，使用共享内存、异步缓冲区描述符环。这些环提供了一种高性能的通信机制，用于在系统中垂直传递缓冲区信息，同时允许 Xen 高效地执行验证检查（例如，检查缓冲区是否包含在域的内存预留中）。 类似于硬件中断，Xen 支持一种轻量级的事件传递机制，用于向域发送异步通知。这些通知通过更新待处理事件类型的位图来实现，并且可以选择性地调用由客户操作系统指定的事件处理程序。这些回调可以根据客户操作系统的判断“暂停”——例如，为了避免频繁唤醒通知带来的额外开销。 domain 到底是一个虚拟机实例还是什么 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#device-ios"},{"categories":["OS"],"collections":null,"content":"The Cost of Porting an OS to Xen XP 不好移植 这样的移植感觉很费力，除非是开源的 OS，闭源的感觉难以操作 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#the-cost-of-porting-an-os-to-xen"},{"categories":["OS"],"collections":null,"content":"Control and Management Domain 0 跑了一个 guestOS 负责 control software ，XenoLinux 应该也是 Xen 的 其他 guestOs 可以是 xenoLinux 也可以是 xenoBSD 等等 separate policy from mechanism wherever possible 尽管虚拟机监视器必须在数据路径方面（例如，在域之间调度 CPU，在传输之前过滤网络包，或在读取数据块时执行访问控制）进行参与，但没有必要让它参与或甚至了解更高层次的问题，例如如何共享 CPU，或每个域可以传输哪种类型的包。 由此产生的架构是，虚拟机监视器本身只提供基本的控制操作。这些操作通过一个可以从授权域访问的接口导出；潜在复杂的策略决策，如准入控制，最好由在 running over a guest OS 上运行的管理软件执行，而不是在特权虚拟机监视器代码中执行。 这个思路感觉和 kvm / hyper-v 完全虚拟化区别的还是很大的 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#control-and-management"},{"categories":["OS"],"collections":null,"content":"DETAILED DESIGN ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#detailed-design"},{"categories":["OS"],"collections":null,"content":"Control Transfer: Hypercalls and Events Xen 与上层域之间的控制交互存在两种机制：域可以使用 Hypercalls 向 Xen 进行同步调用，而 Xen 使用异步事件机制向域传递通知。 hypercall 接口允许域执行 synchronous software trap 进入虚拟机监视器以执行特权操作，类似于传统操作系统中使用系统调用的方式。超调用的一个示例用途是请求一组页表更新，其中 Xen 验证并应用更新列表，并在完成后将控制权返回给调用域。 Xen 与域之间的通信通过异步事件机制提供，该机制取代了设备中断的常规传递机制，并允许对重要事件（如域终止请求）进行轻量级通知。类似于传统的 Unix 信号，只有少数事件，每个事件用于标记特定类型的发生。例如，事件用于指示已通过网络接收到新数据，或虚拟磁盘请求已完成。 待处理事件 Pending events 存储在每个域的位掩码中，Xen 在调用由客户操作系统指定的事件回调处理程序之前更新该位掩码。回调处理程序负责重置待处理事件集，并以适当的方式响应通知。域可以通过设置 Xen 可读的软件标志显式推迟事件处理：这类似于在真实处理器上禁用中断。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#control-transfer-hypercalls-and-events"},{"categories":["OS"],"collections":null,"content":"Data Transfer: I/O Rings The presence of a hypervisor means there is an additional protection domain between guest OSes and I/O devices, so it is crucial that a data transfer mechanism be provided that allows data to move vertically through the system with as little overhead as possible resource management event notification 为了资源责任，我们试图在从设备接收到中断时最小化将数据多路分解到特定域所需的工作——管理缓冲区的开销稍后进行，计算可能归因于适当的域。 类似地，尽可能由相关域提供用于设备 I/O 的内存，以防止共享缓冲池中固有的串扰；在数据传输期间，通过在 Xen 中固定底层页帧来保护 I/O 缓冲区。 图 2 展示了我们 I/O 描述符环的结构。环是由域分配的描述符的循环队列，但可以从 Xen 内部访问。描述符不直接包含 I/O 数据；相反，I/O 数据缓冲区由客户操作系统带外分配，并通过 I/O 描述符间接引用。对每个环的访问基于两对生产者-消费者指针：域在环上放置请求，推进请求生产者指针，Xen 移除这些请求进行处理，推进相关的请求消费者指针。响应以类似的方式放回环中，只是 Xen 作为生产者，客户操作系统作为消费者。没有要求按顺序处理请求：客户操作系统为每个请求关联一个唯一标识符，该标识符在相关响应中重现。这允许 Xen 由于调度或优先级考虑而明确地重新排序 I/O 操作。 这里的 IO ring 和 NIC 的是不是很类似 这种结构足够通用，可以支持多种不同的设备范式。例如，一组“请求”可以为网络数据包接收提供缓冲区；随后的“响应”然后通知这些缓冲区中数据包的到达。在处理磁盘请求时，重新排序是有用的，因为它允许在 Xen 中调度它们以提高效率，并且使用带外缓冲区的描述符使得实现零拷贝传输变得容易。 Xen 实现了零拷贝吗 https://wiki.xenproject.org/wiki/Xen_4.3_Block_Protocol_Scalability blkback/blkfront 我们将请求或响应的生产与另一方的通知解耦：在请求的情况下，域可以在调用超调用以提醒 Xen 之前将多个条目排队；在响应的情况下，域可以通过指定响应阈值数量来推迟通知事件的传递。这允许每个域根据延迟和吞吐量要求进行权衡，类似于 ArseNIC 千兆以太网接口中的流量感知中断调度 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#data-transfer-io-rings"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#subsystem-virtualization"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#cpu-scheduling"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#time-and-timers"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#virtual-address-translation"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#physical-memory"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#network"},{"categories":["OS"],"collections":null,"content":"Subsystem Virtualization CPU scheduling Xen 目前根据 Borrowed Virtual Time（BVT）调度算法[11]调度域，因为它既节省工作量，又有一个特殊的机制用于低延迟唤醒（或调度）域，当它接收到事件时。快速调度对于最小化虚拟化对设计为及时运行的操作系统子系统的影响尤为重要；例如，TCP 依赖于及时交付确认以正确估计网络往返时间。BVT 通过使用虚拟时间扭曲机制提供低延迟调度，该机制暂时违反“理想”的公平共享，以支持最近唤醒的域。然而，其他调度算法可以简单地在我们通用的调度器抽象上实现。每个域的调度参数可以通过在 Domain0 中运行的管理软件进行调整。 Time and timers Xen 为客户操作系统提供了真实时间、虚拟时间和挂钟时间的概念。 配合 BVT 算法？ Virtual address translation Xen 试图以尽可能小的开销虚拟化内存访问。 由于 x86 架构使用硬件页表，这一目标变得更加困难。VMware 的方法是为每个客户操作系统提供一个虚拟页表，该页表对内存管理单元（MMU）不可见 然后，虚拟机监视器负责捕获对虚拟页表的访问，验证更新，并将更改在虚拟页表和 MMU 可见的“影子”页表之间来回传播。这大大增加了某些客户操作系统操作的成本，例如创建新的虚拟地址空间，并需要显式传播硬件更新到“访问”和“脏”位 尽管完全虚拟化迫使使用影子页表，以提供连续物理内存的假象，但 Xen 并不受此限制。事实上，Xen 只需要在页表更新时介入，以防止客户操作系统进行不可接受的更改。因此，我们避免了与使用影子页表相关的开销和额外复杂性——Xen 的方法是直接向 MMU 注册客户操作系统页表，并限制客户操作系统对页表的只读访问。页表更新通过超调用传递给 Xen；为了确保安全，请求在应用之前进行验证。 Hyper-V 应该都是用的 shadow page table 或者能管理 TLB KVM 应该用了硬件 EPT，这个之前见过 Xen 通过半虚拟化技术进行虚拟内存转换，要求客户操作系统进行修改以适应虚拟化环境。以下是 Xen 进行虚拟内存转换的主要步骤和机制： 页表注册：Xen 直接向内存管理单元（MMU）注册客户操作系统的页表，并限制客户操作系统对页表的只读访问。 页表更新：页表更新通过超调用传递给 Xen，并在应用之前进行验证。 类型系统：Xen 为每个机器页帧关联一个类型和引用计数。类型包括页目录（PD）、页表（PT）、本地描述符表（LDT）、全局描述符表（GDT）或可写（RW）。 引用计数：只有在引用计数为零时，帧才能安全地重新分配任务。这确保了内存管理的安全性和隔离性。 验证机制：客户操作系统指示何时为页表使用分配帧，Xen 对帧中的每个条目进行一次性验证。验证通过后，帧的类型固定为 PD 或 PT，直到客户操作系统发出取消固定请求。 固定机制：固定机制在更改页表基指针时特别有用，因为它消除了在每个上下文切换时验证新页表的需要。 批量更新：为了最小化所需的超调用数量，客户操作系统可以在应用整个批次之前本地排队更新，并通过单个超调用应用。这在创建新地址空间时特别有益。 更新提交：更新必须在 TLB 刷新之前提交，以保证正确性。客户操作系统通常在首次使用新映射之前执行 TLB 刷新，确保任何缓存的翻译无效。 故障处理：如果客户操作系统在确定 TLB 中不存在陈旧条目时省略刷新，首次尝试使用新映射可能会导致页面不存在故障。客户操作系统故障处理程序必须检查是否有未完成的更新。 重试机制：如果找到未完成的更新，则刷新它们并重试故障指令，确保内存映射的正确性。 Physical memory 每个域的初始内存分配或预留是在其创建时指定的，因此，内存在域之间静态分区，提供强隔离。还可以指定最大允许预留：如果域内的内存压力增加，它可能会尝试从 Xen 中获取额外的内存页，直到此预留限制。相反，如果域希望节省资源，可能为了避免产生不必要的成本，它可以通过将内存页释放回 Xen 来减少其内存预留。 XenoLinux implements a balloon driver, 通过在 Xen 和 XenoLinux 的页面分配器之间来回传递内存页来调整域的内存使用。尽管我们可以直接修改 Linux 的内存管理例程，但气球驱动程序通过使用现有的操作系统功能进行调整，从而简化了 Linux 移植工作。然而，半虚拟化可以用来扩展气球驱动程序的功能；例如，客户操作系统中的内存不足处理机制可以修改为通过从 Xen 请求更多内存来自动缓解内存压力。 大多数操作系统假设内存最多由几个大的连续区域组成。因为 Xen 不保证分配连续的内存区域，客户操作系统通常会为自己创造连续物理内存的假象， Xen 不保证分配连续的内存区域 Network Domain0 负责插入和删除规则。 为了发送数据包，客户操作系统只需将缓冲区描述符排队到发送环上。Xen 复制描述符，并确保安全，然后复制数据包头并执行任何匹配的过滤规则。数据包负载不会被复制，因为我们使用 scatter-gather DMA；但是请注意，相关的页帧必须固定，直到传输完成。为了确保公平性，Xen 实现了一个简单的轮询数据包调度器。 fairness -\u0026gt; round-robin 有没有性能更好的 Disk 只有 Domain0 可以直接无限制地访问物理磁盘（IDE 和 SCSI）。所有其他域通过虚拟块设备（VBD）的抽象来访问持久存储，这些虚拟块设备由在 Domain0 内运行的管理软件创建和配置。允许 Domain0 管理 VBD 使得 Xen 的机制非常简单，避免了更复杂的解决方案，例如 Exokernel 使用的 UDFs [23]。 一个 VBD 包含一个范围列表，以及相关的所有权和访问控制信息，并通过 I/O 环机制进行访问。 ExoKernel 支持 UDF？是怎么做到的，用户可以自定义访问 IO 吗 VBD 是虚拟块设备 virtual block devices VBD 是一种抽象层，用于将物理磁盘资源虚拟化为多个虚拟磁盘设备，供不同的虚拟机（域）使用。 Xen 以简单的轮询方式处理来自竞争域的批量请求；这些请求随后被传递给 evator scheduler 调度程序，然后再到达磁盘硬件。域可以显式传递重新 reorder barriers, 以防止在需要维护更高级别语义时进行重新排序（例如，在使用预写日志时）。低级调度为我们提供了良好的吞吐量，而请求的批处理提供了相当公平的访问。未来的工作将研究提供更可预测的隔离和差异化服务，可能使用现有的技术和调度程序 磁盘调度应该也是个有意思的方向 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#disk"},{"categories":["OS"],"collections":null,"content":"Building a New Domain 为新域构建初始客户操作系统结构的任务主要委托给 Domain0，Domain0 使用其特权控制接口（第 2.3 节）访问新域的内存并通知 Xen 初始寄存器状态。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#building-a-new-domain"},{"categories":["OS"],"collections":null,"content":"Evaluation 性能评估应该是 Xen 的重头戏 我们首先将 Xen 与多种替代虚拟化技术进行基准测试，然后比较在单个原生操作系统上同时执行多个应用程序的总系统吞吐量与在每个虚拟机中运行每个应用程序的总系统吞吐量。 评估了 Xen 在客户操作系统之间提供的性能隔离 XenoLinux 移植（基于 Linux 2.4.21），因为这是我们最成熟的客户操作系统。我们预计 Windows XP 和 NetBSD 移植的相对开销会相似，但尚未进行全面评估。 机器中的 Xeon 处理器支持 SMT（“超线程”），但由于当前内核都没有 SMT 感知的调度程序，因此禁用了此功能。我们确保所有客户操作系统及其虚拟机管理程序可用的总内存量等于原生 Linux 可用的总内存量。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#evaluation"},{"categories":["OS"],"collections":null,"content":"Relative Performance 下一组条形图显示了在本地 ext3 文件系统上使用 gcc 2.96 构建 Linux 2.4.21 内核默认配置所花费的总时间。原生 Linux 花费了大约 7%的 CPU 时间在操作系统中，主要执行文件 I/O、调度和内存管理。在虚拟机管理程序的情况下，这种“系统时间”在不同程度上被扩展：Xen 仅增加了 3%的开销，而其他虚拟机管理程序则经历了更显著的减速。 我们使用 PostgreSQL 7.1.3 数据库进行了两个实验，通过默认配置的 Open Source Database Benchmark 套件（OSDB）进行测试。我 对比了很多 benchmark 选了几个有意思的，实际上涉及到 DB 操作或者磁盘操作，性能会有一些下降 但其他情况性能几乎是原生一致的 性能这么好的原因是什么，xenoLinux 和 半虚拟化吗，还是 IO ring、还是内存管理 调度算法感觉都是为了公平，但为什么会这么好 奇怪的是横坐标是 Relative Score ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#relative-performance"},{"categories":["OS"],"collections":null,"content":"Operating System Benchmarks 为了更精确地测量 Xen 和其他虚拟机管理程序（VMM）中的开销区域，我们进行了许多针对特定子系统的小型实验 在进程微基准测试（表 3）中，Xen 的 fork、exec 和 sh 性能比原生 Linux 慢。这是预期的，因为这些操作需要大量的页表更新，这些更新必须全部由 Xen 验证。然而，半虚拟化方法允许 XenoLinux 批量更新请求。创建新的页表是一个理想的情况：因为没有理由更早地提交待处理的更新，XenoLinux 可以在每次超调用中分摊 2048 次更新（其批处理缓冲区的最大大小）。因此，每次更新超调用构造 8MB 的地址空间。 表 5 中显示的 mmap 延迟和页面故障延迟结果很有趣，因为它们每页需要两次进入 Xen 的转换：一次是处理硬件故障并将详细信息传递给客户操作系统，另一次是代表客户操作系统安装更新的页表条目。尽管如此，开销相对较小。 开销几乎和原生是没区别的 就是 fork 之类的要慢一些，需要通过 xen 来验证页表创建 上下文切换、内存管理等等。 Network performance 为了评估虚拟化网络的开销，我们检查了通过千兆以太网局域网的 TCP 性能。 为什么 TCP MTU 500 反而性能要低一些？ 使用 500 字节的 MTU 时，每包开销占主导地位。发送防火墙和接收多路复用的额外复杂性对吞吐量产生了不利影响，但仅降低了 14%。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#operating-system-benchmarks"},{"categories":["OS"],"collections":null,"content":"Operating System Benchmarks 为了更精确地测量 Xen 和其他虚拟机管理程序（VMM）中的开销区域，我们进行了许多针对特定子系统的小型实验 在进程微基准测试（表 3）中，Xen 的 fork、exec 和 sh 性能比原生 Linux 慢。这是预期的，因为这些操作需要大量的页表更新，这些更新必须全部由 Xen 验证。然而，半虚拟化方法允许 XenoLinux 批量更新请求。创建新的页表是一个理想的情况：因为没有理由更早地提交待处理的更新，XenoLinux 可以在每次超调用中分摊 2048 次更新（其批处理缓冲区的最大大小）。因此，每次更新超调用构造 8MB 的地址空间。 表 5 中显示的 mmap 延迟和页面故障延迟结果很有趣，因为它们每页需要两次进入 Xen 的转换：一次是处理硬件故障并将详细信息传递给客户操作系统，另一次是代表客户操作系统安装更新的页表条目。尽管如此，开销相对较小。 开销几乎和原生是没区别的 就是 fork 之类的要慢一些，需要通过 xen 来验证页表创建 上下文切换、内存管理等等。 Network performance 为了评估虚拟化网络的开销，我们检查了通过千兆以太网局域网的 TCP 性能。 为什么 TCP MTU 500 反而性能要低一些？ 使用 500 字节的 MTU 时，每包开销占主导地位。发送防火墙和接收多路复用的额外复杂性对吞吐量产生了不利影响，但仅降低了 14%。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#network-performance"},{"categories":["OS"],"collections":null,"content":"Concurrent Virtual Machines 在图 5 中，我们展示了 Xen 在运行 1、2、4 和 8 个 OSDB-IR 和 OSDB-OLTP 实例时实现的总吞吐量。当添加第二个域时，第二个 CPU 的充分利用几乎使总吞吐量翻倍。进一步增加域的数量会导致总吞吐量有所下降，这可以归因于增加的上下文切换和磁盘头移动。在单个 Linux 操作系统上运行多个 PostgreSQL 实例的总分数比使用 Xen 的等效分数低 25-35%。原因尚未完全理解，但似乎 PostgreSQL 存在 SMP 可扩展性问题，并且未能充分利用 Linux 的块缓存。 图 5 还展示了 8 个域之间的性能差异化。Xen 的调度器配置为给每个域一个介于 1 和 8 之间的整数权重。每个域的吞吐量分数反映在条形图的不同分段中。在 IR 基准测试中，权重对吞吐量有精确的影响，每个分段都在其预期大小的 4%以内。然而，在 OLTP 情况下，分配更多资源的域未能实现成比例的高分数：高水平的同步磁盘活动突显了我们当前磁盘调度算法的弱点，导致它们表现不佳 磁盘调度是个问题，前文也提到 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#concurrent-virtual-machines"},{"categories":["OS"],"collections":null,"content":"Performance Isolation 第四个域同时运行了一个“fork 炸弹”和一个虚拟内存密集型应用程序，该应用程序尝试分配和接触 3GB 的虚拟内存，并在失败时释放每个页面然后重新启动。 我们发现，OSDB-IR 和 SPEC WEB99 的结果仅受到运行破坏性过程的两个域的行为的轻微影响——分别比之前报告的结果低 4%和 2%。我们将此归因于额外的上下文切换和缓存效应的开销。考虑到我们当前相对简单的磁盘调度算法，我们认为这是有些幸运的，但在这种情况下，它似乎为基准测试提供了足够的隔离，使其能够从其他域的页面交换和磁盘密集型活动中取得良好的进展。VMware Workstation 实现了类似的隔离水平，但绝对性能水平较低。 论文希望实现的是 P. Shenoy and H. Vin. Cello: A Disk Scheduling Framework for Next-generation Operating Systems. In Proceedings of ACM SIGMETRICS’98, the International Conference on Measurement and Modeling of Computer Systems, pages 44–55, June 1998. 这里的性能隔离可能不如 KVM ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#performance-isolation"},{"categories":["OS"],"collections":null,"content":"Scalability 在本节中，我们考察了 Xen 扩展到 100 个域的能力。我们讨论了运行许多客户操作系统实例及其相关应用程序的内存需求，并测量了其执行的 CPU 性能开销。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:6:5","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#scalability"},{"categories":["OS"],"collections":null,"content":"Related Work 如我们的评估所示，选择完全虚拟化虽然能够更容易地支持现成的操作系统，但对性能有不利影响。 目前，我们知道另外两个采用半虚拟化方法的系统：IBM 目前支持其 zSeries 大型机上的 Linux 半虚拟化版本，允许大量 Linux 实例同时运行。Denali [44]，前面讨论过，是一个当代的隔离内核，试图提供一个能够托管大量虚拟化操作系统实例的系统。 我们与操作系统的可扩展性和主动网络社区有一些动机。然而，当在 Xen 上运行时，不需要检查“安全”代码或保证终止——在任何一种情况下，唯一受影响的人是相关客户。因此，Xen 提供了一个更通用的解决方案：托管代码不需要由受信任的编译器进行数字签名（如 SPIN [5]），不需要附带安全证明（如 PCC [31]），不需要用特定语言编写（如 SafetyNet [22] 或任何基于 Java 的系统），也不需要依赖特定中间件（如移动代理系统）。当然，这些其他技术可以在运行在 Xen 上的客户操作系统内继续使用。这对于具有更多临时任务的工作负载可能特别有用，这些任务不会提供启动新域的机会来分摊成本。 ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#related-work"},{"categories":["OS"],"collections":null,"content":"Conclusion VMWare 成立于 1998 Xen 很多想法都是半虚拟化，paravirtualization 而不是完全虚拟化，使用 Domain0 来管理客制化的 OS，需要实现 OS 随着后面 Intel VT-X/ AMD SVM 的出现，KVM 的性能也可以接近原生，Xen 也开始支持 HVM，也加入了 Linux Foundation Xen 限制还是挺大的，要实现 OS，兼容性没那么好（直到 2012 才实现了 ARM），应该有不少文章对比 KVM 和 Xen 的性能 可能这也是 Xen 现在发展完全不如 KVM 的根源所在吧，尽管 Xen 性能略优，但兼容性和使用门槛才是商业化的敲门砖，不过 Xen 后面被 Citrix 收购了可能商业化会好很多，也推出了全虚拟化 HVM 版本（貌似是 Windows） https://ieeexplore.ieee.org/abstract/document/6714189 S. G. Soriga and M. Barbulescu, \u0026ldquo;A comparison of the performance and scalability of Xen and KVM hypervisors,\u0026rdquo; 2013 RoEduNet International Conference 12th Edition: Networking in Education and Research, Iasi, Romania, 2013, pp. 1-6, doi: 10.1109/RoEduNet.2013.6714189. ","date":"2024-10-12","objectID":"/posts/paper-xen-vir/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Xen and the Art of Virtualization","uri":"/posts/paper-xen-vir/#conclusion"},{"categories":["OS"],"collections":null,"content":"Caladan: Mitigating Interference at Microsecond Timescales Shenango 同一批人做的，在其基础上提出了性能更好、吞吐量更大的 Caladan，将 IOKernel 移出了 datapath，使用的是 Dirctpath ","date":"2024-10-09","objectID":"/posts/paper-caladan/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#caladan-mitigating-interference-at-microsecond-timescales"},{"categories":["OS"],"collections":null,"content":"Abstract 传统观点认为，CPU 资源如核心、缓存和内存带宽必须进行分区，以实现任务之间的性能隔离 什么是 performance isolation 多任务或多用户环境中，确保一个任务或用户的性能不受其他任务或用户的影响 在本文中，我们证明资源分区 resource partitioning 既不是必要的也不是充分的。 Caladan 是一种新的 CPU 调度器，通过一组依赖于快速核心分配而非资源分区的控制信号和策略，可以显著提高quality of service (tail latency, throughput, etc.)。Caladan 包括一个集中式调度核心，主动管理内存层次结构和超线程之间的资源争用，以及一个内核模块，该模块绕过标准 Linux 内核调度器，以支持微秒级的任务监控和放置。当将 memcached 与一个尽力而为的垃圾收集工作负载共存时，Caladan 的表现优于最先进的资源分区系统 Parties，性能提升了 11,000 倍，在资源使用变化期间将尾部延迟从 580 毫秒降低到 52 微秒，同时保持高 CPU 利用率。 绕过 Linux Kernel scheduler，和 eBPF 实现调度有什么区别呢 这性能提升太夸张了，到底是 baseline 选的好还是什么 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 像网络搜索、社交网络和在线零售这样的交互式、数据密集型网络服务通常会将请求分布在数千台服务器上。最小化尾部延迟对这些服务至关重要， 然而，减少尾部延迟的努力必须与最大化数据中心效率的需求仔细平衡； 大规模数据中心运营商通常 pack several tasks 在同一台机器上，以在可变负载的情况下提高 CPU 利用率[22, 57, 66, 71]。在这些条件下，任务必须竞争共享资源，如核心、内存带宽、缓存和执行单元。当共享资源争用高时，延迟会显著增加；这种由于资源争用导致的任务减速称为干扰 interference。 争用高会导致延迟好理解，也就是 CPU 利用率高，为什么会导致尾部延迟呢，感觉不是直接原因 我们的目标是保持高 CPU 利用率和严格的性能隔离（对于吞吐量和尾部延迟），在资源使用频繁变化的真实条件下，从而干扰也频繁变化。 实现微秒级反应时间有两个挑战。首先，共享 CPU 中存在多种干扰（超线程、内存带宽、LLC 等），在微秒级时间尺度上准确检测每种干扰的正确控制信号是困难的。其次，现有系统在收集控制信号或快速调整资源分配方面面临过多的软件开销。 资源监控应该可以用 eBPF 做？ 为了克服这些挑战，我们提出了一种 interferenceaware CPU scheduler, Caladan。Caladan 由一个集中式的专用调度核心组成，该核心收集控制信号并做出资源分配决策，以及一个名为 KSCHED 的 Linux 内核模块，该模块高效地调整资源分配。我们的调度核心区分 high-priority, latency-critical (LC) tasks and low-priority, besteffort (BE) tasks。为了避免硬件分区带来的反应时间限制（§3），Caladan 完全依赖核心分配来管理干扰。 Caladan 使用精心挑选的一组 control signals 和相应的动作，以快速准确地检测和响应微秒级时间尺度上的干扰。我们观察到干扰有两个相互关联的影响：首先，干扰会减慢核心的执行速度（更多的缓存未命中、更高的内存延迟等），影响请求的服务时间；其次，随着核心速度减慢，计算能力下降；当它低于提供的负载时，排队延迟会急剧增加。 Caladan 的调度器针对这些影响进行了优化。它收集内存带宽使用和请求处理时间的细粒度测量数据，分别用于检测内存带宽和超线程干扰。 KSCHED 内核模块加速了调度操作，如唤醒任务和收集干扰指标。它通过分摊发送中断的成本，将调度工作从调度核心 offloading 到任务的核心，并提供一个非阻塞 API， 说实话完全看不懂了。。不太好理解在做什么 据我们所知，Caladan 是第一个能够在频繁变化的干扰和负载下同时保持严格性能隔离和高 CPU 利用率的系统 前文说 resource partitioning is neither necessary nor sufficient 这里应该是说分区是不必要的，但是能保证性能隔离 为了实现这些好处，Caladan 对应用程序提出了两个新的要求：采用自定义的运行时系统进行调度和 LC 任务需要暴露其内部并发性（§8）。作为交换，Caladan 能够比最先进的资源分区系统 Parties（[12]）报告的典型速度快 500,000 倍地收敛到正确的资源配置。我们展示了这种加速在将 memcached 与依赖垃圾收集的 BE 任务共存时，带来了 11,000 倍的尾部延迟减少。此外，我们展示了 Caladan 具有高度的通用性，能够扩展到多个任务，并在共存多种工作负载（memcached、内存数据库、闪存存储服务、x264 视频编码器、垃圾收集器等）时保持相同的好处。 假设是：custom runtime system, LC tasks exposes their internal concurrency 低延迟任务需要暴露并行性？ ","date":"2024-10-09","objectID":"/posts/paper-caladan/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#introduction"},{"categories":["OS"],"collections":null,"content":"Motivation 干扰未被迅速缓解时性能如何下降 许多工作负载表现出阶段性行为，在亚秒级时间尺度上急剧改变它们使用的资源类型和数量。例如，压缩、编译、Spark 计算作业和垃圾收集器 为了更好地理解与时间变化干扰相关的挑战，我们考虑当我们将一个 LC 任务 memcached[43]与一个由于垃圾收集而表现出阶段性行为的 BE 工作负载共存时会发生什么。 在这个实验中，我们向 memcached 提供一个固定的负载，并在两个任务之间 statically partition cores 这个例子说明了固定核心分区是不够的，并且还表明了为了有效缓解干扰，核心重新分配的速度是必要的。 就是先将 CPU 固定给特定的任务，实现性能隔离，但是发生了干扰，无法应对突发负载，需要重新分配 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#motivation"},{"categories":["OS"],"collections":null,"content":"Background 共享 CPU 时可能发生的三种干扰形式：hyperthreading interference, memory bandwidth interference, and LLC interference. 超线程干扰通常在任务运行在兄弟核心时存在，因为 CPU 会划分某些物理核心资源（例如，微操作队列），但其严重程度取决于共享资源（L1/L2 缓存、预取器、执行单元、TLB 等）是否被争用。另一方面，内存带宽和 LLC 干扰的强度可能会有所不同，但会影响共享同一物理 CPU 的所有核心。随着内存带宽使用量的增加，由于干扰，内存访问延迟会逐渐增加，直到内存带宽饱和；访问延迟随后会呈指数级增加[62]。LLC 干扰由每个应用程序使用的缓存量决定：当需求超过容量时，LLC 未命中率会增加。 LLC 干扰 (Last Level Cache Interference) LLC 干扰是指在多任务或多线程环境中，多个任务或线程共享最后一级缓存（LLC）时，由于缓存资源的争用而导致的性能下降。 DDIO data direct IO 可以直接将网络数据推到 LLC ","date":"2024-10-09","objectID":"/posts/paper-caladan/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#background"},{"categories":["OS"],"collections":null,"content":"Existing Approaches to Interference 最先进的系统如 Heracles[38]和 Parties[12]通过动态分区资源（如核心和 LLC 分区大小）来处理干扰 首先，这两个系统使用应用程序级别的尾部延迟测量来检测干扰，这些测量必须在数百毫秒内进行，以获得稳定的结果；Parties 的作者发现，较短的间隔会产生“噪声和不稳定的结果” 因此，Heracles 和 Parties 至少需要 50 倍于我们例子中 GC 周期的时间来适应干扰的变化。 除了收敛速度，现有系统还存在可扩展性限制。例如，典型的数据中心服务器必须同时处理多个 LC 和 BE 任务[66, 71]，但 Heracles 仅限于单个 LC 任务（和许多 BE 任务） 这些系统所依赖的硬件机制也施加了限制。例如，超线程缺乏对资源分区的控制，因此 Heracles 和 Parties 完全关闭了它们。 所以一方面是现有的处理干扰策略太慢，毫秒级别，资源分配慢，convergence speed 慢，扩展性也不行，有些仅限于单个 LC 任务，也依赖于硬件，比如不支持超线程。 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#existing-approaches-to-interference"},{"categories":["OS"],"collections":null,"content":"Limitations of Hardware Extensions 英特尔的 CAT（Cache Allocation Technology）技术可以将 LLC 分区，但分区配置的变化需要相当长的时间才能生效。 内存带宽分配（Memory Bandwidth Allocation，MBA） 太接近硬件设计了这一篇论文，我感觉只要了解一下 interference 的原理，和现有解决方案的不足，以及 caladan 怎么做的就差不多了 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#limitations-of-hardware-extensions"},{"categories":["OS"],"collections":null,"content":"Challenges and Approach 我们的总体目标是保持性能隔离的同时最大化 CPU 利用率。 管理干扰变化需要微秒级反应时间。 因此 Caladan 的方法是通过控制核心如何分配给任务来管理干扰。 之前的系统已经将核心调整作为其管理干扰策略的一部分[12, 28, 38, 70]，但 Caladan 是第一个完全依赖核心分配来管理多种干扰形式的系统。 two key challenges: Sensitivity: 为了快速和有针对性的反应，Caladan 需要能够在微秒内识别干扰的存在及其来源（任务和争用资源）的控制信号。常用的性能指标如 CPI[71]或尾部延迟[12, 38]（以及硬件机制如 MBM 和 CMT）在短时间内太嘈杂，无法有用。像排队延迟[8, 42, 47, 68]这样的指标可以在微秒级时间尺度上测量，但无法识别干扰的来源，只能表明任务的性能正在下降。 Scalability: 现有系统严重依赖 Linux 内核来收集控制信号和调整资源分配（例如，使用 sched_setaffinity() 来调整核心分配）。不幸的是，Linux 在这些操作中增加了开销，并且在存在干扰以及核心和任务数量增加时，这些开销会增加。 我们通过仔细选择能够快速检测干扰的控制信号，并专门分配一个核心来监控这些信号并在干扰发生时采取行动来解决敏感性挑战。我们通过一个名为 KSCHED 的 Linux 内核模块来解决可扩展性挑战。我们将在下面更详细地描述这些内容。 什么信号？ 专用核心调度，解决信号问题 内核扩展、绕过 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#challenges-and-approach"},{"categories":["OS"],"collections":null,"content":"Caladan’s Approach Caladan 专门分配一个核心，称为调度器，以持续轮询和收集一组控制信号，时间尺度为微秒级。 到底是什么信号啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 看一半才讲也太夸张了这论文 对于超线程，我们假设当两个兄弟核心都处于活动状态时，干扰总是存在（因为某些物理核心资源被分区），并专注于减少对尾部延迟有影响的请求的干扰——即运行时间最长的请求。我们测量请求处理时间以识别这些请求。 于内存带宽，我们测量全局内存带宽使用情况以检测 DRAM 饱和，并测量每个核心的 LLC 未命中率以将使用情况归因于特定任务。对于像 LLC 这样的情况，我们无法直接测量或推断干扰，我们仍然可以测量干扰的一个关键副作用：由于计算能力减少而增加的排队延迟。通过专注于干扰驱动的控制信号，Caladan 可以在服务质量下降之前检测到问题。 the longest running requests, global Memory Bandwidth Usage Per-Core LLC Miss Rates, queueing delay 最后，Caladan 引入了一个名为 KSCHED 的 Linux 内核模块。KSCHED 在微秒级时间尺度上跨多个核心同时执行调度功能，即使在存在干扰的情况下也是如此。KSCHED 通过三种主要技术实现这些目标：（1）它在 Caladan 管理的所有核心上运行，并将调度工作从调度核心转移到运行任务的核心；（2）它利用硬件对多播处理器间中断（IPIs）的支持，以分摊同时启动多个核心操作的成本；（3）它提供了一个完全异步的调度器接口，以便调度器可以在等待远程核心完成操作的同时发起操作并执行其他工作。 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#caladans-approach"},{"categories":["OS"],"collections":null,"content":"Design ","date":"2024-10-09","objectID":"/posts/paper-caladan/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#design"},{"categories":["OS"],"collections":null,"content":"Overview 图 2 展示了 Caladan 的关键组件及其之间的共享内存区域。Caladan 与 Shenango[47]共享一些架构和实现构建块：每个应用程序都与一个运行时系统链接，一个专用的调度核心（以 root 权限运行）忙碌轮询共享内存区域以收集控制信号并进行核心分配。这两个系统都设计为在正常 Linux 环境中互操作，可能管理可用核心的子集。 Shenango 使用排队延迟作为其唯一的控制信号来管理负载变化； Caladan 使用多个控制信号来管理多种类 外，Shenango 的调度核心将网络处理与 CPU 调度结合在一起；Caladan 的调度核心仅负责 CPU 调度，消除了数据包处理瓶颈（§6）型的干扰以及负载变化。 Caladan 的运行时系统提供“绿色”线程和内核旁路 I/O，使用工作窃取来平衡负载，并在没有工作可窃取时让出核心。这使得管理干扰更容易，并能导出正确的每任务控制信号。 用户为每个任务配置保证核心和突发核心，任务被指定为 LC（延迟敏感型）或 BE（尽力而为型）。BE 任务以较低的优先级运行，仅在 LC 任务不需要时分配突发核心，并根据需要进行限制以管理干扰。 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#overview"},{"categories":["OS"],"collections":null,"content":"The Caladan Scheduler 独立的控制器模块检测内存带宽和超线程干扰，分别对核心分配施加约束并在必要时撤销核心。 撤销？ 调度器从三个来源收集控制信号。 首先，运行时系统提供请求处理时间和排队延迟的信息。 其次，DRAM 控制器提供全局内存带宽使用情况的信息。 第三，KSCHED 提供每个核心的 LLC 未命中率信息，并在任务自愿让出时通知调度器核心。 感觉就是加了这些指标 后面的就不看了，理解 Caladan 是什么，怎么做的（新指标、KSched） 主要是将一些 LC 和 BE 任务，不再需要资源分区 而是通过分配和调度，减少干扰，降低尾部延迟 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#the-caladan-scheduler"},{"categories":["OS"],"collections":null,"content":"Implementation ","date":"2024-10-09","objectID":"/posts/paper-caladan/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#implementation"},{"categories":["OS"],"collections":null,"content":"Evaluation ","date":"2024-10-09","objectID":"/posts/paper-caladan/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#evaluation"},{"categories":["OS"],"collections":null,"content":"Comparison to Other Systems ","date":"2024-10-09","objectID":"/posts/paper-caladan/:9:1","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#comparison-to-other-systems"},{"categories":["OS"],"collections":null,"content":"Diverse Colocations ","date":"2024-10-09","objectID":"/posts/paper-caladan/:9:2","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#diverse-colocations"},{"categories":["OS"],"collections":null,"content":"Microbenchmarks ","date":"2024-10-09","objectID":"/posts/paper-caladan/:9:3","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#microbenchmarks"},{"categories":["OS"],"collections":null,"content":"Discussion Caladan 要求应用程序使用其运行时系统，因为它依赖于运行时系统向调度器导出控制信号，并快速将线程和数据包处理工作映射到频繁变化的可用核心集上。我们的运行时系统不是完全 Linux 兼容的，但它提供了一个现实的并发编程模型（继承自 Shenango），包括线程、互斥锁、条件变量和同步 I/O[47]。 Caladan 还包括一个部分兼容层，用于系统库（例如，libpthread），可以在不修改的情况下支持 PARSEC[9]，使我们对设计在未来支持未修改的 Linux 应用程序具有一定的信心。不使用我们运行时的应用程序可以共存在同一台机器上，但它们必须运行在不受 Caladan 管理的核心上，并且如果它们引起干扰，则无法被限制。 为什么不完全支持 linux，为什么 Caladan 更基本的要求是需要 LC 任务向运行时系统暴露其内部并发性（例如，通过生成绿色线程），这可能需要对现有代码进行更改。如果并发性不足，任务将无法从额外核心中受益，从而阻碍 Caladan 管理负载或干扰变化的能力。通常，我们建议任务通过为每个连接或每个请求生成一个线程来暴露并发性。例如，通常 memcached 为每个线程复用多个 TCP 连接，但我们修改它以生成一个单独的线程来处理每个 TCP 连接。 这个很重要，因为大部分 web 应用要么用线程池要么用事件驱动，每个线程复用 TCP 但一个线程用一个 TCP，这是否就降低了 memcached 的并发量和最大用户量、吞吐量，或者是线程数量增加？ 其实除去尖峰， p99.9 尾部延迟是增加了的， Caladan 的当前实现有两个限制。首先，它无法管理跨 NUMA 节点的干扰。NUMA 引入了额外的共享资源，这些资源容易受到干扰，包括跨套接字互连和每个节点的独立内存控制器。幸运的是，这些资源的高精度性能计数器是可用的，我们计划在未来探索 NUMA 感知的干扰缓解策略，例如在节点之间撤销核心或迁移任务。其次，我们的调度策略不会最小化跨超线程兄弟的瞬态执行攻击的威胁[3, 11, 65]。理想情况下，只有相互信任的任务才允许在兄弟核心上运行。在撰写本文时，Linux 内核正在开发类似的功能[13]。 ","date":"2024-10-09","objectID":"/posts/paper-caladan/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Caladan","uri":"/posts/paper-caladan/#discussion"},{"categories":["OS"],"collections":null,"content":"ghOSt: Fast \u0026amp; Flexible User-Space Delegation of Linux Scheduling Google 的 ghOSt，在这之前还有 Snap MicroKernel Approach to Host Networking，是一个用户网络协议栈，支持 RDMA 等等。 ghOSt 更进一步，可以兼容 Linux，更加容易使用，更灵活，灵感来自微内核，OS 委托 delegate 给用户空间。 而且 Shenango, Caladan, Shinjuku 等提出了可以使用 centralized model 来单独调度，可以达到微秒级别的 work-conservation 也有 IX, ZygOS, Shinjuku, Shenango, Caladan, Snap 提出了 container-like data plane os 是 LibOS 那一套吗，用于 kernel-bypass ghOSt 的想法还是没有太天马行空，是很实际的，扩展 Linux 而不是从头搞，可能考虑到 google 内部需要用于生产环境？ ","date":"2024-10-07","objectID":"/posts/paper-ghost/:1:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#ghost-fast--flexible-user-space-delegation-of-linux-scheduling"},{"categories":["OS"],"collections":null,"content":"Abstract 我们推出了 ghOSt，这是我们的基础设施，用于将内核调度决策委托给用户空间代码 在定制的数据平面操作系统中采用专门的调度策略可以在数据中心环境中提供令人信服的性能结果 ghOSt 在 Linux 环境中提供了将调度策略普遍委托给用户空间进程的功能。ghOSt 提供了 state encapsulation, communication, and action mechanisms 允许在用户空间代理内部复杂地表达调度策略，同时协助同步。 程序员可以使用任何语言来开发和优化策略，这些策略可以在不重启主机的情况下进行修改。 ghOSt 支持广泛的调度模型，from per-CPU to centralizedrun-to-completion to preemptive, and incurs low overheads for scheduling actions。我们在包括 Google Snap 和 Google Search 在内的学术和现实世界工作负载上展示了 ghOSt 的性能。我们证明，通过使用 ghOSt 而不是内核调度器，我们可以迅速达到相当的吞吐量和延迟，同时使 policy optimization, non-disruptive upgrades, and fault isolation 成为可能，从而为我们的数据中心工作负载服务。我们开源了我们的实现，以促进基于 ghOSt 的未来研究和开发 schedule policy，不需要重启，怎么做到的 ghost 本身不是 policy，而是 process 支持 bpf ghost 使用 txn 来进行调度提交 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:2:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction CPU scheduling plays an important role in application performance and security. Tailoring policies for specific workload types can substantially improve key metrics such as latency, throughput, hard/soft real-time characteristics, energy efficiency, cache interference, and security. 尽管 Linux 支持多种调度实现，但为每个应用程序量身定制、维护和部署不同的机制在大型集群中是难以管理的。 在这篇论文中，我们介绍了 ghOSt 的设计及其在生产环境和学术用例中的评估。ghOSt 是一个用于原生 OS 线程的调度器，它将策略决策委托给用户空间。ghOSt 的目标是根本改变调度策略的设计、实现和部署方式。ghOSt 提供了用户空间开发的灵活性和易于部署的优势，同时仍然能够进行微秒级调度。ghOSt 为用户空间软件定义复杂的调度策略提供了抽象和接口，从每 CPU 模型到系统范围内的（集中式）模型。重要的是，ghOSt 将内核调度机制与策略定义解耦。机制位于内核中，很少改变。策略定义位于用户空间，并迅速改变。 ghOSt 给我的感觉更像是把 snap 之类的或者微内核落地了，并且兼容 Linux 通过调度原生线程，ghOSt 无需任何更改即可支持现有应用程序。在 ghOSt（图 1）中，调度策略逻辑在正常 Linux 进程中运行，称为代理，这些代理通过 ghOSt API 与内核交互。 内核通过消息队列（第 3.1 节）在异步路径中通知代理所有托管线程的状态变化——例如，线程创建/阻塞/唤醒。 然后代理使用基于事务的 API 在同步路径中提交线程的调度决策（第 3.2 节）。 ghOSt 支持多个策略的并发执行、故障隔离以及为不同应用程序分配 CPU 资源。重要的是，为了使现有系统平滑过渡到使用 ghOSt，它可以与其他在内核中运行的调度器共存，如 CFS（第 3.4 节）。 我们证明了 ghOSt 使我们能够定义各种策略，其性能与现有调度器在学术和生产工作负载上的表现相当或更好（第 4 节）。我们描述了关键 ghOSt 操作的开销（第 4.1 节）。我们展示了 ghOSt 的开销很小，从 265 纳秒的消息传递，到进入代理的上下文切换需要几百纳秒，再到 888 纳秒调度一个线程，使得 ghOSt 的调度开销仅略高于现有内核调度器。通过摊销，这些开销允许单个 ghOSt 代理每秒调度超过 200 万条线程（图 5）。我们通过实现集中式和抢占式策略来评估 ghOSt，这些策略在存在高请求分散度和对抗者的情况下实现了高吞吐量和低尾部延迟（第 4.2 节）。 我们将 ghOSt 与 Shinjuku，一个专门的现代数据平面进行比较，以展示 ghOSt 的最小开销和具有竞争力的微秒级性能（与 Shinjuku 相差不超过 5%），同时支持更广泛的工作负载集，包括多租户。我们还为 Snap，我们在数据中心使用的生产包交换框架实现了一个策略，导致与 MicroQuanta，我们目前使用的软实时调度器相比，在某些情况下尾部延迟降低了 5-30%（第 4.3 节）。然后，我们为运行 Google Search 的机器实现了 ghOSt 策略（第 4.4 节）。通过根据机器拓扑定制策略，总共不到 1000 行代码，我们证明了 ghOSt 匹配了现有调度器提供的吞吐量，而且通常将延迟提高了 40-50%。最后，我们为虚拟机实现了一个 ghOSt 策略，该策略对最近发现的微体系结构漏洞具有安全性，并显示 ghOSt 的性能与纯内核策略实现具有竞争力。借助 ghOSt，以前需要广泛内核修改的调度策略现在只需几十或几百行代码就能实现。 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:3:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#introduction"},{"categories":["OS"],"collections":null,"content":"Background \u0026amp; Design Goals Scheduling in Linux: Linux supports implementing multiple policies via scheduling classes. 这些类按优先级排序：使用较高优先级类调度的线程将抢占使用较低优先级类调度的线程。为了优化特定应用的性能，开发者可以修改调度类以更好地满足应用的需求，例如，优先处理应用的线程以减少网络延迟[21]，或使用实时策略调度应用的线程以满足数据库查询的截止时间[47]。 然而，实现在内核中实现和维护这些策略的复杂性导致许多开发者选择使用现有的通用策略，如 Linux 完全公平调度器（CFS [7]）。因此，Linux 中现有的类设计旨在支持尽可能多的用例。这就使得使用这些过于通用的类来优化高性能应用以充分利用硬件变得具有挑战性。 CFS 完全公平调度 感觉这方向的论文，大部分都是让通用变得专用，一小部分让几个专用变成通用 Implementing schedulers is hard: ghOSt 使调度器的更新、测试和调优成为可能，而无需更新内核和/或重启机器和应用程序。 Deploying schedulers is even harder: ghOSt 是一个运行在用户空间的内核调度器，调度原生 OS 线程 User-level threading is not enough: ghOSt enables the best of both worlds by guaranteeing control over response time while allowing flexible sharing of CPU resources. ghost 可以调度用户级线程吗？ Custom scheduler/data plane OS per workload is impractical: Shinjuku、Shenango 内核实现不易更改，且对于其他工作负载的性能较差。 Shenango [1]有 8,399 行代码，只能为网络工作负载实现单一的线程调度策略。添加额外的策略需要大量的代码修改。此外，这两个系统都无法在没有特定 NICs 的机器上运行，且 Shenango 无法调度非网络工作负载。 Custom scheduling via BPF is insufficient: 一个吸引人的方式来定制内核调度是将 BPF [61]程序注入内核调度器，就像为其他内核子系统所做的那样[62]。确实可以实现一个 Linux 调度器类，其函数指针调用 BPF 程序来决定下一个运行的线程。不幸的是，BPF 在表达能力和可访问的内核数据结构方面有限制。例如，BPF 验证器必须能够确定循环将退出，BPF 程序不能使用浮点数。 更重要的是，BPF 程序是同步运行的，这意味着它们必须快速响应调度事件，直到完成前会阻塞 CPU。相比之下，异步调度器可以在稍后的时间接收调度事件并对此做出反应。因此，如第 3.3 节所述的全局调度器这样的异步模型，可以根据由多个调度事件组成的系统更广泛的视角来做出调度决策。话虽如此，BPF 在 ghOSt 中扮演了重要角色，用于加速快速路径操作，并在内核性能关键点提供定制化，正如第 3.2 节所解释的。 BPF 调度应该是有前景的？ ","date":"2024-10-07","objectID":"/posts/paper-ghost/:4:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#background--design-goals"},{"categories":["OS"],"collections":null,"content":"Design Goals Policies should be easy to implement and test: implementing systems in userspace rather than kernel space simplifies development and enables faster iteration as popular languages and tools can be used. Scheduling expressiveness and efficiency. Enabling scheduling decisions beyond the per-CPU model: The Linux scheduler ecosystem implements scheduling algorithms that make per-CPU scheduling decisions, 最近的工作表明，通过集中式、专用轮询调度线程[1, 21, 25]，即采用集中式模型，可以为微秒级工作负载带来显著的尾部延迟改进。 Supporting multiple concurrent policies Non-disruptive updates and fault isolation 怎么做到无中断更新和故障隔离？虚拟化？ ","date":"2024-10-07","objectID":"/posts/paper-ghost/:4:1","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#design-goals"},{"categories":["OS"],"collections":null,"content":"Design ghOSt overview: 图 1 总结了 ghOSt 的设计。用户空间代理做出调度决策，并指示内核如何在 CPU 上调度原生线程。ghOSt 的内核侧作为一个调度类实现，类似于常用的 CFS 类。这个调度类为用户空间代码提供了一个丰富的 API 来定义任意的调度策略。为了帮助代理做出调度决策，内核通过消息和状态字向代理暴露线程状态（第 3.1 节）。然后，代理通过事务和系统调用来指导内核的调度决策（第 3.2 节）。 本节将使用两个示例：per-CPU 调度和集中式调度。传统的内核调度策略，如 CFS，是每 CPU 调度器。尽管这些策略通常采用负载均衡和工作窃取来平衡系统内的负载，但它们仍然从每 CPU 的角度操作。集中式调度的例子类似于 Shinjuku [25]、Shenango [1]和 Caladan [21]中提出的模型。在这种情况下，有一个单一的全局实体不断地观察整个系统，并为其管辖下的所有线程和 CPU 做出调度决策。 Threads and CPUs: ghOSt 调度原生线程在 CPU 上。本节提到的所有线程都是原生线程，与第 2 节提到的用户级线程相对。我们将逻辑执行单元称为 CPU。例如，我们认为一台拥有 56 个物理核心和 112 个逻辑核心（超线程）的机器有 112 个 CPU。 所以不能调度用户线程？ Partitioning the machine. ghOSt supports multiple concurrent policies on a single machine using enclaves 系统可以按照 CPU 粒度划分为多个独立的 enclave，每个 enclave 运行自己的策略，如图 2 所示。从调度的角度来看，这些 enclave 是隔离的。当在单台机器上运行不同的工作负载时，分区尤其有意义。通常可以通过机器拓扑设置这些 enclave 的粒度，如每个 NUMA 插槽或每个 AMD CCX [40]。enclave 也有助于隔离故障，将代理崩溃的损害限制在其所属的 enclave 内 enclaves 是什么 ghOSt userspace agents. 调度策略逻辑是在用户空间代理中实现的 为了实现容错和隔离，如果一个或几个代理崩溃，系统将回退到默认调度器，如 CFS。此时，机器仍完全功能正常，直到启动新的 ghOSt 用户空间代理——无论是最后一个已知的稳定版本还是带有修复的新修订版。得益于崩溃恢复的特性，更新调度策略相当于重新启动用户空间代理，而无需重启机器。这一特性使得可以针对各种硬件和工作负载进行实验和快速策略定制。开发者可以对策略进行微调并简单地重新启动代理。关于 ghOSt 策略的动态更新将在第 3.4 节中讨论。 崩溃后怎么回退，是全部回退到 per-CPU 还是什么，如果之前的是 centrialized 的怎么办 无论调度模型是每 CPU 还是集中式，每个由 ghOSt 管理的 CPU 都有一个本地代理，如图 2 所示。在每 CPU 的情况下，每个代理负责其自身 CPU 的线程调度决策。在集中式的情况下，一个单一的全局代理负责 enclave 中所有 CPU 的调度。所有其他本地代理都是不活跃的。每个代理都在一个 Linux pthread 中实现，所有代理都属于同一个用户空间进程。 agent 到底代表什么，调度器？ ","date":"2024-10-07","objectID":"/posts/paper-ghost/:5:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#design"},{"categories":["OS"],"collections":null,"content":"Kernel-to-Agent Communication Exposing thread state to the agents. 为了让代理能够对其管辖下的线程做出调度决策，内核必须向代理暴露线程状态。 一种方法是将现有的内核数据结构内存映射到用户空间，例如 task_structs，这样代理可以检查它们以推断线程状态。然而，这些数据结构的可用性和格式在不同的内核和内核版本之间有所不同，紧密地将用户空间策略实现与内核版本耦合在一起。另一种方法是通过 sysfs 文件暴露线程状态，采用/proc/pid/...的形式。然而，文件系统 API 对于快速路径操作效率低下，难以支持微秒级的策略：open/read/fseek 最初是为块设备设计的，速度太慢且复杂（例如，需要错误处理和数据解析）。 ghOSt messages. 在 ghOSt 中，内核使用表 1 中列出的消息通知用户空间代理线程状态的变化。例如，如果一个线程被阻塞但现在准备运行，内核会发布一个 THREAD_WAKEUP 消息。此外，内核还通过 TIMER_TICK 消息通知代理定时器滴答。为了帮助代理验证它们的决策基于最新的状态，消息还包括序列号，这一点将在后面解释。 Message queues: 消息通过消息队列传递给代理。在集中式例子中，所有线程都被分配到全局队列（图 2，右）。关于 CPU 事件的消息，如 TIMER_TICK，会被路由到与 CPU 相关联的代理线程的队列。 这个很有意思，这些队列从哪里来的？ 虽然实现队列的方法有很多，但我们选择在共享内存中使用自定义队列，以高效地处理代理唤醒（如下所述）。我们认为现有的队列机制对于 ghOSt 来说是不足的，因为它们仅存在于特定的内核版本中。例如，BPF 系统通过 BPF 环形缓冲区[BPF ring buffers][65]将 BPF 事件传递到用户空间，最近版本的 Linux 还通过 io_uring[66]将异步 I/O 消息传递到用户空间。这两种都是快速的无锁环形缓冲区，同步消费者/生产者访问。然而，旧版本的 Linux 内核和其他操作系统不支持它们。 BPF ring buffer, io_uring 所以 ghost 是自己做的，而不是用 bpf, io_uring 吗 Thread-to-queue association. 在 ghOSt 的 enclave 初始化之后，enclaves 中有一个默认队列。代理进程可以使用 CREATE/DESTROY_QUEUE() API 创建/销毁队列。添加到 ghOSt 的线程默认被分配为向默认队列发送消息。代理可以通过 ASSOCIATE_QUEUE()改变这种分配。 Queue-to-agent association. 队列可以配置为当消息被生产到队列时唤醒一个或多个代理。代理可以通过 CONFIG_QUEUE_WAKEUP()配置唤醒行为。在每 CPU 的例子中，每个队列与一个 CPU 关联，并配置为唤醒相应的代理。在集中式例子中，**队列由全局代理连续轮询，因此唤醒是多余的，因此没有配置。**消息被生产到队列并在代理中观察到的延迟将在第 4.1 节中讨论。 代理唤醒使用标准的内核机制来唤醒阻塞的线程。这涉及识别要唤醒的代理线程，将其标记为可运行，可选地向目标 CPU 发送中断以触发重新调度，并执行上下文切换到代理线程。 这里有什么办法绕过中断和上下文切换吗 Moving threads between queues/CPUs. 在我们的每 CPU 例子中，为了实现 CPU 之间的负载均衡和工作窃取，代理可以通过 ASSOCIATE_QUEUE()改变线程消息到队列的路由。正确的跨队列到代理的消息路由协调取决于代理实现（在用户空间）。如果一个线程在原始队列中有待处理消息时从一个队列关联到另一个队列，关联操作将会失败。在这种情况下，代理必须排空原始队列，然后再重新发出 ASSOCIATE_QUEUE()。 协程、用户级线程是如何处理的？ Synchronizing agents with the kernel 代理根据通过消息观察到的系统状态做出调度决策 每个代理都有一个序列号，记为 𝐴𝑠𝑒𝑞，每当有消息被发布到与该代理关联的队列时，这个序列号就会递增。 Exposing sequence numbers via shared memory ghOSt 允许代理通过状态字高效地轮询有关线程和 CPU 状态的辅助信息，这些状态字被映射到代理的地址空间中。为了简洁起见，我们只讨论如何使用状态字向代理暴露序列号 𝐴𝑠𝑒𝑞 和 𝑇𝑠𝑒𝑞。当内核更新线程或代理的序列号时，它也会更新相应状态字。然后，代理可以从共享映射中的状态字读取序列号。 轮询线程和 CPU 状态，那能用复杂结构实现 epoll 吗 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:5:1","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#kernel-to-agent-communication"},{"categories":["OS"],"collections":null,"content":"Agent-to-Kernel Communication We now describe how the agents instruct the kernel which thread to schedule next. Sending scheduling decisions via transactions: Agents send scheduling decisions to the kernel by committing transactions 代理通过提交事务向内核发送调度决策。代理必须能够调度其本地 CPU（每 CPU 的情况）以及其他远程 CPU（集中式的情况）。提交机制必须足够快，以支持微秒级的策略，并且能够扩展到数百个核心。 顺便提一句，将调度接口设计为共享内存中的事务，未来可以将调度决策卸载到可以访问该内存的外部设备上。 transactions in shared memory 受到事务内存[67]和数据库系统[68]的启发，我们设计了自己的事务 API，通过共享内存实现。这些系统支持快速的分布式提交操作，具有原子性语义，并且可以有多个提交同时针对同一个远程节点。ghOSt 代理也需要类似的属性。 代理使用 TXN_CREATE()辅助函数在共享内存中打开一个新的事务。代理写入要调度的线程的 TID 以及要调度线程的 CPU 的 ID。在每 CPU 的例子中，每个代理只调度自己的 CPU。当事务填写完毕后，代理通过 TXNS_COMMIT()系统调用将其提交给内核，这会触发提交过程并促使内核发起上下文切换。图 3 展示了一个简化的例子。 Group commits: 在集中式调度的例子中，为了让 ghOSt 能够扩展到数百个 CPU 和每秒数十万次的事务，我们必须减轻系统调用的高昂成本。我们通过引入组合提交来分摊事务的成本。组合提交还减少了发送到其他 CPU 的中断数量，类似于 Caladan [21]的做法。代理通过将所有事务传递给 TXNS_COMMIT()系统调用来提交多个事务。这个系统调用将高昂的开销分摊到多个事务上。最重要的是，它通过使用大多数处理器中存在的批量中断功能来分摊发送中断的开销。内核不会为每个事务发送多个中断，而是向远程 CPU 发送一个批量中断，从而节省大量开销。 这里的事务是为了什么，隔离还是并发？ Sequence numbers and transactions. 在每 CPU 的例子中，提交事务的代理正在将其 CPU 让给它正在调度的目标线程。当代理正在运行时，队列中新发布的消息不会引起唤醒，因为代理已经在运行。然而，队列中的新消息可能是来自优先级更高的线程，并且如果代理知道这一点的话，会影响调度决策。代理只有在下次唤醒时才有机会检查该消息，而这已经太晚了。我们现在通过序列号来解释如何解决这一挑战，适用于每 CPU 的例子。集中式调度的略有不同情况将在第 3.3 节中解释。 我们使用代理序列号 𝐴𝑠𝑒𝑞 来解决这一挑战。代理通过检查代理线程的状态字来轮询其 𝐴𝑠𝑒𝑞。回想一下，当新的消息被发布到与代理关联的队列时，𝐴𝑠𝑒𝑞 会递增。操作顺序是：1) 读取 𝐴𝑠𝑒𝑞；2) 从队列中读取消息；3) 做出调度决策；4) 将 𝐴𝑠𝑒𝑞 与事务一起发送给 TXNS_COMMIT()。如果与事务一起发送的 𝐴𝑠𝑒𝑞 比内核观察到的当前 𝐴𝑠𝑒𝑞 旧（即，有新消息被发布到代理的队列），则事务被认为是“过期”的，并将以 ESTALE 错误失败。然后，代理将清空其队列以检索较新的消息，并重复该过程。 这里的 challenge 没太理解，没有具体的例子 Accelerating scheduling with BPF: ghOSt 提供的用户级灵活性并非没有代价：message delivery 和 group scheduling 最多可能耗费 5 微秒（参见第 4.1 节中的表 3）；在集中式调度模型中，一个线程可能要等到整个集中式调度循环结束才能为其提交调度决策（在第 4.4 节中为 30 微秒）。 5us 和 30us 看上去很低，为什么说这是代价？ ","date":"2024-10-07","objectID":"/posts/paper-ghost/:5:2","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#agent-to-kernel-communication"},{"categories":["OS"],"collections":null,"content":"The Centralized Scheduler One global agent with a single queue: 在集中式调度中，存在一个单一的全局代理轮询单个消息队列，并为所有受 ghOSt 管理的 CPU 做出调度决策。如果指定的 CPU 上已经运行了一个 ghOSt 线程，事务将抢占之前的线程，转而运行新的线程。 Avoiding preemption of the global agent: 为了支持微秒级调度，全局代理必须持续运行，因为任何抢占都会直接导致调度延迟。 怎么知道是全局代理？最高优先级？ Sequence numbers and centralized scheduling: 在某些时候，全局代理可能对某个线程的状态有一个不一致的视图。例如，线程 𝑇 可能发布了 THREAD_WAKEUP 消息。全局代理接收到这条消息并决定在 𝐶𝑃𝑈𝑓 上调度 𝑇。与此同时，系统中的某个实体调用了 sched_setaffinity()，导致发布了 THREAD_AFFINITY 消息，禁止 𝑇 在 𝐶𝑃𝑈𝑓 上运行。我们需要一种机制来确保将 𝑇 调度到 𝐶𝑃𝑈𝑓 的事务会失败。 我们通过线程序列号来解决这个问题。回想一下，每个排队的消息 𝑀𝑇 都附带了线程序列号 𝑇𝑠𝑒𝑞，形式为（𝑀𝑇, 𝑇𝑠𝑒𝑞）。当代理为线程 𝑇 提交事务时，它会将事务连同它所知的最新线程序列号 𝑇𝑠𝑒𝑞 一起发送。当内核接收到事务时，它会验证事务中的线程的 𝑇𝑠𝑒𝑞 是否是最新的。如果不是，事务将以 ESTALE 错误失败。 这就是用事务的原因吗，避免 centralized scheduling 导致 global agent have inconsistent view of a thread state ","date":"2024-10-07","objectID":"/posts/paper-ghost/:5:3","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#the-centralized-scheduler"},{"categories":["OS"],"collections":null,"content":"Fault Isolation and Dynamic Upgrades Interaction with other kernel scheduling classes: 我们希望避免 ghOSt 线程对其他线程造成意外后果，比如饥饿、优先级反转、死锁等。 我们通过将 ghOSt 的内核调度类分配一个比默认调度类（通常是 CFS）更低的优先级来实现这一目标。结果是，系统中的大多数线程会抢占 ghOSt 线程。ghOSt 线程的抢占会导致创建一个 THREAD_PREEMPT 消息，触发相关的代理（该代理运行在不同的高优先级调度类中）做出调度决策。代理进一步决定如何处理抢占。 ghost 导致死锁、饥饿、优先级反转的原因是什么？抢占？ Dynamic upgrades and rollbacks. ghOSt 通过以下两种方式之一实现动态升级： (a) 替换代理，同时保持 enclave 基础设施完好； (b) 销毁 enclave 并重新开始。 没理解，这说的太简单了，怎么替换，怎么保持 enclave, enclave 也没感觉好好介绍到底是什么 ghOSt watchdog. ghOSt 或任何其他内核调度器中的调度错误可能有系统范围的影响。例如，一个 ghOSt 线程可能在持有内核互斥锁时被抢占，如果它长时间未被调度，可能会间接地阻塞其他线程，包括那些在 CFS 或其他 ghOSt 飞地中的线程。类似地，如果关键线程（如垃圾收集器和 I/O 轮询器）未被调度，机器将陷入停滞。作为一种安全机制，ghOSt 会自动销毁行为异常的代理飞地。例如，当内核检测到代理在用户可配置的毫秒数内未调度可运行线程时，会销毁飞地。 halt 所以需要 linux 来调度？还是 ghost 怎么自动销毁，这个配置怎么调才合适呢 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:5:4","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#fault-isolation-and-dynamic-upgrades"},{"categories":["OS"],"collections":null,"content":"Evaluation 我们的 ghOSt 评估集中在三个问题上： (a) ghOSt 特有的操作有哪些开销，这些操作在经典调度器中不存在（第 4.1 节）； (b) 使用 ghOSt 实现的调度策略与先前的工作（如 Shinjuku [25]）相比表现如何（第 4.2 节）； (c) ghOSt 是否是大规模和低延迟生产负载的有效解决方案，包括 Google Snap（第 4.3 节）、Google Search（第 4.4 节）和虚拟机（第 4.5 节）？ 特有的操作，比如消息传递 我还想看一个 evaluation 就是论文提到的死锁、优先级反转，能不能设计一个实验来复现，观察一下回退导致的问题 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#evaluation"},{"categories":["OS"],"collections":null,"content":"Analysis of ghOSt Overheads and Scaling LOC: ghOSt 已准备好用于生产，并且支持我们生产负载的一系列调度策略，比 CFS 少 40%的内核代码。 Experimental Setup: 实验在 Linux 4.15 上运行 4.15 是比较老的，19 年 5.0 发布，21 年应该 5.15 也发布了，24 年现在是 6.12，在 6.6 引入了新的 EEVDF process scheduler(Earliest eligible virtual deadline first scheduling) 替代 CFS，毕竟 CFS 是很老了 https://lwn.net/Articles/925371/ An EEVDF CPU scheduler for Linux CFS 追踪了每个进程获得的时间，实现公平调度 EEVDF 提出更多来解决一些约束，比如最大限度利用系统的内存缓存，最小化进程在 CPU 之间的移动 让所有 CPU 保持忙碌 但有种情况：有些进程不需要很多 CPU time 但是确实需要，是一种 latency requirements 所以需要优先级，但这是特权操作？ 对于每个进程，EEVDF 计算该进程应该获得的时间与实际获得的时间之间的差异; 这种差异称为“滞后”。 是为了解决 CFS 的一些启发式补丁吗？ Message delivery overhead: 在每 CPU 的例子中，向本地代理传递消息包括将消息添加到队列、上下文切换到本地代理以及从队列中移除消息。开销（725 纳秒）主要由上下文切换（410 纳秒）主导。在集中式例子中，向全局代理传递消息（265 纳秒）包括将消息添加到队列并在全局代理中移除消息，全局代理始终处于自旋状态。 Local scheduling: 在每 CPU 模型中，这是提交事务并在本地 CPU 上执行上下文切换，直到目标线程运行的开销。开销（888 纳秒）略高于 CFS 上下文切换开销（599 纳秒），主要是由于事务提交，但仍具有竞争力。 事务带来了一些问题 Remote scheduling: 在集中式调度模型中，代理端提交事务并发送跨处理器中断（IPI）。目标 CPU 处理 IPI 并执行上下文切换。代理的开销（668 纳秒）设定了每个代理每秒 1.5M 可调度线程的理论最大吞吐量。将 10 个不同 CPU 的事务组合起来，通过分摊 IPI 开销，理论最大值提高到 10 * 10^9 / 3964 = 2.52M 每秒可调度线程。根据这些数字，单个代理理论上可以在 100 个 CPU 的服务器上每秒调度大约 25,200 个线程。如果线程长度为 40 微秒，代理可以保持 100 个 CPU 忙碌。policy 开发者在设计 ghOSt 策略时应考虑每个代理的可扩展性限制。随着更多代理的加入，这一限制相对线性地改善。 这个 inter-processor interrupt (IPI) 会产生什么问题吗 Global agent scalability (Fig. 5). To show how a global agent scales, we analyze a simple round-robin policy. ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:1","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#analysis-of-ghost-overheads-and-scaling"},{"categories":["OS"],"collections":null,"content":"Comparison to Custom Centralized Schedulers Systems under comparison. We compared three implementations of the scheduling approach in Shinjuku [25], all serving a RocksDB workload [70]. We use one physical core for load generation with all systems. The Shinjuku system runs on Linux 4.4 as its Dune [35] driver fails to compile for newer versions. The other systems under comparison (ghOSt-Shinjuku and CFS-Shinjuku) run on Linux 4.15 with our ghOSt patches applied. RocksDB workload，这个有意思 Single Workload Comparison: 99.5% of requests - 4 𝜇s, 0.5% of requests - 10 ms. 反映了 ghOSt 在调度每个请求的线程时的额外开销，而 Shinjuku 则在自旋线程之间传递请求描述符。由于缺乏抢占，CFS-Shinjuku 比其他两个系统早约 30%达到饱和。 没看懂这个数据集。实验结果还是纯 shinjuku 比较好？是因为调度和消息传递的开销吗。 Multiple Workloads Comparison: 图 6c 显示，当我们把一个批处理应用程序与由 Shinjuku 管理的 RocksDB 工作负载共置时，即使 RocksDB 负载较低，批处理应用程序也无法获得任何 CPU 资源。 为了实现低延迟和批处理工作负载的安全共置，可以考虑使用面向线程的集中式调度系统，如 Shenango [1]。Shenango 的集中式调度器监控网络应用程序的负载，当应用程序负载较轻时，调度器将多余的 CPU 周期分配给批处理应用程序。然而，Shenango 不适合处理执行时间变化较大的请求，因此 RocksDB 工作负载的尾部延迟会远高于 Shinjuku。 将多余的 CPU 周期分配给批处理应用程序 shinjuku 的缺点使用 ghost 可以解决 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:2","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#comparison-to-custom-centralized-schedulers"},{"categories":["OS"],"collections":null,"content":"Google Snap 类似于 DPDK [72]，Snap 维护了轮询（工作）线程，负责与 NIC 硬件的交互，并代表重要服务运行自定义的网络和安全协议。Snap 可能会根据网络负载的变化决定生成或合并工作线程。 How are worker threads scheduled today? 和 Snap 的对比，感觉 ghost 就是用来增强 snap 的 在 64B 情况下 因此，当流量突发且包含大量 64B 消息时，ghOSt 的调度事件开销变得明显。对于 64KB 消息，ghOSt 在 99 百分位延迟以内表现与基线相似（在 15%范围内）。对于 99.9 百分位及以上，ghOSt 的尾延迟比基线低 5%到 30%。64KB 消息需要更多的处理（用于复制数据），因此导致较少的调度事件。ghOSt 在某些情况下表现优于 MicroQuanta 的原因是它可以在线程可用时重新定位工作线程。 其实看不太懂这个对比，是说 p99999 的表现更好吗 但是这也太严格了，只有 0.001% 的请求超过了延迟？p99 其实都差不多，但是 p999 好一点 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:3","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#google-snap"},{"categories":["OS"],"collections":null,"content":"Google Search 主要是尾延迟，tail latency 表现很不错就不看了 QPS、吞吐量和 CFS 没太差 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:4","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#google-search"},{"categories":["OS"],"collections":null,"content":"Protecting VMs from L1TF/MDS Attacks 这一章没理解，怎么突然多一个安全测试 使用三种调度策略：1) CFS，不提供对推测执行攻击的保护；2) 内核中的安全虚拟机核心调度；3) ghOSt 中的安全虚拟机核心调度。结果如表 4 所示。CFS 提供了更好的整体性能，但没有任何安全性。ghOSt 策略缓解了跨超线程攻击，并且在考虑 ghOSt 中的额外上下文切换开销的情况下，性能与内核中的核心调度版本相当。 主要是前文只是稍微提了一下安全、隔离，但是实现讲的比较模糊 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:6:5","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#protecting-vms-from-l1tfmds-attacks"},{"categories":["OS"],"collections":null,"content":"Future Work Accelerating scheduling with BPF：通过将一些代理职责委托给同步 BPF 回调来加速 ghOSt 是一个开放的研究领域。在§4.4 中，全局代理的调度循环耗时 30 微秒，这可能导致调度间隙。实际上，系统中的一些线程在阻塞之前只运行 5-30 微秒，导致这些间隙期间 CPU 空闲。我们可以使用§3.2 中描述的集成 BPF 程序来缓解这些调度间隙。 BPF 程序通过共享内存与用户空间通信，使用多个多生产者、多消费者环形缓冲区。代理将可运行的线程插入这些缓冲区，BPF 尝试运行它们。代理可以在 BPF 调度线程之前撤销线程。例如，全局代理可以为每个 NUMA 节点使用一个环形缓冲区；全局代理可以跟踪每个线程的首选 NUMA 节点，并在两个环之间进行负载均衡。 能用 bpf 来 bypass 一些上下文切换吗？ Tick-less scheduling 当 ghOSt 处于集中模式时，可以禁用所有 CPU 上的定时器滴答，以避免在虚拟机工作负载中出现昂贵的 VM 退出。在经典的每个 CPU 调度器中，滴答每毫秒触发一次调度器，以确保所有虚拟机之间的轮转抢占。不幸的是，这些滴答会导致虚拟机退出到主机内核上下文。 这个完全看不懂 ","date":"2024-10-07","objectID":"/posts/paper-ghost/:7:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#future-work"},{"categories":["OS"],"collections":null,"content":"Related Work ","date":"2024-10-07","objectID":"/posts/paper-ghost/:8:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#related-work"},{"categories":["OS"],"collections":null,"content":"Conclusion ","date":"2024-10-07","objectID":"/posts/paper-ghost/:9:0","tags":["Paper Reading"],"title":"Paper Reading: ghOSt: Fast \u0026 Flexible User-Space Delegation of Linux Scheduling","uri":"/posts/paper-ghost/#conclusion"},{"categories":["OS"],"collections":null,"content":"Ens ̄o: A Streaming Interface for NIC-Application Communication NIC interface 相关的论文，hardware offloads, steaming interface 2023 OSDI best paper，我读起来觉得很困难，尤其是不理解其中的一些设计，这篇论文工作量应该是非常高的，值得好好读 之前看得都是 kernel bypass (XDP, DPDK) 又或者是 NIC offload，但 NIC 到 软件 的缓冲区和包传递其实也存在瓶颈 核心思想是固定大小的包缓冲区变成了流缓冲区，省去了 metadata 传输等等，对于小包传输带宽提升明显，enso 用单核就能跑到很高的带宽 2023 年操作系统设计与实现研讨会（OSDI）有哪些值得关注的文章？ - ChenKuGai 的回答 - 知乎 https://www.zhihu.com/question/591516372/answer/3506857114 知乎的解答很不错 ","date":"2024-09-30","objectID":"/posts/paper-enso/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#ens-o-a-streaming-interface-for-nic-application-communication"},{"categories":["OS"],"collections":null,"content":"Abstract 今天，网卡和软件之间的大多数通信都涉及到交换固定大小的数据包缓冲区。这种分组接口是为一个时代而设计的，当时 NIC 实现了很少的卸载，软件实现了在应用程序数据和数据包之间进行转换的逻辑。然而，NIC 和网络软件都已经发展: 现代 NIC 实现了硬件卸载，例如 TSO、 LRO 和序列化卸载，可以更有效地在应用程序数据和数据包之间进行转换。此外，现代软件越来越多地批处理网络 I/O，以减少开销。这些变化导致了分组接口(假设网卡和软件交换固定大小的缓冲区)与现代网卡提供并由现代软件使用的特性之间的不匹配。接口和数据之间的这种不一致性增加了软件复杂性和 I/O 开销，从而限制了通信性能。本文提出了一种新的流式 NIC-to-software 接口 Ens o，旨在更好地支持当今 NIC 和软件的交互方式。在其核心，Ens o 避免固定大小的缓冲区，而是将通信结构设计为一个流，可用于发送任意大小的数据。我们展示了这个更改减少了软件开销，降低了 PCIe 带宽需求，并导致更少的缓存丢失。这些改进使得基于 Ens o 的网卡能够在 100Gbps 的链路上使用单个核心的最小数据包(转发速度为 148.8 Mpps) ，提高高性能网络应用的吞吐量 1.5-6 倍，降低延迟高达 43% hardware offloads, serialization offloads 避免固定大小的缓冲区 ","date":"2024-09-30","objectID":"/posts/paper-enso/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction NIC offloads allow the NIC to perform common tasks (e.g., segmentation) previously implemented in software; 更高效的网络 I/O 库和接口，包括 DPDK 和 XDP，允许应用程序减少网络栈的处理 大多数 NIC 目前提供了一个接口，其中所有软件和 NIC 之间的通信都需要发送（和接收）一系列固定大小的缓冲区，我们在本文中称之为数据包缓冲区。 首先，许多 NIC 卸载，如 TCP 分段卸载（TSO）[20, 39]、大接收卸载（LRO）[14]、序列化卸载[44, 71, 86]和传输卸载[3, 14, 27, 77]，它们的输入（和输出）可以跨越多个数据包并且大小不一。在使用这些卸载与数据包化接口时，软件在与 NIC 通信时必须不必要地将数据分割（和重新组合）到多个数据包缓冲区中。 Packetized abstraction: 尽管在软件始终需要交换 MTU 大小的数据包时，采用固定大小的缓冲区效果合理，但在使用更高级别的抽象（如应用程序级消息（例如，RPC）、字节流）或更简单的卸载（如 LRO）时，它变得笨拙。 Poor cache interaction: 由于数据包化接口将传入和传出的数据分散在内存中，它限制了预取器和其他需要预测软件将访问的下一个内存地址的 CPU 优化的有效性——我们称之为混乱的内存访问。 如对象缓存[9, 57]和键值存储[4, 52]。 Metadata overhead: 由于数据包化接口依赖于每个数据包的元数据，它将相当一部分 PCIe 带宽用于传输元数据——在使用小消息时高达 39%的可用带宽。这导致处理小请求的应用程序受到 PCIe 的瓶颈限制，我们在实现 Google 的 Maglev 负载均衡器[23]时观察到了由于 PCIe 瓶颈导致的可扩展性问题。 负载均衡器会受到影响？ 在本文中，我们提出了 Ensō，一种新的 NIC-应用程序通信接口，它摒弃了较低级别的数据包概念。相反，Ensō 提供了一种流式抽象，NIC 和应用程序可以使用它来通信任意大小的数据块。这样做不仅使 NIC 和应用程序能够使用更适合各自实现的功能的任意数据格式，而且还摆脱了数据包化接口中存在的性能问题。因为 Ensō 对数据格式本身没有任何假设，所以可以根据应用程序和 NIC 上启用的卸载进行重新利用。例如，如果 NIC 只负责复用/解复用，它可以使用 Ensō 来传输原始数据包；如果 NIC 也了解应用程序级消息，它可以使用 Ensō 将整个消息和 RPC 传递给应用程序；如果 NIC 实现了传输协议，如 TCP，它可以使用 Ensō 使用字节流与应用程序通信。 流式、字节流 为了提供流式抽象，Ensō 用包含数据的环形缓冲区替换了当前 NIC 接口使用的包含描述符的环形缓冲区。NIC 和软件通过将数据附加到这些环形缓冲区进行通信。Ensō 将缓冲区视为不透明的数据，并且不对它们的内容、结构或大小施加任何要求，从而允许它们被用来传输任意大小的数据，其大小可以和环形缓冲区本身一样大。Ensō 还显著减少了由于元数据导致的 PCIe 带宽开销，因为它能够对写入同一缓冲区的多个数据块进行聚合通知。最后，它 enables better use of CPU prefetcher 以掩盖内存延迟，从而进一步提高应用程序性能。 ring buffer 为了理解其性能，我们使用基于 FPGA 的 SmartNIC 完全实现了 Ensō。我们在第 5 节中描述了我们的硬件和软件实现，以及根据 NIC 提供的功能 Ensō 的使用方式在第 6 节中。在第 7 节中，我们展示了 Ensō 的评估，包括它在四个应用程序中的使用：Maglev 负载均衡器[23]、基于 NitroSketch 的网络遥测应用程序[54]、MICA 键值存储[52]，以及受 AWS CloudWatch Logs[6]启发的日志监控器。我们还实现了一个软件数据包生成器，我们在大多数实验中使用它。我们观察到与 DPDK 没有硬件卸载的实现相比，Maglev 的速度提高了 6 倍，MICA 的速度提高了 1.47 倍，而。 实现了一个硬件？还是硬件上的软件？为什么和 dpdk 非硬件卸载比，是不是不太对照 ","date":"2024-09-30","objectID":"/posts/paper-enso/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#introduction"},{"categories":["OS"],"collections":null,"content":"Background and Motivation 数据包化 NIC 接口决定了几乎所有高性能网络库提供的 API，包括 io_uring[15]、DPDK[19]和 netmap[73] ","date":"2024-09-30","objectID":"/posts/paper-enso/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#background-and-motivation"},{"categories":["OS"],"collections":null,"content":"Packetized NIC Interface NIC 卸载允许 NIC 执行以前在软件中实现的常见任务（例如，分段）；更高效的网络 I/O 库和接口，包括 DPDK 和 XDP，允许应用程序减少网络栈的处理。 但 NIC 到软件的接口几十年来一直没有变化。 数据包化 NIC 接口的一个核心设计选择是为每个数据包放置一个专用的数据包缓冲区。 缓冲区通常被设置为可以容纳 MTU 大小的数据包 图 1 显示了一个数据包化 NIC 接口被用来从 NIC 上的特定硬件队列接收四个数据包的示例。NIC 队列与一组 NIC 寄存器相关联，这些寄存器可以用来控制接收（RX）描述符环形缓冲区和传输（TX）描述符环形缓冲区。在能够接收数据包之前，软件通过在 RX 描述符环形缓冲区中排队指向每个缓冲区的描述符，通知 NIC 其缓冲区池中多个可用缓冲区的地址。然后，NIC 可以使用 DMA 将传入的数据包数据写入下一个可用的数据包缓冲区，并排队更新的描述符，包含数据包大小等元数据。重要的是，NIC 还在描述符中设置了一个“标志 flag”位，向软件发出信号，表明该缓冲区已到达数据包。观察到描述符下的头部指针的通知位，软件然后可以增加头部指针。 传输过程也类似：发送软件为准备传输的数据包缓冲区组装一组描述符，并将描述符（而不是数据包本身）复制到 TX 环形缓冲区；描述符中的标志位现在用于向 NIC 发出信号，表明已经传输（而不是接收）了一个数据包。 为每个数据包分配缓冲区的一个主要好处是，可以在软件中高效地进行复用/解复用。 然而，现代高性能软件栈的使用模式看起来非常不同。不是有一个软件实体（例如，内核，软件交换机）调解对 NIC 的访问，可能有多个线程或进程直接访问 NIC（即，内核绕过）。然后 NIC 承担了解复用的所有责任，使用例如 RSS[82]、Intel 的 Flow Director[39]，或者（对于非常丰富的交换模型）Microsoft 的 AccelNet[28]。在这种设置中，数据包化 NIC 接口的复用/解复用能力没有提供任何额外的价值。 ","date":"2024-09-30","objectID":"/posts/paper-enso/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#packetized-nic-interface"},{"categories":["OS"],"collections":null,"content":"Issues with a Packetized Interface 尽管许多高性能应用程序今天从数据包化接口中获得的好处很少，但它们仍然需要为伴随而来的开销付出代价。将 NIC 和应用程序之间的数据通信硬塞进固定大小的块中，会导致 CPU 缓存和 PCIe 带宽在小请求上的使用效率低下，以及由于应用程序依赖于大消息或字节流而导致的额外数据复制。 主要是面对小请求吗，不知道后续的 evaluation 是不是也都是小请求 Chaotic Memory Access: 基于 DPDK 的简单 ping/pong 程序，主要是由于 L1 和 L2 缓存未命中。像键值存储[4, 52]或数据包处理器[18]这样的应用程序在数据访问上表现出非常高的空间-时间局部性。实际上，先前的工作[55, 81]已经反复证明，数据包处理应用程序的工作集大小经常超出专用于 DDIO[35]的缓存空间，抵消了这种硬件优化的好处，将 I/O 数据直接带入缓存。正如我们在第 7.2.3 节中详细讨论的，使用一种促进顺序内存访问的不同 NIC 接口可以将 L1d 缓存的未命中率从 6%降低到 0.2%，将 L2 缓存的未命中率从 55%降低到 9%。 这里的缓存是 cpu 的吗 Metadata Bandwidth Overhead: 高达 39%的 CPU 到 NIC 互连带宽用于传输描述符 总结：通过为每个数据包配对一个单独的描述符，数据包化 NIC 接口非常适合以前一代需要在软件中实现复用的高吞吐量网络应用程序。然而，对于今天的高性能应用程序，它引入了不必要的性能开销。 ","date":"2024-09-30","objectID":"/posts/paper-enso/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#issues-with-a-packetized-interface"},{"categories":["OS"],"collections":null,"content":"Enso Overview 网络接口卡（NIC）与应用程序通信的流式接口 灵活性，允许它用于不同类别的卸载操作，这些操作在不同的网络层级上运行，并且具有不同的数据大小； 低软件开销，减少应用程序在通信上需要花费的周期数；以及 硬件简单性，使得在商品 NIC 上的实际实现成为可能。 这是一种新的缓冲区抽象，允许应用程序和 NIC 交换任意大小的数据块，就像读写一个无界内存缓冲区一样。与数据包化接口使用的环形缓冲区不同（这些缓冲区包含分散的数据包缓冲区的描述符），Ens ̄o Pipe 是实现为数据环形缓冲区，包含实际的数据包数据 都是环，区别在哪呢 High-level operation: 最初，Pipes 是空的，HeadSW 和 TailNIC 指向缓冲区 1 中的同一位置。当 NIC 接收到消息时，它使用 DMA 将它们排队到 Pipes 拥有的连续内存中。在图中，NIC 在 Pipe A 的内存中排队了两个消息，在 Pipe B 的内存中排队了三个。NIC 通过在通知缓冲区中排队两个通知（每个 Enso Pipe 一个）来通知软件。软件使用这些通知来推进 TailNIC 并处理消息。一旦消息被处理，软件就写入一个内存映射 I/O（MMIO）寄存器（推进 HeadSW）来通知 NIC——允许内存被后续消息重用 3。发送消息是对称的，除了最后一步：NIC 通过覆盖 CPU 用来通知 NIC 有消息可供传输的通知来通知软件消息已被传输。 感觉过程和 ring buffer 没啥区别？ Ens ̄o Pipe’s flexibility：尽管图 3 显示了发送消息的步骤，但由于 Ens ̄o Pipes 是不透明的，它们可以用来传输任意大小的数据块。这些可以是原始数据包，由多个 MTU 大小的数据包组成的消息，甚至是无界字节流。数据的格式由应用程序和在 NIC 上运行的卸载决定。此外，Ens ̄o Pipes 的不透明性意味着它们可以映射到应用程序内存空间内的任何固定内存。因此，通过将 RX 和 TX Ens ̄o Pipes 映射到同一区域，网络功能和其他转发应用程序可以避免复制数据包。在我们的评估（§7）中，我们在实现 Maglev 和网络遥测应用程序时使用了这种方法。 Performance advantages of an Ens ̄o Pipe: pipes 允许应用程序顺序读写 I/O 数据，从而避免了混乱的内存访问 Challenges: 尽管实现数据传输的环形缓冲区本身是一个简单的想法，但协调 CPU 和 NIC 之间的通知以更新头部和尾部指针却是一个挑战。 Efficient coordination: 在§4.1 中，我们讨论了通知的简单方法如何会使 MMIO 和 DMA 的最坏情况性能受到压力。特别是，对同一内存地址的并发访问可能会在 CPU 和 NIC 之间产生缓存争用。Ens ̄o 使用专用的通知缓冲区来同步头部和尾部指针的更新；当与批处理和多队列处理结合使用时，通知缓冲区方法减少了缓存争用的威胁。 Notification pacing: Pipes 被设计成可以合并多个数据包的通知，减少了 CPU 和 NIC 之间传输的元数据量。然而，决定何时发送通知仍然很重要：如果发送得太频繁，它们会浪费 PCIe 带宽并增加软件开销，但如果发送得太不频繁，核心可能会闲置等待通知，从而降低吞吐量。Ens ̄o 包括两种机制，反应性通知和通知预取（§4.2），它们控制何时发送通知。这些机制是自然自适应的，即它们在不限制吞吐量的情况下最小化发送的通知数量，并且可以在不增加硬件复杂性的情况下实现。 同一地址访问，缓存争用，是什么情况 enso 使用了多队列 流计算：合并多个包？ Low hardware complexity and state: Ens ̄o 的设计涉及硬件和软件，我们必须小心，不要用软件的简单性来换取硬件的复杂性。Ens ̄o 倾向于需要很少 NIC 状态的协调机制。我们的目标是设计一个简单且易于并行化的系统 Target applications: 流式接口，针对按顺序处理接收数据的情况进行优化。我们的评估（§7）表明，这涵盖了广泛的网络密集型应用程序 由此产生的设计不适合需要复用和解复用数据包的应用程序（例如，像 Open vSwitch [67]和 BESS [32]这样的虚拟交换机），因为这些应用程序需要额外的复制。然而，令人惊讶的是，即使需要这样的额外复制，Ens ̄o 的性能也超过了数据包化接口 为什么不适合 multiplex and demultiplex 但是为什么性能又好很多？硬件 Offload 带来的显著提升吗 ","date":"2024-09-30","objectID":"/posts/paper-enso/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#enso-overview"},{"categories":["OS"],"collections":null,"content":"Efficient Notifications efficiently coordinating Enso Pipes between the CPU and the NIC ","date":"2024-09-30","objectID":"/posts/paper-enso/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#efficient-notifications"},{"categories":["OS"],"collections":null,"content":"Efficient Ens ̄o Pipe Coordination How should software and the NIC communicate pointer updates? 在数据包化 NIC 接口使用的描述符环形缓冲区中，软件使用 MMIO 写入来向 NIC 通信指针更新，而 NIC 通过描述符缓冲区本身的内联信号来通信指针更新[25, 39]，避免了 MMIO 读取的开销。因为描述符的格式由 NIC 定义，NIC 可以在每个描述符中专门设置一个“标志信号”来信号描述符是有效的。然后，软件可以轮询下一个描述符，直到其标志变为有效。这样，NIC 就不需要明确地告诉软件指针更新。 不幸的是，我们不能对 Ens ̄o 使用这种方法。虽然 Ens ̄o Pipes 仍然可以使用 MMIO 写入来更新软件的指针，但我们不能在 Ens ̄o Pipe 本身中嵌入内联信号，因为我们不对数据施加任何结构。 我们考虑了几种设计选项来通信指针更新。我们在这里关注 illuminating rejected design here：在主内存中 NIC 和软件之间共享一个地址。对于每个 Pipe，我们可能有一个专用的内存地址，NIC 在其中写入最新的 TailNIC。然后，软件可以轮询这个地址来确定最新的值。不幸的是，这种方法会导致争用，因为每次 NIC 写入内存时，CPU 上的缓存条目就会被使无效。如果软件继续轮询同一缓存行，由此产生的争用会将吞吐量降低几个数量级：我们在使用小传输时使用这种方法测量到的吞吐量低于 5 Gbps。我们在附录中讨论了其他被拒绝的方法。 不对数据施加任何结构？为什么，因为是流式？ 共享一个地址，导致争用，为什么 NIC 写内存 CPU 缓存会失效？缓存一致性？缓存行无效？为什么不直接写入内存，DMA 呢？ Pipe coordinate 还是指针的位置问题。 ","date":"2024-09-30","objectID":"/posts/paper-enso/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#efficient-ens-o-pipe-coordination"},{"categories":["OS"],"collections":null,"content":"Notification Buffer Ens ̄o 使用通知缓冲区来通信指针更新。虽然通知缓冲区本身的结构并不能解决缓存争用挑战，但当它与批量通知结合使用，并且用于聚合多个 Ens ̄o Pipes 的通知更新时，这种方法可以防止 CPU 忙等待共享缓存行，从而避免争用引起的减速。 一个通知，指示多个连续数据库 批量/合并通知，消费通知后 MMIO 写入指针，使用通知缓冲区来更新指针。 还是不懂为什么不直接用 DMA ","date":"2024-09-30","objectID":"/posts/paper-enso/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#notification-buffer"},{"categories":["OS"],"collections":null,"content":"Multiplexing and Scaling 在单个线程内：为了让单个线程有效地访问多个 Ens ̄o Pipes，我们将多个 Ens ̄o Pipes 与同一个通知缓冲区关联。因此，软件可以探测单个通知缓冲区来检索多个 Ens ̄o Pipes 的更新。这避免了需要轮询多个队列的已知可扩展性问题 在多个线程之间：为了让多个线程独立地发送和接收数据，Ens ̄o 支持多个通知缓冲区。每个线程可以使用专用的通知缓冲区，避免了昂贵的同步原语。在设置新的 Ens ̄o Pipe 时，软件告诉 NIC 它与哪个通知缓冲区关联。因此，NIC 知道要向哪个通知缓冲区发送通知。 在多个应用程序之间：除了使用独立的的通知缓冲区，Ens ̄o 确保应用程序只访问它们自己的 Ens ̄o Pipes 和通知缓冲区的子集。每个队列的 MMIO 指针寄存器对保持在其自己的专用页面对齐的内存块中[22]。这让内核可以将指针寄存器以每个队列的粒度映射到请求它的应用程序的地址空间。 那 pipe 是怎么分的，还需要元数据吗，到底多少个 pipes 呢，是多个应用就多个 pipe 吗， ","date":"2024-09-30","objectID":"/posts/paper-enso/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#multiplexing-and-scaling"},{"categories":["OS"],"collections":null,"content":"Notifications: Contention and Overhead 允许多个 Ens ̄o Pipes 共享同一个通知队列（§4.1.2），并且只有对于更大的数据批次才有通知到达（§4.2）自然地通过保持 NIC 在更新通知缓冲区时“领先”于 CPU 来防止争用，并且也减少了通信这些通知的 PCIe 开销。当 CPU 读取一个 Ens ̄o Pipe 的数据时，NIC 正在为后续的 Ens ̄o Pipes 写入新的条目。因为 CPU 正在处理更大的数据批次，所以在需要检查通知缓冲区之前，它会更忙更长时间。因此，随着线速的提高，两者不太可能同时访问同一个缓存行。 这里 FIFO 队列的 tail latency 应该是比较低的 而且必须注册连续的地址空间，类似 RDMA？ 后续的没来得及看了 ","date":"2024-09-30","objectID":"/posts/paper-enso/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Enso: A Streaming Interface for NIC-Application Communication (OSDI2023)","uri":"/posts/paper-enso/#notifications-contention-and-overhead"},{"categories":["OS"],"collections":null,"content":"Shenango: Achieving High CPU Efficiency for Latency-sensitive Datacenter Workloads 蛮知名的一篇文章，引用很高，后续论文也有 ghOSt，和 Efficient Scheduling Policies for Microsecond-scale Tasks presentation 也很不错，看论文看到现在反而更喜欢看作者的 pre，有图就好理解，尤其是动图 https://cloud.tencent.com/developer/article/2322227 这篇文章总结的不错 算法很好理解，但实现起来应该很难，不得不说 system 方向或者 network 方向的论文都非常扎实，得好好补补基础。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#shenango-achieving-high-cpu-efficiency-for-latency-sensitive-datacenter-workloads"},{"categories":["OS"],"collections":null,"content":"Abstract 数据中心应用要求操作系统提供微秒级尾延迟和高请求率， 实现微秒级延迟的最佳可用解决方案是内核旁路网络，它将 CPU 核心专门用于应用程序进行网络卡的自旋轮询。但这种方法浪费 CPU：即使在适度的平均负载下，也必须为预期的峰值负载分配足够的核心。 Shenango achieves comparable latencies but at far greater CPU efficiency 它在非常细的粒度上——每 5 微秒——重新分配核心给应用程序，使得延迟敏感型应用程序未使用的周期可以被批处理应用程序有效地利用。它通过(1)一种高效的算法来检测应用程序何时会从更多核心中受益，以及(2)一个名为 IOKernel 的特权组件来实现如此快速的重新分配率，该组件在一个专用核心上运行，从 NIC 引导数据包并协调核心重新分配。在处理延迟敏感型应用程序（如 memcached）时，我们发现 Shenango 实现了与 ZygOS（一种最先进的内核旁路网络栈）相当的尾延迟和吞吐量，但可以线性地将延迟敏感型应用程序吞吐量交换为批处理应用程序吞吐量，大幅提高 CPU 效率。 efficiency 指的是？ zygOS https://dl.acm.org/doi/10.1145/3132747.3132780 ZygOS 在 IX 基础上构建（让网卡把任务分配到多个 CPU 核对应的不同队列），为了负载均衡，空闲的 CPU 核从其他核的队列里“窃取”任务（work stealing），并利用核间中断（IPI）降低响应包的处理延迟。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction CPU 效率变得至关重要 像 ZygOS 这样的内核旁路网络栈能够通过绕过内核调度器支持更高吞吐量的微秒级延迟 然而，这些系统仍然浪费了大量的 CPU 周期；它们依赖于自旋轮询网络接口卡（NIC）来检测数据包到达，因此即使没有数据包要处理，CPU 也总是在使用中 低尾延迟和高 CPU 效率之间的紧张关系 为什么今天的系统迫使我们浪费核心来维持微秒级延迟？谷歌最近的一篇论文认为，糟糕的尾延迟和效率是由于系统软件已经针对毫秒级 I/O（例如，磁盘）进行了调整 [15]。事实上，今天的调度器只在粗粒度上做出线程平衡和核心分配决策（Linux 每四毫秒一次，Arachne [63]和 IX [62]每 50-100 毫秒一次），阻止了对负载不平衡的快速反应。 本文介绍了 Shenango，一个专注于实现三个目标的系统：（1）为数据中心应用程序提供微秒级端到端尾延迟和高吞吐量；（2）CPU-efficient packing of applications on multi-core machines；（3）由于同步 I/O 和标准的编程抽象（如轻量级线程和阻塞 TCP 网络套接字），提高了应用程序开发者的生产力。 同步 IO 为什么可以提高开发者生产力？容易理解？那支持异步 IO 吗 为了实现其目标，Shenango 解决了在非常细的时间尺度上重新分配核心到应用程序的难题；它每 5 微秒重新分配一次核心，比我们所知的任何系统都快几个数量级。Shenango 提出了两个关键思想。首先，Shenango 引入了一个高效算法，根据可运行的线程和传入的数据包准确地确定应用程序何时会从额外的核心中受益。其次，Shenango 每台机器分配一个忙碌的 busy-spinning 自旋核心给一个名为 IOKernel 的集中式软件实体，该实体将数据包 steers 引导到应用程序并跨它们分配核心。应用程序在用户级运行时中运行，这些运行时提供了高效的高级编程抽象，并与 IOKernel 通信以促进核心分配。 Shenango 实现了与 ZygOS [61]相似的吞吐量和延迟，ZygOS 是一种最先进的内核旁路网络栈，但 CPU 效率更高。例如 Shenango 可以实现每秒超过五百万次的 memcached 吞吐量，同时保持 99.9 百分位延迟低于 100 微秒（比 ZygOS 多一百万）。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#introduction"},{"categories":["OS"],"collections":null,"content":"The Case Against Slow Core Allocators 为什么 millisecond-scale 毫秒级核心分配器在处理微秒级请求时无法保持高 CPU 效率。我们将 CPU efficiency 定义为用于执行应用程序级工作（而不是忙等待、上下文切换、数据包处理或其他系统软件开销）的周期比例。 这个定义是通用的吗？如果改成每 5us 分配一次，处于用户的时间肯定更多，那么这样的 cpu 效率肯定更高，坏处是什么呢 像 IX[62]和 Arachne[63]这样的工作引入了用户级核心分配器，它们在 50-100 毫秒的间隔内调整核心分配。同样，Linux 主要在毫秒级定时器滴答响应下重新平衡核心上的任务。不幸的是，所有这些系统调整核心的速度都太慢，无法有效地处理微秒级请求。 图 1 显示了我们模拟中提供的负载与 CPU 效率（使用周期除以分配周期）之间的关系。它还显示了 Shenango 运行时在同一工作负载下本地运行的效率，通过生成一个线程来执行每个请求持续时间的合成工作。对于模拟结果，我们用模拟器分配的核心数标记了每一段线段；锯齿形模式之所以出现，是因为只能分配整数数量的核心。即使没有网络或系统软件开销，也必须保留大部分空闲核心以吸收负载的突发，导致 CPU 效率的损失。这种损失在一至四个核心之间尤为严重，随着负载的变化，应用程序可能会在这种低效率区域花费大量时间。理想的系统将为每个请求的持续时间精确地启动一个核心，并实现完美的效率，因为应用程序级工作将与 CPU 周期一一对应。 On the other hand, a slow core allocator is likely to perform worse than its theoretical upper bound in practice. First, CPU efficiency would be even lower if there were more service time variability or tighter tail-latency requirements. service time variability 所以支持动态负载吗，shenango 提出的 fast core allocation 和 TCP 拥塞控制很像，但为什么是 fast，明明是先分配很少的核心 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#the-case-against-slow-core-allocators"},{"categories":["OS"],"collections":null,"content":"Challenges and Approach Shenango 的目标是通过尽可能少地分配核心给每个应用程序来优化 CPU 效率 avoiding a condition we call compute congestion, 在这种情况下，如果不给应用程序分配额外的核心，将导致工作延迟超过几微秒。这个目标释放了被低估的核心供其他应用程序使用，同时仍然保持尾延迟在控制之中。 所以就是降低分配时间，所以能够降低尾部延迟，不需要等太久，那么平均延迟会降低多少呢 5us 是精心挑选的值吗 尽可能少地分配给每个应用程序核心来优化 CPU 效率，这提升了什么？不会更加慢吗，所以要控制算法在微妙内？ 现代服务经常经历非常高的请求率（单个服务器每秒数百万数据包），核心分配开销使得按请求重新分配核心变得不可行。相反，Shenango 紧密接近这个理想，每五微秒检测一次负载变化，并每秒调整核心分配超过 60,000 次。如此短的调整间隔需要新的方法来估计负载。我们现在更详细地讨论这些挑战。 estimating load 怎么做 Core allocations impose overhead, 分配带来开销：Arachne 重新分配一个核心需要 29 微秒的延迟[63]，而 IX 需要数百微秒，因为它必须更新 NIC 规则以引导数据包到核心 Estimating required cores is difficult: 以前的系统使用应用程序级指标，如延迟、吞吐量或核心利用率，来估计长时间尺度上的核心需求。Shenango 旨在估计瞬时负载， 负载估计的区别在哪？怎么预估核心数？所以 shenango 就干脆不预估，直接用 congestion 来冷启动？ ","date":"2024-09-30","objectID":"/posts/paper-shenango/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#challenges-and-approach"},{"categories":["OS"],"collections":null,"content":"Shenango’s Approach 首先，Shenango 将线程和数据包排队延迟都视为计算拥堵的信号，它引入了一个高效的拥堵检测算法，利用这些信号来决定如果给应用程序更多的核心是否会受益。 congestion detection algorithm 类似 TCP 吗 怎么知道是不是受益的 因此，Shenango 的第二个关键理念是专门分配一个持续运行的核心给一个称为 IOKernel（第 4 节）的集中式软件实体 IOKernel 进程以 root 权限运行，作为应用程序与网络接口卡硬件队列之间的中介。通过持续运行，IOKernel 可以在微秒级检查线程和数据包队列，以协调核心分配。此外，它可以提供低延迟的网络访问，并允许在软件中将数据包导向核心，当核心被重新分配时能够快速重新配置数据包导向规则。结果是，核心重新分配仅需 5.9 微秒即可完成，并且需要不到两微秒的 IOKernel 计算时间来进行协调。这些开销支持足够快的核心分配速率，既能够适应负载的变化，也能迅速纠正我们的拥塞检测算法中的任何误预测。 应用程序逻辑在每个应用程序的运行时（第 5 节）中运行，通过共享内存与 IOKernel 通信（图 2）。每个运行时都是不可信的，并负责提供有用的编程抽象，包括线程、互斥锁、条件变量和网络套接字。应用程序以库的形式链接到 Shenango 运行时，允许类似内核的功能在其地址空间内运行。 5.9us 是平均还是最低？ app shared memory 和 IOkernel 通信，同步问题怎么解决？尤其是多分配核心的时候 在启动时，运行时创建多个内核线程（即 pthreads），每个都有一个本地运行队列，最多达到运行时可能使用的最大核心数。应用程序逻辑在轻量级用户级线程中运行，这些线程被放入这些队列中；通过工作窃取实现跨核心的工作平衡。我们将运行时创建的每个每核心内核线程称为 kthread，将用户级线程称为 uthread。Shenango 被设计为能够在未经修改的 Linux 环境中共存；IOKernel 可以配置为管理一部分核心，而 Linux 调度器管理其他部分。 steal ","date":"2024-09-30","objectID":"/posts/paper-shenango/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#shenangos-approach"},{"categories":["OS"],"collections":null,"content":"IO Kernel The IOKernel runs on a dedicated core 在任何时候，它决定为每个应用程序分配多少核心（4.1.1 节）以及分配哪些核心给每个应用程序 它处理所有的网络 I/O，绕过了操作系统内核。在接收路径上，它直接轮询 NIC 接收队列，并将每个传入的数据包放到应用程序核心之一的共享内存队列中。在传输路径上，它轮询每个运行时的数据包出口队列，并将数据包转发到 NIC ","date":"2024-09-30","objectID":"/posts/paper-shenango/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#io-kernel"},{"categories":["OS"],"collections":null,"content":"Core Allocation IOKernel 必须快速做出核心分配决策，因为其花费在核心分配上的任何时间都不能用于转发数据包，从而降低吞吐量。为了简化，IOKernel 分离了它的两个决策；in most cases, it first decides if an application should be granted an additional core, and then decides which core to grant. Number of cores per application 每个应用程序的运行时都会被分配一定数量的 guaranteed cores 和一定数量的 burstable cores。运行时总是有权使用其保证的核心，而不会面临被抢占的风险（不允许过量订阅），但如果它没有足够的工作来占用它们，它可以使用更少（甚至是零）的核心。当有额外的核心可用时，IOKernel 可以将它们作为可突发核心分配，使繁忙的运行时能够暂时超过其保证的核心限制。 在决定为运行时分配多少核心时，IOKernel 的目标是在避免计算拥塞（第 3 节）的同时最小化分配给每个运行时的核心数量。为了确定运行时是否有多余的核心，IOKernel 依赖于运行时的 kthread 在不需要时自愿放弃核心。当一个 kthread 找不到任何工作要做，意味着它的本地运行队列为空，并且没有从其他活跃的 kthread 中找到可窃取的工作时，它会放弃其核心并通知 IOKernel（我们称之为停放）。IOKernel 也可以随时抢占可突发核心，迫使它们立即停放。 IOKernel 利用其独特的视角通过监控活跃 kthread 的队列占用情况来检测即将发生的计算拥塞。当一个数据包到达一个没有分配核心的运行时，IOKernel 立即为其分配一个核心。为了监测活跃运行时的拥塞情况，IOKernel 每 5 微秒调用一次拥塞检测算法（算法 1）。 拥塞检测算法根据两种负载源判断一个运行时是否超载：排队的线程和排队的入站数据包。如果在拥塞检测算法连续两次运行中发现任何项目存在于队列中，则表明一个数据包或线程至少排队了 5 微秒。因为排队的数据包或线程代表了可以在另一个核心上并行处理的工作，运行时被认为“拥塞”，IOKernel 会为其分配一个额外的核心。我们发现，排队的持续时间比队列长度是一个更稳健的信号，因为使用队列长度需要仔细调整不同请求持续时间的阈值参数[63, 74]。 将队列实现为环形缓冲区可以实现简单且高效的检测机制。检测一个项目是否在队列中存在两个连续的时间间隔只是一个比较当前头部指针与前一次迭代的尾部指针的问题。运行时通过每个 kthread 的单个缓存行的共享内存向 IOKernel 暴露这种状态。 直观上，核心分配可能会表现出振荡行为，可能每次迭代都会添加和停放一个核心。这是设计的一部分，因为较慢的调整要么牺牲尾部延迟，要么阻止我们在短时间内复用核心。事实上，现代 CPU 能够进行足够高效的上下文切换；进程上下文标识符（PCIDs）允许在不刷新 TLB 的情况下交换页表。Linux 切换进程大约需要 600 纳秒，因此足以处理由 IOKernel 产生的核心重新分配速率。在第 7.3 节中，我们评估了不同的核心分配间隔对尾部延迟和 CPU 效率的影响。 拥塞算法，先放几个线程/核心，然后怎么判断再给后续的？ Which cores for each application When deciding which core to grant to an application, the IOKernel considers three factors: Hyper-threading efficiency, Intel 的超线程技术允许两个硬件线程在同一物理核心上运行。这些线程共享处理器资源，如 L1 和 L2 缓存及执行单元，但被暴露为两个独立的逻辑核心，因此，IOKernel 倾向于将同一物理核心上的超线程分配给同一应用程序。 Cache locality, 如果一个应用程序的状态已经存在于新分配给它的核心的 L1/L2 缓存中，它可以避免许多耗时的缓存未命中。因此，IOKernel 跟踪运行时的当前和过去的核心分配情况。 Latency, 因此，如果存在空闲核心，IOKernel 总是选择一个空闲核心而不是抢占一个忙碌的核心。 如何跟踪？哈希表？ 一旦 IOKernel 选择了要分配给应用程序的核心，它还必须选择一个已停放的 kthread 来唤醒并在该核心上运行。为了缓存局部性，它首先尝试挑选最近在这个核心上运行过的 kthread。如果没有这样的 kthread 可用，IOKernel 会选择停放时间最长的 kthread，这样可以留出其他停放的 kthread，以防它们最近运行过的某个核心变得可用。 调用检测算法的总成本与总核心数量成线性关系。 shenango 长期运行会怎么样，会不会存在追踪很久的记录 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#core-allocation"},{"categories":["OS"],"collections":null,"content":"Core Allocation IOKernel 必须快速做出核心分配决策，因为其花费在核心分配上的任何时间都不能用于转发数据包，从而降低吞吐量。为了简化，IOKernel 分离了它的两个决策；in most cases, it first decides if an application should be granted an additional core, and then decides which core to grant. Number of cores per application 每个应用程序的运行时都会被分配一定数量的 guaranteed cores 和一定数量的 burstable cores。运行时总是有权使用其保证的核心，而不会面临被抢占的风险（不允许过量订阅），但如果它没有足够的工作来占用它们，它可以使用更少（甚至是零）的核心。当有额外的核心可用时，IOKernel 可以将它们作为可突发核心分配，使繁忙的运行时能够暂时超过其保证的核心限制。 在决定为运行时分配多少核心时，IOKernel 的目标是在避免计算拥塞（第 3 节）的同时最小化分配给每个运行时的核心数量。为了确定运行时是否有多余的核心，IOKernel 依赖于运行时的 kthread 在不需要时自愿放弃核心。当一个 kthread 找不到任何工作要做，意味着它的本地运行队列为空，并且没有从其他活跃的 kthread 中找到可窃取的工作时，它会放弃其核心并通知 IOKernel（我们称之为停放）。IOKernel 也可以随时抢占可突发核心，迫使它们立即停放。 IOKernel 利用其独特的视角通过监控活跃 kthread 的队列占用情况来检测即将发生的计算拥塞。当一个数据包到达一个没有分配核心的运行时，IOKernel 立即为其分配一个核心。为了监测活跃运行时的拥塞情况，IOKernel 每 5 微秒调用一次拥塞检测算法（算法 1）。 拥塞检测算法根据两种负载源判断一个运行时是否超载：排队的线程和排队的入站数据包。如果在拥塞检测算法连续两次运行中发现任何项目存在于队列中，则表明一个数据包或线程至少排队了 5 微秒。因为排队的数据包或线程代表了可以在另一个核心上并行处理的工作，运行时被认为“拥塞”，IOKernel 会为其分配一个额外的核心。我们发现，排队的持续时间比队列长度是一个更稳健的信号，因为使用队列长度需要仔细调整不同请求持续时间的阈值参数[63, 74]。 将队列实现为环形缓冲区可以实现简单且高效的检测机制。检测一个项目是否在队列中存在两个连续的时间间隔只是一个比较当前头部指针与前一次迭代的尾部指针的问题。运行时通过每个 kthread 的单个缓存行的共享内存向 IOKernel 暴露这种状态。 直观上，核心分配可能会表现出振荡行为，可能每次迭代都会添加和停放一个核心。这是设计的一部分，因为较慢的调整要么牺牲尾部延迟，要么阻止我们在短时间内复用核心。事实上，现代 CPU 能够进行足够高效的上下文切换；进程上下文标识符（PCIDs）允许在不刷新 TLB 的情况下交换页表。Linux 切换进程大约需要 600 纳秒，因此足以处理由 IOKernel 产生的核心重新分配速率。在第 7.3 节中，我们评估了不同的核心分配间隔对尾部延迟和 CPU 效率的影响。 拥塞算法，先放几个线程/核心，然后怎么判断再给后续的？ Which cores for each application When deciding which core to grant to an application, the IOKernel considers three factors: Hyper-threading efficiency, Intel 的超线程技术允许两个硬件线程在同一物理核心上运行。这些线程共享处理器资源，如 L1 和 L2 缓存及执行单元，但被暴露为两个独立的逻辑核心，因此，IOKernel 倾向于将同一物理核心上的超线程分配给同一应用程序。 Cache locality, 如果一个应用程序的状态已经存在于新分配给它的核心的 L1/L2 缓存中，它可以避免许多耗时的缓存未命中。因此，IOKernel 跟踪运行时的当前和过去的核心分配情况。 Latency, 因此，如果存在空闲核心，IOKernel 总是选择一个空闲核心而不是抢占一个忙碌的核心。 如何跟踪？哈希表？ 一旦 IOKernel 选择了要分配给应用程序的核心，它还必须选择一个已停放的 kthread 来唤醒并在该核心上运行。为了缓存局部性，它首先尝试挑选最近在这个核心上运行过的 kthread。如果没有这样的 kthread 可用，IOKernel 会选择停放时间最长的 kthread，这样可以留出其他停放的 kthread，以防它们最近运行过的某个核心变得可用。 调用检测算法的总成本与总核心数量成线性关系。 shenango 长期运行会怎么样，会不会存在追踪很久的记录 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#number-of-cores-per-application"},{"categories":["OS"],"collections":null,"content":"Core Allocation IOKernel 必须快速做出核心分配决策，因为其花费在核心分配上的任何时间都不能用于转发数据包，从而降低吞吐量。为了简化，IOKernel 分离了它的两个决策；in most cases, it first decides if an application should be granted an additional core, and then decides which core to grant. Number of cores per application 每个应用程序的运行时都会被分配一定数量的 guaranteed cores 和一定数量的 burstable cores。运行时总是有权使用其保证的核心，而不会面临被抢占的风险（不允许过量订阅），但如果它没有足够的工作来占用它们，它可以使用更少（甚至是零）的核心。当有额外的核心可用时，IOKernel 可以将它们作为可突发核心分配，使繁忙的运行时能够暂时超过其保证的核心限制。 在决定为运行时分配多少核心时，IOKernel 的目标是在避免计算拥塞（第 3 节）的同时最小化分配给每个运行时的核心数量。为了确定运行时是否有多余的核心，IOKernel 依赖于运行时的 kthread 在不需要时自愿放弃核心。当一个 kthread 找不到任何工作要做，意味着它的本地运行队列为空，并且没有从其他活跃的 kthread 中找到可窃取的工作时，它会放弃其核心并通知 IOKernel（我们称之为停放）。IOKernel 也可以随时抢占可突发核心，迫使它们立即停放。 IOKernel 利用其独特的视角通过监控活跃 kthread 的队列占用情况来检测即将发生的计算拥塞。当一个数据包到达一个没有分配核心的运行时，IOKernel 立即为其分配一个核心。为了监测活跃运行时的拥塞情况，IOKernel 每 5 微秒调用一次拥塞检测算法（算法 1）。 拥塞检测算法根据两种负载源判断一个运行时是否超载：排队的线程和排队的入站数据包。如果在拥塞检测算法连续两次运行中发现任何项目存在于队列中，则表明一个数据包或线程至少排队了 5 微秒。因为排队的数据包或线程代表了可以在另一个核心上并行处理的工作，运行时被认为“拥塞”，IOKernel 会为其分配一个额外的核心。我们发现，排队的持续时间比队列长度是一个更稳健的信号，因为使用队列长度需要仔细调整不同请求持续时间的阈值参数[63, 74]。 将队列实现为环形缓冲区可以实现简单且高效的检测机制。检测一个项目是否在队列中存在两个连续的时间间隔只是一个比较当前头部指针与前一次迭代的尾部指针的问题。运行时通过每个 kthread 的单个缓存行的共享内存向 IOKernel 暴露这种状态。 直观上，核心分配可能会表现出振荡行为，可能每次迭代都会添加和停放一个核心。这是设计的一部分，因为较慢的调整要么牺牲尾部延迟，要么阻止我们在短时间内复用核心。事实上，现代 CPU 能够进行足够高效的上下文切换；进程上下文标识符（PCIDs）允许在不刷新 TLB 的情况下交换页表。Linux 切换进程大约需要 600 纳秒，因此足以处理由 IOKernel 产生的核心重新分配速率。在第 7.3 节中，我们评估了不同的核心分配间隔对尾部延迟和 CPU 效率的影响。 拥塞算法，先放几个线程/核心，然后怎么判断再给后续的？ Which cores for each application When deciding which core to grant to an application, the IOKernel considers three factors: Hyper-threading efficiency, Intel 的超线程技术允许两个硬件线程在同一物理核心上运行。这些线程共享处理器资源，如 L1 和 L2 缓存及执行单元，但被暴露为两个独立的逻辑核心，因此，IOKernel 倾向于将同一物理核心上的超线程分配给同一应用程序。 Cache locality, 如果一个应用程序的状态已经存在于新分配给它的核心的 L1/L2 缓存中，它可以避免许多耗时的缓存未命中。因此，IOKernel 跟踪运行时的当前和过去的核心分配情况。 Latency, 因此，如果存在空闲核心，IOKernel 总是选择一个空闲核心而不是抢占一个忙碌的核心。 如何跟踪？哈希表？ 一旦 IOKernel 选择了要分配给应用程序的核心，它还必须选择一个已停放的 kthread 来唤醒并在该核心上运行。为了缓存局部性，它首先尝试挑选最近在这个核心上运行过的 kthread。如果没有这样的 kthread 可用，IOKernel 会选择停放时间最长的 kthread，这样可以留出其他停放的 kthread，以防它们最近运行过的某个核心变得可用。 调用检测算法的总成本与总核心数量成线性关系。 shenango 长期运行会怎么样，会不会存在追踪很久的记录 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#which-cores-for-each-application"},{"categories":["OS"],"collections":null,"content":"Dataplane The IOKernel busy loops, continuously polling the incoming NIC packet queue and the outgoing application packet queues. Packet steering. 由于 IOKernel 跟踪属于每个运行时的核心，因此它可以将传入的数据包直接交付给运行适当运行时的核心。在 Shenango 中，每个运行时都配置了自己的 IP 和 MAC 地址。当一个新的数据包到达时，IOKernel 通过在哈希表中查找 MAC 地址来识别其运行时。然后，IOKernel 使用 RSS 哈希选择该运行时内的一个核心，并将数据包放入该核心的入站数据包队列中。虽然 Shenango 偶尔会重新排序数据包（例如，当分配给运行时的核心数量发生变化时），但我们发现，在短时间间隔内，同一流中的数据包通常会到达同一个运行时的入站数据包队列（第 7.3 节）。我们的系统可以通过采用类似 Intel 的 Flow Director [8] 或 FlexNIC [42] 等技术进一步优化数据包转向。 RSS hash, Receive side scaling 网卡上的跨核收发 通过哈希分发 Polling transmission queues. 为了找到要传输的数据包而轮询多个出站队列可能会带来较高的 CPU 开销，尤其是在具有许多队列的系统中 [68]。由于 IOKernel 跟踪哪些 kthread 是活跃的，它只轮询对应于活跃 kthread 的出站运行时数据包队列。这使得轮询出站队列的 CPU 开销可以根据系统中的核心数量进行扩展。 IOKernel 的核心会处理别的事情吗，会带来其他的缓存一致性问题吗 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#dataplane"},{"categories":["OS"],"collections":null,"content":"Runtime Shenango 的运行时经过优化以提高编程性，提供了高级抽象，如阻塞的 TCP 网络套接字和轻量级线程。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#runtime"},{"categories":["OS"],"collections":null,"content":"Scheduling 运行时在由 IOKernel 动态分配给它的核心之间进行应用内部的调度。 我们的运行时围绕每个 kthread 的运行队列和工作窃取构建，类似于 Go [6]，与 Arachne 的工作共享模型 [63] 相反 应该说类似 GMP？ 受到 ZygOS 的启发，我们对 uthread 进行细粒度的工作窃取以减少尾部延迟，这对具有服务时间变化的工作负载特别有益 stealing 可以减少 tail latency 的原因是什么 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#scheduling"},{"categories":["OS"],"collections":null,"content":"Networking 我们的运行时负责为应用程序提供所有网络功能，包括 UDP 和 TCP 协议处理。 On the other hand, we found that there were significant advantages to relaxing ordering requirements and violating flow consistent hashing 这是为什么，不需要排序？ Berkeley Sockets 是 BSD 吗，和 posix 的关系是什么 http://www.openss7.org/papers/strsock/sockimp.pdf 本质上看没什么区别，为什么论文不基于 POSIX？ An earlier version of the runtime attempted to support zero-copy networking 然而，我们发现这种方法存在严重的问题。首先，它需要 API 更改，破坏了与 Berkeley Sockets 的兼容性。其次，我们惊讶地发现它对性能有负面影响。进一步调查后，我们发现 IOKernel 的吞吐量对驻留缓冲区的数量敏感，因为 DDIO（一种将数据包有效载荷直接推送到 LLC 的 Intel 技术）对数据包数据可以占用的最大缓存行数有限制。当超过这个限制时，数据包数据被推送到 RAM，大大增加了访问延迟。通过复制有效载荷，我们可以鼓励 DDIO 重用相同的缓冲区，从而保持在其缓存占用阈值内。这与“泄漏的 DMA”问题 [70] 类似。 没看懂，有点难理解。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#networking"},{"categories":["OS"],"collections":null,"content":"Implementation IOKernel \u0026amp;\u0026amp; runtime Shenango 用 C 语言实现，并包括 C++ 和 Rust 的绑定 IOKernel 由 2,244 行代码（LOC）实现，运行时由 6,155 行代码实现。 IOKernel 使用 Intel 数据平面开发套件（DPDK）[2] 版本 18.11，以快速从用户空间访问 NIC 队列。整个系统在未经修改的 Linux 环境中运行。 同样用了 DPDK ","date":"2024-09-30","objectID":"/posts/paper-shenango/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#implementation"},{"categories":["OS"],"collections":null,"content":"IOKernel Implementation Shenango 依赖于几个 Linux 内核机制来将线程固定到核心，并在 IOKernel 和运行时之间进行通信 运行时设置了一系列描述符环队列（灵感来自 Barrelfish 的轻量级 RPC 实现 [17]） 为了将运行时的 kthread 分配到特定的核心，IOKernel 使用 sched_setaffinity。 IOKernel 到底怎么追踪应用使用过哪些 thread? ","date":"2024-09-30","objectID":"/posts/paper-shenango/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#iokernel-implementation"},{"categories":["OS"],"collections":null,"content":"Runtime Implementation 我们的运行时包括对轻量级线程、互斥锁、条件变量、读-复制-更新（RCU）、高分辨率定时器以及同步的 TCP 和 UDP 套接字的支持。与 IOKernel 一样，运行时使用有限的一组现有的 Linux 原语；它通过 mmap 分配内存，通过调用 pthread_create() 创建 kthread，并通过共享内存、eventfd 文件描述符和信号与 IOKernel 交互。我们根据 RFC [36] 从头实现 TCP，我们的 TCP 堆栈与 Linux 和 ZygOS 的 TCP 堆栈兼容，包括流量控制和快速重传，但不包括拥塞控制。 RCU 这个周末一定看 其他技术都是很底层的，操作系统应该要了解的 shenango 还实现了 TCP，是基于 dpdk 做的吗，为什么不做更快的协议呢 为了提高内存分配性能，运行时使用了每个 kthread 的缓存 [21]，特别是在分配线程栈和网络数据包缓冲区时。运行时提供了一个 RCU 子系统，以支持对主要读取的数据结构的高效访问 [52]。运行时在每个 kthread 重新调度后检测一个空闲期，以便释放任何过期的 RCU 对象。内部，RCU 用于 ARP 表以及 TCP 和 UDP 套接字表。 Shenango 提供了 C++ 和 Rust 的绑定，具有惯用的接口（例如，像 std::thread）和分别支持 lambda 和闭包的功能。大多数绑定都是围绕底层 C 库的薄包装。然而，我们的 uthread 支持利用了一个独特的优化。我们扩展了 Shenango 的 spawn 函数，在每个 uthread 栈的底部预留空间用于跳板数据（捕获、返回值空间等），避免了额外的分配。 Preemption. 当 IOKernel 发送 SIGUSR1 信号时，Linux 内核会将 CPU 状态保存到线程栈上的 trapframe 中，并调用运行时安装的信号处理程序。信号处理程序立即将控制权转移到调度器上下文并停车，将被抢占的 uthread 重新放回运行队列。运行中的 uthread 最终可能被另一个 kthread 窃取，或者如果重新分配到核心则在同一 kthread 上恢复运行。 GMP? cooperative 在运行时执行的某些关键部分，通过增加线程本地计数器来延迟抢占信号。这些部分包括整个调度器上下文、RCU 和自旋锁的关键部分，以及访问每个 kthread 状态的代码区域。支持活动 uthread 的抢占带来了一些挑战。如果线程上下文开始在不同的 kthread 上执行，指向 thread-local storage（TLS）的指针可能会变得陈旧。不幸的是，gcc 没有提供禁用这些地址缓存的方法。据我们所知，Microsoft 的 C++ 编译器是唯一支持这一点的编译器。作为变通方案，我们使用自己的 TLS 机制来处理在调度器上下文之外访问的每个 kthread 数据结构，并且目前要求应用程序在访问线程本地变量（包括 glibc 的 malloc 和 free）时禁用抢占。我们正在考虑扩展运行时以支持每个 uthread 的 TLS，从而减轻开发者的负担。然而，TLS 数据部分必须保持较小，以防止在创建 uthread 时出现较高的初始化开销。 延迟抢占，自己实现 TLS 使得 uthread 不能访问已经失效的缓存？ ","date":"2024-09-30","objectID":"/posts/paper-shenango/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#runtime-implementation"},{"categories":["OS"],"collections":null,"content":"Evaluation latency and CPU efficiency sudden bursts in load What is the contribution of the individual mechanisms in Shenango to its observed performance? hyper-threads 我们比较了 Shenango 与 Arachne、ZygOS 和 Linux。 我们评估了 memcached 我们还用 Rust 编写了几个新的 Shenango 应用程序来测量不同的负载模式，利用了语言特性如闭包和移动语义。例如，我们实现了一个 spin-server，通过使用 CPU 一段时间来模拟计算密集型应用程序，然后再响应每个请求。 此外，我们实现了 loadgen，一个现实的负载生成器，可以为我们的 spin-server 以及 memcached 生成精确定时的请求模式。这两款应用程序共需要 1,366 行代码。为了与其它系统进行比较，我们使用了 ZygOS 仓库 [7] 中的 ZygOS 和 Linux spin-server 变体，并为 Arachne 实现了自己的 spin-server。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#evaluation"},{"categories":["OS"],"collections":null,"content":"CPU Efficiency and Latency memcached, spin-server, gdnsd Shenango 必须将一个核心（2 个超线程）专用于运行 IOKernel，因此应用程序可用的超线程减少了两个 IOKernel 占用的核心不能处理其他事情，那这个核心会导致缓存行失效，L1 miss 率高的问题吗 Shenango 在几乎所有负载下的批处理应用吞吐量都优于其他系统。在极低负载下，Linux 因为没有为 IOKernel 或核心仲裁器预留超线程，所以批处理吞吐量最高。 Shenango 基于 DPDK，为什么不和 DPDK 比一下呢 而且 memcached 几乎都是 99.8% GET 请求，写会导致什么呢 为了评估 Shenango 在存在批处理应用的情况下处理服务时间变化的能力，我们运行了 spin-server，Shenango 每秒可重新分配核心多达 60,000 次，能够快速适应负载突增并保持较低的尾部延迟，同时将未使用的周期授予批处理应用。 我们通过同时运行 gdnsd 和 swaptions 来评估 UDP 性能， ","date":"2024-09-30","objectID":"/posts/paper-shenango/:9:1","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#cpu-efficiency-and-latency"},{"categories":["OS"],"collections":null,"content":"Resilience to Bursts in Load In this experiment, we generate TCP requests with 1 µs of fake work 虽然排除了 Linux 和 ZygOS 但我认为这个指标很重要，面对高负载可以反应迅速，尾部延迟还是很低，几乎是一条直线，怎么做到的 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:9:2","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#resilience-to-bursts-in-load"},{"categories":["OS"],"collections":null,"content":"Microbenchmarks 还给每个组件进行了 benchmark Thread library Network stack and core allocation overheads Packet load balancing. Core allocation interval. ","date":"2024-09-30","objectID":"/posts/paper-shenango/:9:3","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#microbenchmarks"},{"categories":["OS"],"collections":null,"content":"Conclusion ZygOS 与 Shenango 最为相似；它在 IX 的基础上增加了工作窃取，以改进应用程序内的负载均衡。然而，这些系统都无法在细粒度上动态地在应用程序之间重新分配核心。相反，它们静态地将核心分区到应用程序，或者使用外部控制平面在大时间尺度上重新配置核心分配。 ","date":"2024-09-30","objectID":"/posts/paper-shenango/:9:4","tags":["Paper Reading"],"title":"Paper Reading: Shenango","uri":"/posts/paper-shenango/#conclusion"},{"categories":["OS"],"collections":null,"content":"The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems Datacenter system 指的是 MPP 吗？ Github DemiKernel 好像最近还在更新 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:1:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#the-demikernel-datapath-os-architecture-for-microsecond-scale-datacenter-systems"},{"categories":["OS"],"collections":null,"content":"Abstract 数据中心系统和 I/O 设备现在以单位微秒的延迟运行，需要 ns 级别的操作系统。传统的基于内核的操作系统带来了难以承受的开销，因此最近的内核绕过操作系统[73]和库[23]从 I/O 数据路径中消除了操作系统内核。然而，这些系统都没有提供一个通用的数据路径操作系统的替代品，以满足 μs 规模系统的需要。提出了一种适用于异构内核旁路设备和 μs 规模数据中心系统的柔性数据通路操作系统 Demikernel。我们构建了两个原型 Demikernel 操作系统，并展示了移植现有 μs 规模系统所需的最小努力。一旦移植，Demikernel 允许应用程序在具有 ns 级别开销且没有代码更改的异构内核旁路设备上运行 nanoseconds 比 microseconds 还要小吧，什么样的 os 是 ns 级别的？ 这篇文章和 XDP 的区别是什么？ ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:2:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#abstract"},{"categories":["OS"],"collections":null,"content":"Overview 网络往返时间、磁盘访问以及像 Redis 这样的内存系统[80]，可以实现个位数的微秒级延迟。 为了避免成为瓶颈，datapath systems software 必须在亚微秒级——或纳秒级——延迟下运行。 为了最小化延迟，广泛部署的内核旁路设备[78, 16]将传统操作系统内核移动到 control path，并让微秒级应用程序直接执行 datapath I/O.。 内核旁路设备将操作系统的保护功能（例如，隔离、地址转换）offload 到用户空间，以安全地提供用户级 I/O，并且更强大的设备实现一些操作系统管理（例如，网络）以进一步减少 CPU 使用。现有的内核旁路库[57, 23, 44]提供了一些缺失的操作系统组件；然而，没有一个库是通用的、可移植的数据路径操作系统。 缺少了什么？我感觉全是不通用、难以移植的吧，除非真的合入 Linux Kernel 比如 XDP 没有标准的数据路径架构和通用的数据路径操作系统，内核旁路对于微秒级应用程序来说很难利用。程序员不希望为不同的设备重新架构应用程序，因为他们可能无法提前知道哪些设备可用。新设备功能似乎每年都在发展， 因此，微秒级应用程序需要一个具有可移植操作系统的数据路径架构，该操作系统实现了常见的操作系统管理：存储和网络堆栈、内存管理和 CPU 调度。除了支持具有纳秒级延迟的异构设备外，数据路径操作系统还必须满足微秒级应用程序的新需求。例如，零拷贝 I/O 对于减少延迟非常重要，因此微秒级系统需要一个具有清晰零拷贝 I/O 语义和内存管理的 API，以协调应用程序和操作系统之间的共享内存访问。同样，微秒级应用程序每几微秒执行一次 I/O，因此应用程序工作和操作系统任务之间的细粒度 CPU 多路复用也是至关重要的。 所以本文的最重要的点是可移植性？ 零拷贝、更好的内存管理、CPU 调度，应该大家都是相同的 本文介绍了 Demikernel，一个为异构内核旁路设备和微秒级内核旁路数据中心系统设计的灵活数据路径操作系统和架构。Demikernel 定义了： (1) 新的数据路径操作系统管理功能，适用于微秒级应用程序， (2) 一个新的可移植数据路径 API（PDPIX），以及 (3) 一个灵活的数据路径架构，用于最小化异构设备之间的延迟。Demikernel 数据路径操作系统与传统的控制平面内核（例如，Linux 或 Windows）一起运行，并由具有相同 API、操作系统管理功能和架构的可互换库操作系统组成。 每个库操作系统都是设备特定的：它在可能的情况下卸载到内核旁路设备，并在用户空间库中实现剩余的操作系统管理。这些库操作系统旨在简化跨异构内核旁路设备的微秒级数据中心系统的开发，同时最小化操作系统开销。 需要设备支持吗 Demikernel 遵循从内核导向的操作系统向库导向的数据路径操作系统的趋势，这一趋势是由越来越高效的 I/O 设备导致的 CPU 瓶颈所驱动的。它不是为那些直接访问内核旁路硬件（例如，HPC [45]，软件中间件[90, 72]，RDMA 存储系统[17, 98, 7, 101]）而设计的系统，因为它施加了一个隐藏更复杂设备功能（例如，单边 RDMA）的通用 API。 本文描述了两个用于 Linux 和 Windows 的原型 Demikernel 数据路径操作系统。我们的实现主要使用 Rust，利用其在数据路径操作系统堆栈中的内存安全优势。我们还描述了新的零拷贝、纳秒级 TCP 堆栈和内核旁路感知内存分配器的设计。我们的评估发现，使用 Demikernel 构建和移植应用程序很容易，I/O 处理延迟约为 50 纳秒每 I/O，与直接使用内核旁路 API 相比，峰值吞吐量开销为 17-26% 新的零拷贝、纳秒级 TCP 堆栈和内核旁路感知内存分配器 50ns 有比较吗，吞吐量 10-20% 是什么带来的 tradeoff 呢 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:3:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#overview"},{"categories":["OS"],"collections":null,"content":"Demikernel Datapath OS Requirements 现代内核旁路设备、操作系统和库消除了操作系统内核在 I/O 数据路径中的作用，但没有完全替代其所有功能，从而在内核旁路操作系统架构中留下了一个空白。这个空白暴露了一个关键问题：微秒级系统的正确数据路径操作系统替代方案是什么？本节详细介绍了微秒级系统和异构内核旁路设备的需求，这些需求推动了 Demikernel 的设计。 heterogenous 这个词其实之前看论文很少注意，有没有具体场景？ ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:4:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#demikernel-datapath-os-requirements"},{"categories":["OS"],"collections":null,"content":"Support Heterogenous OS Offloads 对比了 arrakis：app 绕过 kernel，通过 libOS 来做 user I/O，其他 caladan/eRPC 也很类似 DemiKernel 应该和 arrakis 也差不多吧， 当今的数据路径架构是 ad-hoc 的：现有的内核旁路库[34, 73]在特定的内核旁路设备之上提供了不同的操作系统功能。由于不同的设备卸载了不同的操作系统功能，因此可移植性具有挑战性。例如，DPDK 提供了一个低级别的原始 NIC 接口，而 RDMA 实现了一个具有拥塞控制和有序、可靠传输的网络协议。因此，使用 DPDK 的系统实现了一个完整的网络堆栈，这对于使用 RDMA 的系统来说是不必要的。 ad-hoc 实习的时候经常见到的词汇，从来没理解，到底是自己定制还是说临时方案，或者是特殊的方案，非通用？ 这种异构性源于硬件设计者长期以来一直面临的根本权衡[50, 66, 10]——卸载更多功能可以提高性能，但会增加设备复杂性——随着最近的研究提出越来越复杂的卸载功能[52, 41, 86]，这种情况只会变得更糟。例如，DPDK 更通用且广泛可用，但 RDMA 在数据中心内的微秒级系统中实现了最低的 CPU 和延迟开销，因此任何数据路径操作系统都必须可移植地支持两者。未来的 NIC 可能会引入其他权衡，Given this complexity, Demikernel’s design must portably manage complex zero-copy I/O coordi- nation between applications and OS components. 所以到底是做一个更好的，还是更通用的？这才是 tradeoff 吗？ complexity 是真的需要考虑的吗 ，RDMA 和 DPDK 使用起来好像并不太复杂，大部分 NIC 应该也支持 RDMA ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:4:1","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#support-heterogenous-os-offloads"},{"categories":["OS"],"collections":null,"content":"Multiplex and Schedule the CPU at μs-scale 微秒级数据中心系统通常每几微秒 us 执行一次 I/O；因此，数据路径操作系统必须能够在类似的速度下多路复用和调度 I/O 处理和应用程序工作。现有的内核级抽象，如进程和线程，对于微秒级调度来说过于粗粒度 coarse-grained，因为它们会占用整个核心数百微秒。因此，内核旁路系统缺乏通用的调度抽象。 datapath OS 应该就是专指 kernel bypass 方法 最近的用户级调度器[9, 75]在每个 I/O 的基础上以微秒级分配应用程序工作者；然而，它们仍然使用粗粒度的抽象来处理操作系统工作（例如，整个线程[23]或核心[30]）。有些调度器更进一步，采用了微内核方法，将操作系统服务分离到另一个进程[57]或环[3]中以提高安全性。 没懂，但这个微内核就是 IX，使用了 Dune 虚拟化进程 微秒级的 RDMA 系统通常交错 I/O 和应用程序请求处理。这种设计使得调度变得隐式而不是显式：数据路径操作系统无法控制分配给应用程序与数据路径 I/O 处理的 CPU 周期的平衡。例如，FaRM [17]和 Pilaf [64]在消息到达时总是立即执行 I/O 处理，即使有更高优先级的任务（例如，为可能被丢弃的传入数据包分配新的缓冲空间）。 考虑调度是不是这篇论文比较有意思的地方 这些系统都不是理想的，因为它们的调度决策仍然是分布式的，要么在内核和用户级调度器之间（对于 DPDK 系统），要么在代码之间（对于 RDMA 系统）。最近的工作，如 eRPC [34]，已经表明，在单个线程上多路复用应用程序工作和数据路径操作系统任务是实现纳秒级开销所必需的。因此，Demikernel 的设计需要一个微秒级调度抽象和调度器。 应该是基于 arrakis 或者类似的架构，记得之前是没太看懂 arrakis 是怎么实现的，应该也是虚拟化技术吧 IOMMU SR-IOV ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:4:2","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#multiplex-and-schedule-the-cpu-at-μs-scale"},{"categories":["OS"],"collections":null,"content":"Demikernel Overview and Approach Demikernel 是首个满足微秒级应用程序和内 kernel-bypass devices 需求的“数据路径操作系统”。它引入了新的操作系统特性和新的可移植数据路径 API，这些对程序员是可见的，同时还有新的操作系统架构和设计，这些对程序员是不可见的。本节概述了 Demikernel 的方法，下一节将描述对程序员可见的特性和 API，而第 5 节将详细介绍 Demikernel（库）操作系统的架构和设计。 什么叫满足内核旁路设备？ ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:5:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#demikernel-overview-and-approach"},{"categories":["OS"],"collections":null,"content":"Design Goals Simplify μs-scale kernel-bypass system development Offer portability across heterogenous devices Demikernel 应该让应用程序无需代码更改就能在多种类型的内核旁路设备（例如，RDMA 和 DPDK）和虚拟化环境中运行。 Achieve nanosecond-scale latency overheads 不太懂这三个目标和以前的有什么太大区别，同样是内核旁路和新的 API，为什么说就更加可移植呢 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:5:1","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#design-goals"},{"categories":["OS"],"collections":null,"content":"System Model and Assumptions Demikernel 依赖于流行的内核旁路设备，包括 RDMA [61] 和 DPDK [16] NIC 以及 SPDK 磁盘 [88]，同时也适应未来的可编程设备 [60, 58, 69]。我们假设 Demikernel 数据路径操作系统与应用程序运行在同一个进程和线程中，因此它们相互信任，任何隔离和保护都由控制路径内核或内核旁路设备提供。这些假设在数据中心中是安全的，因为在数据中心中，应用程序通常自带库和操作系统，数据中心运营商使用硬件强制执行隔离。 Demikernel 使得应用程序内存可以直接通过网络发送，因此应用程序必须仔细考虑敏感数据的位置。如果必要，这一功能可以被关闭。其他技术，如信息流控制或验证，也可以被利用来确保应用程序内存的安全性。 其实就是 LibOS？用户态的一个库提供运行运行时吗 发送内存，是 DPDK 还是 RDMA 没看懂这个安全的假设 To minimize datapath latency, Demikernel uses cooperative scheduling 因此应用程序必须在紧密的 I/O 处理循环中运行（ 引入了调度的概念，协作调度应该是适合 IO 密集型的任务 我们的原型目前专注于独立调度单个 CPU 核心，依赖硬件支持进行多核调度 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:5:2","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#system-model-and-assumptions"},{"categories":["OS"],"collections":null,"content":"Demikernel Approach A portable datapath API and flexible OS architecture. 新的 API 称之为 PDPIX Portable datapath? PDPIX 扩展了标准的 POSIX API，以更好地适应微秒级内核旁路 I/O。微秒级内核旁路系统是面向 I/O 的：它们花费大部分时间和内存来处理 I/O。因此，PDPIX 围绕 I/O 队列抽象展开，使 I/O 显式化：它允许微秒级应用程序提交整个 I/O 请求，消除了 POSIX 基于管道的 I/O 抽象的延迟问题。 为了最小化延迟，Demikernel 库操作系统尽可能地将操作系统功能卸载到设备上，并在软件中实现剩余的功能 啥意思？ A DMA-capable heap, use-after-free protection Demikernel 提供了三个新的外部操作系统特性，以简化零拷贝内存协调 （1）具有清晰语义的 I/O 内存缓冲区所有权的可移植 API，（2）零拷贝、DMA 能力的堆，以及（3）零拷贝 I/O 缓冲区的使用后释放（UAF）保护。与 POSIX 不同，PDPIX 定义了清晰的零拷贝 I/O 语义：应用程序在调用 I/O 时将所有权传递给 Demikernel 数据路径操作系统，并且在 I/O 完成之前不会收回所有权。 什么叫 clear semantics for I/O memory buffer ownership 只是语义上的加强？ UAF 保护 (User after free) 悬垂指针的问题吧，感觉其实就是 Rust 带来的好处 Coroutines and μs-scale CPU scheduling 内核旁路调度通常以每个 I/O 为基础进行；然而，POSIX API 并不适合这种用途。epoll 和 select 有一个众所周知的“惊群”问题 [56]：当套接字共享时，不可能将事件精确地传递给一个工作者。因此，PDPIX 引入了一个新的异步 I/O API，称为 wait，它允许应用程序工作者等待特定的 I/O 请求，并且数据路径操作系统可以显式地将 I/O 请求分配给工作者。 thundering herd 多个进程等待一个资源时候，可能会导致惊群，但是其实有别的处理方法比如 epoll_exclusive 这个 wait 是 rust Tokio? 还是 asyncio 提供的？ Demikernel 使用协程 Coroutines 来封装操作系统和应用程序的计算。协程是轻量级的，具有低成本的上下文切换，并且非常适合 I/O 堆栈通常需要的基于状态机的异步事件处理。我们选择协程而不是用户级线程（例如，Caladan 的绿色线程 [23]），后者可以同样出色地执行，因为协程封装了每个任务的状态，消除了全局状态管理的需要。例如，Demikernel 的 TCP 堆栈为每个 TCP 连接使用一个协程进行重传，这保持了相关的 TCP 状态。 协程不就是用户级线程吗 每个库操作系统都有一个集中化的协程调度器，针对内核旁路设备进行了优化。由于在纳秒级 [33, 15] 中断是不可承受的，Demikernel 协程是协作式的，它们通常在几微秒或更短的时间内让出。传统的协程通常通过轮询工作：调度器运行每个协程以检查进度。然而，我们发现轮询在纳秒级是不可承受的，因为大量协程被阻塞在偶发的 I/O 事件上（例如，TCP 连接的包到达）。因此，Demikernel 协程也是可阻塞的。调度器将可运行和阻塞的协程分开，并在事件发生时仅将阻塞的协程移动到可运行队列中。 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:5:3","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#demikernel-approach"},{"categories":["OS"],"collections":null,"content":"PDPIX: A Portable Datapath API PDPIX 是面向队列的，而不是面向文件的 不面向文件是什么意思。。fd 变成 队列描述符 I/O Queues: queue() 创建一个轻量级的内存中队列，类似于 Go 通道 为什么要把文件变成队列？ Network and Storage I/O: Memory: 应用程序不为传入的数据分配缓冲区；UAF protection Scheduling: PDPIX 用异步的 wait_* 调用替换了 epoll 事件驱动、异步？ ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:5:4","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#pdpix-a-portable-datapath-api"},{"categories":["OS"],"collections":null,"content":"Demikernel Datapath Library OS Design ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#demikernel-datapath-library-os-design"},{"categories":["OS"],"collections":null,"content":"Design Overview 每个 Demikernel 库操作系统支持单一类型的内核旁路 I/O 设备（例如，DPDK、RDMA、SPDK），并由一个用于 I/O 设备的 I/O 处理堆栈、一个特定于库操作系统的内存分配器和一个集中化的协程调度器组成。为了同时支持网络和存储，我们将库操作系统集成到一个单一的库中，用于两种设备（例如，RDMAxSPDK）。 Rust 通过语言特性和编译器强制执行内存安全 跨平台的可移植性 Rust 对协程有出色的支持 demikernel github 还在更新 使用 Rust 的主要缺点是需要许多跨语言绑定，因为内核旁路接口和微秒级应用程序仍然主要用 C/C++ 编写 Demikernel 库操作系统使用 Rust 的 async/await 语言特性 [85] 在协程中实现异步 I/O 处理。Rust 利用对生成器的支持将命令式代码编译成带有转换函数的状态机。Rust 编译器不直接保存寄存器和交换栈；它将协程编译成带有“在栈上”值直接存储在状态机中的常规函数调用 [55]。使用 Rust 的这个关键好处使得协程上下文切换轻量且快速（在我们的 Rust 原型中约为 12 个周期），并帮助我们的 I/O 堆栈在关键路径上避免真正的上下文切换。虽然 Rust 的语言接口和编写协程的编译器支持是定义良好的，但 Rust 目前没有协程运行时。因此，我们在每个库操作系统中实现了一个简单的协程运行时和调度器，该调度器针对每个内核旁路设备所需的 I/O 处理量进行了优化。 async? DPDK/RDMA 其实是 c 写的吧，应该也有 rust-implementation 这里提到的协程运行时很有意思，Go 的应该就是是 GMP M:N 之类的？在 runtime package 图例看上去是一个更加大一统的架构，而不是很有创新自己的方法，能不能理解为，根据不同的 API 选择不同的 LibOS ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:1","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#design-overview"},{"categories":["OS"],"collections":null,"content":"I/O Processing Demikernel I/O 堆栈共享应用程序线程，并旨在实现运行到完成：快速路径协程处理传入数据，找到阻塞的 qtoken，调度应用程序协程并处理任何传出消息，然后再继续下一个 I/O。同样，Demikernel I/O 堆栈在 push 中内联传出 I/O 处理（在应用程序协程中），并在无错误情况下提交 I/O（到异步硬件 I/O API）。尽管在快速路径和应用程序协程之间会发生协程上下文切换，但它不会中断运行到完成，因为 Rust 将协程切换编译为函数调用。 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:2","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#io-processing"},{"categories":["OS"],"collections":null,"content":"Memory Management 每个 Demikernel 库操作系统使用特定于设备的、经过修改的 Hoard 进行内存管理 Hoard 资源分配器 替代 malloc？所以这篇文章基本就是缝合吗 对于使用后释放保护，所有 Demikernel 分配器提供了一个简单的引用计数接口：inc_ref(void _) 和 dec_ref(void _)。请注意，这些接口不是 PDPIX 的一部分，而是 Demikernel 库操作系统的内部接口。库操作系统 I/O 堆栈在发出 I/O 时调用 inc_ref，并在完成缓冲区时调用 dec_ref。如前所述，Demikernel 库操作系统可能需要长时间持有引用；例如，TCP 堆栈只有在接收方确认数据包后才能安全地调用 dec_ref 应该自己实现了一个引用计数来做 UAF 但引用计数会不会不够呢，内核态会不会存在循环引用？ ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:3","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#memory-management"},{"categories":["OS"],"collections":null,"content":"Coroutine Scheduler A Demikernel libOS has three coroutine types, reflecting com- mon CPU consumers: (1) a fast-path I/O processing coroutine for each I/O stack that polls for I/O and performs fast-path I/O processing, (2) several background coroutines for other I/O stack work (e.g., managing TCP send windows), and (3) one application coroutine per blocked qtoken, which runs an application worker to process a single reques 每个库操作系统调度器优先运行可运行的应用程序协程，然后是后台协程和始终可运行的快速路径协程，按 FIFO 方式进行 因此，每个调度器一次运行一个协程。我们期望协程设计将扩展到更多核心。然而，Demikernel 库操作系统需要仔细设计以避免跨核心的共享状态，因此我们尚不清楚这是否会成为一个主要限制。 跨核心的共享状态，dpdk 又怎么做呢，锁？感觉都是用多队列缓冲 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:4","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#coroutine-scheduler"},{"categories":["OS"],"collections":null,"content":"Network and Storage LibOS Integration Demikernel 内存分配器为 DPDK 网络或 SPDK 存储 I/O 提供 DMA 能力的内存。我们修改 Hoard，从 DPDK 内存池为 SPDK 分配内存对象，并将相同的内存注册到 RDMA。我们在轮询 DPDK 设备和 SPDK 完成队列之间拆分快速路径协程，以轮询方式分配 CPU 周期的公平份额，在无待处理 I/O 的情况下。未来可以实现更复杂的 CPU 周期在网络和存储 I/O 处理之间的调度。总的来说，网络和存储数据路径库操作系统之间的可移植集成显著简化了在网络和存储内核旁路设备上运行的微秒级应用程序。 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:6:5","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#network-and-storage-libos-integration"},{"categories":["OS"],"collections":null,"content":"Demikernel Library OS Implementations We prototype two Demikernel datapath OSes: DEMILIN for Linux and DEMIWIN for Windows 其实做 Linux 应该不难，大家都在做。但是做 Windows 的有点厉害吧 DEMIWIN 目前仅支持通过 WSL 的 Catpaw 和 Catnap POSIX 库操作系统实现的 RDMA 内核旁路设备 DEMIWIN 实现 DEMIWIN 是 Demikernel 在 Windows 上的实现，目前仅支持 RDMA 内核旁路设备。它通过 WSL（Windows Subsystem for Linux）使用 Catpaw 和 Catnap POSIX 库操作系统。Catpaw 基于 NDSPI v2 构建 不太清楚这里说的支持 windows 到底是 wsl 还是 raw windows ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:7:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#demikernel-library-os-implementations"},{"categories":["OS"],"collections":null,"content":"Catnap POSIX Library OS ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:7:1","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#catnap-posix-library-os"},{"categories":["OS"],"collections":null,"content":"Catmint and Catpaw RDMA Library OSes Catmint 在 rdma_cm [79] 接口之上构建 PDPIX 队列来管理连接，并使用 ib_verbs 接口高效地发送和接收消息。它使用双边 RDMA 操作来发送和接收消息，这简化了对 wait_* 接口的支持。我们在 NSDPI 之上的 Catpaw 使用了类似的设计。 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:7:2","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#catmint-and-catpaw-rdma-library-oses"},{"categories":["OS"],"collections":null,"content":"Catnip DPDK Library OS ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:7:3","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#catnip-dpdk-library-os"},{"categories":["OS"],"collections":null,"content":"Cattree SPDK Library OS 命名感觉怪怪的，这里又 Cat 什么什么 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:7:4","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#cattree-spdk-library-os"},{"categories":["OS"],"collections":null,"content":"Evaluation ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:8:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#evaluation"},{"categories":["OS"],"collections":null,"content":"Experimental Setup ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:8:1","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#experimental-setup"},{"categories":["OS"],"collections":null,"content":"Programmability for μs-scale Datacenter Systems TURN UDP Relay: Teams / Skype 他将 TURN 服务器移植到 Demikernel 花费了 1 天 [38]，而移植到 io_uring [12] 和 Seastar [42] 各花费了 2 天。最终，他无法使 Seastar 工作，并且遇到了 io_uring 的问题。 Redis: TxnStore: 太长了这篇论文，直接根据 evaluation 做点分析 两个内核旁路应用程序（testpmd [93] 和 perftest [84]）就是 DPDK 和 RDMA 三个最新的内核旁路库（eRPC [34]、Shenango [71] 和 Caladan [23]） 首先比较的是可编程性，用的是 LoC。。第一次见，又不是说差几个数量级，同数量级下就 10% 不到的代码量减少，根本就是语言设计风格和误差吧，让不会用 Rust 的写 DemiKernel 比 C 带来的负担应该更大？不过 C 应该能调用 DemiKernel 但是让专家来移植一些经典应用来说还是有信服力的 只不过 redis 用了 epoll，论文换成了队列，解决了一些 epoll 带来的问题，metrics 在 7.5 figure 11 可以看吹的那么好，还加个了 persistent Log 指标，为什么又要比较 disk 性能了。 处于 POSIX 的性能下降了几乎 30%+，RDMA 又没有参考对比，DPDK 有提升，但也没有参考。 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:8:2","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#programmability-for-μs-scale-datacenter-systems"},{"categories":["OS"],"collections":null,"content":"Echo Application 就结果来看，并没有太好，先不说图有点误导人，加上个 everything else 又不说是什么， 同样 DPDK / Catnip 带来了 2-3 us 的延迟，这又和 motivation 矛盾，而这只是简单的 echo，更复杂的呢？ 论文还提到了跨多核心的问题 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:8:3","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#echo-application"},{"categories":["OS"],"collections":null,"content":"Conclusion 集大成的论文，总的来说是不错的论文，恶补 MIT OS 的计划要提上日程了 但 motivatin 有点弱，通用性和性能是很经典的 tradeoff，但我不认为有很好的通用性，用户要用 dpdk 就直接用 dpdk 官方了，也不会突然转头说要来一个 rdma，同时使用两种的场景会受硬件限制吧 性能说是没有削弱太多，但给的 evaluation 都没什么说服力，echo 太简单，redis 性能只有 dpdk 不错但没有参照了，不知道是不是我看漏了什么 其他一些带来的优势更像是语言特性，UAF 之类的。唯一有意思的还是用队列替换 epoll 什么的，加入异步，但感觉也是语言特性。 零拷贝应该是很有意思的地方，论文说是实现了很不错的零拷贝，但感觉只是语义上的，能不能把 kafka 什么的拉出来做个比较呢 ","date":"2024-09-24","objectID":"/posts/paper-demikernel/:9:0","tags":["Paper Reading"],"title":"Paper Reading: The Demikernel Datapath OS Architecture for Microsecond-scale Datacenter Systems","uri":"/posts/paper-demikernel/#conclusion"},{"categories":["OS"],"collections":null,"content":"Rethinking the RDMA Interface for Distributed Systems 对 RDMA 没什么了解 Linux 高性能网络详解：从 DPDK、RDMA 到 XDP，这本书看评价很不错，有机会可以看看 DMA: IO 设备比如网卡，需要从内存拿发送的数据，需要告诉 CPU 从内存缓冲区复制，许多复制会让 CPU 阻塞。DMA 控制器，可以专门读写内存，绕开了 CPU，但 DMA 控制器一般和 IO 设备一起？ RDMA：绕开 TCP/IP 协议栈来读取远端的内存。传统网络 A 发送消息给 B，A 内存数据复制到 B 的内存，需要 CPU 中断、协议栈处理等等，RDMA 则可以直接从内存复制数据，用硬件组装后发送到对方网卡。 RDMA 协议：InfiniBand ","date":"2024-09-22","objectID":"/posts/paper-prism/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#rethinking-the-rdma-interface-for-distributed-systems"},{"categories":["OS"],"collections":null,"content":"Abstract RDMA + distributed systems However, most of the distributed protocols used in these systems cannot easily be expressed in terms of the simple memory READs and WRITEs provided by RDMA 引入额外的协议支持 RDMA 还是放弃 RDMA，additional round trip 是什么意思 本文认为，RDMA interface 的扩展可以解决这一难题。我们介绍了 PRISM 接口，它添加了四个新的原语: indirection, allocation, enhanced compare-and-swap, and operation chaining。这些提高了 RDMA 接口的表达能力，同时仍然可以使用相同的底层硬件特性来实现。我们通过使用 PRISM 原语设计三个几乎不需要服务器端 CPU 参与的新应用程序来展示它们的实用性: (1) PRISM-KV，一个键值存储; (2) PRISM-RS，一个 replicated block store; (3) PRISM-TX，一个分布式事务协议。使用基于软件的 PRISM 原语实现，我们表明这些系统优于先前基于 RDMA 的等效系统 扩展 RDMA 接口 RDMA 应该是分布式存储里的热门方向 ","date":"2024-09-22","objectID":"/posts/paper-prism/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction reduce the CPU cost of packet processing This makes RDMA, which provides a standard, accelerated interface for one host to directly access another’s memory, appealing: hardware RDMA implementations bypass the host CPU entirely [28], and even software implementations offer significant performance improvements by simplifying the network stack and reducing context-switching overhead 软件绕过协议栈减少上下文切换 + 硬件绕过 CPU A common theme is that adapting applications to run on RDMA requires complex—and costly—contortions. Consequently, many RDMA applications are forced to add extra operations, i.e., extra network round trips, to their protocols, sacrificing some of the latency benefits network round trips 有例子吗，引用了一篇 RDMA KV store Christopher Mitchell, Yifeng Geng, and Jinyang Li. 2013. Using One-sided RDMA Reads to Build a Fast, CPU-efficient Key-value Store. In Proceedings of the 2013 USENIX Annual Technical Conference USENIX, San Jose, CA, USA 额外的 RTT 是说需要计算吗，A-\u0026gt;B 请求生成数据，B 生成数据存在内存，A RDMA READ 其他一些则采用混合设计，需要在某些操作中涉及应用程序 CPU[10, 31, 43]，或者在某些情况下，仅仅使用 RDMA 来实现更快的消息传递协议[15, 34]，从而抵消了 CPU 旁路的益处。 如果用 RDMA 做消息传递是不够高效的？多次 RTT。为什么说 negating the benefits of CPU bypass 本文认为，超越基本的 RDMA 接口对于实现构建低延迟系统的全部网络加速潜力是必要的。RDMA 接口最初是为了支持并行超级计算应用而设计的，但它不能满足当今分布式系统的需求。我们展示了通过使用一些额外的原语来扩展接口，完全使用远程操作来实现复杂的分布式应用程序(如复制存储 replicated storage)成为可能。 存储副本，冗余？ 我们在本文中的目标是确定 RDMA 接口的一组通用(即非特定于应用程序的)扩展，允许分布式系统更好地利用 RDMA 硬件的低延迟和 CPU 卸载能力 better utilize the low latency and CPU offload capabilities。我们的提议是 PRISM 接口。PRISM 扩展了 RDMA 读/写接口，增加了四个基本元素: indirection, allocation, enhanced compare-and-swap, and operation chaining. 结合起来，它们支持常见的远程访问模式，比如数据结构导航、异地更新和并发控制。我们认为 PRISM API 非常简单，可以在 RDMA 网卡中实现，因为它重用了现有的微架构机制。我们还在一个基于软件的网络堆栈原型中实现了 PRISM 接口，该原型使用专用的 CPU 核心来实现远程操作，灵感来自于 Google 的 SNAP 网络堆栈所采用的方法 Google SNAP, Scalable Network Appliance Platform 应该也是用户网络栈 existing RDMA interface leads to extra protocol complexity for distributed systems. PRISM interface, which extends RDMA with additional primitives to support common patterns in distributed applications three sophisticated applications—key-value stores, replicated block storage, and distributed transactions can be implemented entirely using the PRISM interface. software-based prototype of the PRISM interface 尽管相对于 NIC 存在额外的性能开销，但在 PRISM 之上构建的应用程序仍能实现延迟和吞吐量的优势 什么样的 tradeoff? ","date":"2024-09-22","objectID":"/posts/paper-prism/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#introduction"},{"categories":["OS"],"collections":null,"content":"Background and Motivation RDMA 是一个广泛部署的[13,26]网络接口，允许远程客户端直接在远程主机上读写内存，完全绕过远程 CPU ","date":"2024-09-22","objectID":"/posts/paper-prism/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#background-and-motivation"},{"categories":["OS"],"collections":null,"content":"RPCs vs Memory Accesses: The RDMA Dilemma RDMA 提供了两种类型的操作。Two-sided 双边操作具有传统的消息传递 message-passing 语义。SEND 操作将消息传输到远程应用程序，该应用程序调用 RECEIVE。One-sided 单边操作允许主机在远程主机（在预注册的区域中）上进行内存的读取或写入。系统社区中关于是否使用单边或双边操作的争论一直存在 [16, 19, 43]。One-sided 单边操作更快且更节省 CPU 资源，但仅限于简单的读写操作。双边消息传递由于允许在两端进行处理，尽管通信操作本身较慢，但总体上可能会产生更快的系统。 tradeoff: 以 Pilaf [31]为例，这是一个早期的基于 RDMA 的键值存储系统。Pilaf 在哈希表中存储键值对象的指针，实际数据存储在单独的 extents 结构中。这两个结构都通过 RDMA 暴露出来，因此客户端可以通过远程读取哈希表，然后使用指针进行远程读取到 extents 存储中来执行键值查找。这不需要服务器端的 CPU 参与，但需要两次往返。使用双边操作构建的传统键值存储实现只需要一次往返，但每次操作都需要 CPU 参与。 为什么传统 kv 只需要一次 rt 但需要 cpu pilaf 应该是一次读哈希得到指针，一次读 extents 得到数据 双边：一次请求，返回数据，但是 cpu 处理？ 系统设计者的困境在于，是构建一个基于读写操作的更复杂协议，还是构建一个使用消息传递的更简单协议？换句话说，是执行两次（或更多次）单边 RDMA 操作更快，还是执行一次 RPC 更快？在 RDMA 的早期，选择是明确的：RDMA 操作比 RPC 快大约 20 倍 [31]。 本文的 tradeoff 到底是什么？ pilaf 是单边 one-sided: 多次 rt，基于读写操作的更复杂协议 two (or more) one-sided RDMA operations: 消息传递更简单协议，传统 kv，一次 rt，需要 cpu， single RPC：RDMA 比 rpc 快 20 倍 随着后续工作显著降低了双边 RPC 的成本 [16, 19]，并且 RDMA 在更大规模、更高延迟的环境中部署 [12, 13]，选择使用哪种操作变得更加复杂。 我们在两台通过 40 Gb 以太网连接的服务器上测量了单边 RDMA 操作与使用 eRPC [16]实现的双边 RPC 的性能 这论文感觉很多概念写得不太清楚，多次 rt 肯定简单吧，早期的做法也是多次 rt？ ","date":"2024-09-22","objectID":"/posts/paper-prism/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#rpcs-vs-memory-accesses-the-rdma-dilemma"},{"categories":["OS"],"collections":null,"content":"Principles for Post-RDMA Systems 大多数关于 RDMA 系统的工作假设我们仅限于当前的 RDMA 读/写接口。如果我们能够扩展 RDMA 接口呢？硬件供应商 [29] 和软件实现 [26] 已经以临时方式添加了各种新操作 Navigating data structures: RDMA 支持在已知大小和位置的情况下进行远程读取。大多数应用程序，如 Pilaf，使用更复杂的数据结构来构建索引、存储可变长度的对象，并帮助处理并发更新。遍历这些结构需要多次 RDMA 读取。能够对指针执行间接操作可以消除其中一些往返。 如果不知道位置和大小呢 Supporting out-of-place writes: 修改远程数据结构尤其具有挑战性，因为读取可能同时发生。为了避免随之而来的一致性问题，许多系统 [10, 31, 32] 仅从服务器 CPU 执行写入。我们的目标是构建能够使用 RDMA 操作处理读取和写入的系统。为此，我们提倡一种设计模式，其中新数据被异地写入单独的缓冲区，然后指针被原子更新以从旧值切换到新值——一种类似于并发编程中的读-复制-更新 [27] 的方法。实现这一点需要新的 RDMA 支持，以将数据写入新缓冲区，并原子更新指向其位置的指针。 RDMA KV 是如何处理的？本文看上去用了 RCU(Read-copy update) 应该很类似 Copy On Write COW？ COW 存在的问题是什么时候回收，RCU 使用 RCU-sync + CoW 来解决。 https://aandds.com/blog/linux-rcu.html RCU 的读性能比“读写锁”要好，它适应满足两个条件，读多写少，没有强一致要求 。 但 KV 一般会存在写多读少的情况？ Optimistic concurrency control: 复杂数据结构的更新需要同步。虽然今天可以使用 RDMA 实现锁 [44, 45]，但性能损失可能很大。扩展 RDMA 的比较-交换功能将使我们能够实现复杂的、基于版本的乐观并发控制 [21]，这种方法与我们的异地更新方法非常契合。 好奇 RDMA + 锁的性能会差多少 RCU 还有个关键点应该是链表 + 内存屏障？ Linux 内核也有 RCU，这能复用吗 https://wangxu.me/translation/2008/06/01/what-is-rcu/index.html https://www.kernel.org/doc/html/next/RCU/whatisRCU.html 过两天学一下顺便写一下 Chaining operations: 一个常见的主题是应用程序需要执行复合操作，其中一个操作的参数依赖于前一个操作的结果——读取一个指针，然后读取它指向的值，或者写入一个对象，然后交换指针。今天，这需要将中间结果返回给客户端并执行新操作——以及另一个网络往返。如果我们有一种方法可以将操作链接起来，使一个操作依赖于另一个操作，但仍然在一次往返中执行它们，我们可以避免这种开销。 是没太懂这种使用场景，有具体例子吗 ","date":"2024-09-22","objectID":"/posts/paper-prism/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#principles-for-post-rdma-systems"},{"categories":["OS"],"collections":null,"content":"The Case for an Extended Interface 扩展 RDMA 接口 另一种方法是 allow applications to provide their own code that runs on the remote host，即将自定义应用程序逻辑部署到智能网卡（Smart NICs） 我们主张采用一组简单、通用的原语。这种简单的扩展可能对更多的应用程序有用，无论是当前的还是未来的。简单性在这里也有助于实现：我们认为我们提出的原语可以添加到基于软件的网络堆栈、可重配置的智能网卡，甚至是未来的固定功能网卡中。本文的其余部分提出了这些通用扩展，并展示了它们对于常见应用程序的实用性。 还是有意思的，可以用在自定义网络栈或者 NIC ","date":"2024-09-22","objectID":"/posts/paper-prism/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#the-case-for-an-extended-interface"},{"categories":["OS"],"collections":null,"content":"PRISM Interface 为了解决使用 RDMA 构建分布式系统所固有的挑战，我们提出了一种扩展的网络接口，称为 PRISM（用于系统内存远程交互的原语）。PRISM 在现有的 RDMA 接口中增加了四个额外的功能。这些功能旨在支持我们在实现分布式协议时观察到的常见模式。PRISM 的接口设计基于三个原则：（1）通用性——它们不应编码特定于应用程序的功能；（2）最小接口复杂性；（3）最小实现复杂性，这使得性能快速且可预测，并便于在各种平台上实现，包括未来的 NIC ASIC。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-interface"},{"categories":["OS"],"collections":null,"content":"Indirect Operations 许多 RDMA 应用程序需要遍历远程数据结构。这些结构使用间接寻址来实现许多目的：提供索引、支持可变长度数据等。目前，跟随指针需要额外的往返。 PRISM 允许 READ、WRITE 和比较-交换（CAS）操作接受间接参数。这些操作的目标地址可以解释为指向实际目标的指针地址。此外，WRITE 或 CAS 操作的数据可以从服务器端内存位置读取，而不是从 RDMA 请求本身读取。 对于 READ 和 WRITE 操作，目标地址可以选择性地解释为⟨ptr, bound⟩结构。在这种情况下，操作长度限制为 bound 或客户端请求长度中较小的一个。这支持可变长度对象：客户端可以执行具有较大长度的读取，但只接收实际存储的数据量。 PRISM 中的间接操作重用了现有的 RDMA 安全机制，确保远程客户端只能操作主机内存中已被授予访问权限的区域。要使用间接操作访问内存区域，客户端必须包含主机在首次向 NIC 注册区域时生成的 rkey。如果目标地址或目标地址指向的位置位于具有不同 rkey（或未注册）的内存区域中，则主机将拒绝该操作。 应该和 RDMA 几乎一样，多了间接参数，比较-交换（CAS）可能是新的？为什么可以从服务器端内存读取是什么。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#indirect-operations"},{"categories":["OS"],"collections":null,"content":"Memory Allocation 使用现有的 RDMA 接口修改数据结构尤其具有挑战性：对象必须写入固定的预分配内存区域，这使得处理可变大小的对象或异地更新变得困难。需要的是一种内存分配原语。PRISM 提供了一种内存分配原语，它分配一个缓冲区并返回指向其位置的指针。 RDMA 都要注册内存区域吧，ibv_reg_mr 获得 rkey，PRISM 到底有什么区别，客户端可以通过一次间接读取操作获取链表中的多个节点，而不需要多次往返？ RDMA 读取链表，传统需要多次往返，READ 读取和下一个指针，然后重复？ PRISM 可以读多个？为什么啊 要使用 PRISM 的分配原语，服务器端进程将缓冲区队列（在 RDMA 术语中称为“发布”）注册到 NIC。当 NIC 从远程主机接收到 ALLOCATE 请求时，它从空闲列表中弹出一个缓冲区，将提供的数据写入缓冲区，并返回地址。这种操作在与 PRISM 的请求链接机制结合使用时特别强大，如下所述：PRISM 客户端可以分配一个缓冲区，写入其中，然后通过 CAS 将其指针安装到另一个数据结构中。 PRISM 在 NIC（或软件网络堆栈）数据平面执行内存分配。然而，内存注册由服务器 CPU 完成。这是必要的，因为注册内存需要与内核交互以识别相应的物理地址并固定缓冲区。由于服务器 CPU 参与其中，因此对于应用程序重用这些缓冲区的正确性至关重要，只有在并发 NIC 操作完成后，回收的缓冲区才能添加回空闲列表。虽然这简单地将 NIC 和服务器 CPU 同步的负担转移到原语的实现上，但它重要的是将这种同步从应用程序的常规路径中移除。 管理客户端分配的内存可能具有挑战性；这是通过远程访问修改状态的应用程序所面临的根本挑战。具体的内存管理策略由应用程序自行决定。本文中的应用程序使用客户端检测对象何时不再使用，例如，当先前版本已被替换时。它们将未使用的缓冲区报告给在服务器上运行的守护进程（通过传统的 RPC），该守护进程将其重新注册到 NIC 的空闲列表中；可以在客户端和服务器端使用批处理来最小化开销。另一种受垃圾收集启发的方法是服务器端应用程序代码定期扫描数据结构以识别可以回收的缓冲区。 我们的分配器设计有意保持简单，仅从特定队列中分配第一个可用的缓冲区。我们选择这种设计而不是更复杂的分配器，因为（如§4.2 所述）现有的 RDMA NIC 已经具有实现它所需的硬件支持。一个结果是，使用整个预分配缓冲区分配内存会引入空间开销。应用程序可以通过注册包含不同大小缓冲区的多个队列并选择适当的队列来最小化这种影响。例如，使用大小为 2 的幂的缓冲区可以保证最大空间开销为 2 倍。纯软件实现可能会选择使用更复杂的分配器。 支持 variable-sized objects or out-of-place updates. pre register a queue of buffers ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#memory-allocation"},{"categories":["OS"],"collections":null,"content":"Enhanced Compare-And-Swap 原子比较-交换（CAS）是并行系统中更新数据的一个经典原语。RDMA 标准已经提供了一个原子 CAS 操作 [30]，但它受到高度限制：它进行单一的相等性比较，然后交换一个 64 位的值。根据我们的经验，这对于实现高性能应用程序（包括§6–8 中的那些）是不够的。事实上，除了实现锁 [33, 45] 之外，很少有应用程序今天使用 RDMA 原子操作。虽然可以使用锁来实现任意复杂的原子操作，但这需要多次昂贵的往返并增加争用。 为了解决这个问题，我们通过三种方式扩展了 CAS 原语。首先，我们采用了 Mellanox RDMA 设备当前提供的扩展原子接口 [30]，该接口允许 CAS 操作最多 32 字节，并使用单独的位掩码来比较和交换参数，使得可以比较结构中的一个字段并交换另一个字段。其次，我们为目标地址或比较和交换值引入了间接寻址（§3.1）。我们不保证间接参数指针的解引用是原子的——只有 CAS 本身是原子的——但这允许我们从内存中加载参数值。最后，我们在比较阶段提供了对算术比较操作符（大于/小于）的支持，除了按位相等性之外。这支持了更新版本化对象的常见模式：检查新版本是否大于现有版本，如果是，则更新版本号和对象。 PRISM 的 CAS 操作相对于其他 PRISM 操作是原子的。与现有的 RDMA 原子操作一样，它们不保证与并发 CPU 操作的原子性。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#enhanced-compare-and-swap"},{"categories":["OS"],"collections":null,"content":"Operation Chaining 分布式应用程序通常需要执行一系列数据依赖的操作来读取或更新远程数据结构。例如，它们可能希望分配一个缓冲区，写入其中，然后更新一个指针以指向它。目前，每个操作必须返回到客户端，然后才能发出下一个操作。PRISM 提供了一种链接机制，允许像这样的复合操作在一次往返中执行。 Conditional operations: RDMA 通常不保证操作按顺序执行。我们添加了一个条件标志，延迟执行操作，直到来自同一客户端的前一个操作完成，并且仅在前一个操作成功时才执行。比较失败的 CAS 操作在这里被视为不成功。 Output redirection: 我们引入了另一个标志，指定操作的输出（READ 或 ALLOCATE）应写入指定的内存位置，而不是发送给客户端。该内存位置通常是每个连接的临时缓冲区。例如，可以执行 ALLOCATE，将其输出重定向到临时位置，然后使用条件 WRITE 将指向新分配缓冲区的指针存储在其他地方。 kv 确实存在先读后写的操作，这个条件标志是什么，per-connection temporary buffer ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#operation-chaining"},{"categories":["OS"],"collections":null,"content":"Discussions PRISM 原语共同实现了§2.2 和§2.3 中的目标。间接操作减少了导航数据结构所需的往返次数。ALLOCATE 和增强的 CAS 原语，结合链接，支持异地更新：应用程序可以在一次往返中 ALLOCATE 一个新缓冲区，将数据写入其中，并使用 CAS 将指向它的指针安装到另一个结构中。最后，CAS 操作的灵活性使得实现基于版本的并发控制机制成为可能。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:5:5","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#discussions"},{"categories":["OS"],"collections":null,"content":"PRISM Implementation PRISM 的 API 由简单的原语组成，以便它们可以轻松地添加到各种 RDMA 实现中。为了评估它们在构建分布式应用程序中的有效性，我们构建了一个基于软件的实现（§4.1）。我们还分析了在 NIC 中实现 PRISM 的可行性（§4.2）。我们评估了软件实现的性能和硬件实现的预期收益，以及智能 NIC 方法（§4.3）。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-implementation"},{"categories":["OS"],"collections":null,"content":"Software PRISM Implementation 受 Google 的 Snap [26]启发的软件实现 尽管软件实现使用服务器端 CPU，但在网络堆栈中执行的单边操作通过避免上下文切换和应用程序级线程调度的成本提供了性能优势。 Snap 已经支持（并且其应用程序使用）间接读取。 这篇论文看得有点难受，讲解不清楚，前后也不一致，稍微看了下性能也不是提高很多，但是对 RDMA 做分布式事务或存储应该是有蛮多贡献的 原则上，软件实现也可以用于智能 NIC；我们已经在 Mellanox BlueField 上进行了实验。然而，如下所述（§4.3），这种方法的性能低于软件实现，因此我们不主张除非减少 CPU 利用率是主要目标，否则不使用它。 为什么呢，一般来说 offload 到 NIC 上应该性能提升会更高吧 ","date":"2024-09-22","objectID":"/posts/paper-prism/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#software-prism-implementation"},{"categories":["OS"],"collections":null,"content":"Hardware NIC Feasibility PRISM 的设计旨在可实现于未来的 RDMA NIC 中。这部分分析必然是推测性的，因为支持 RDMA 的 NIC ASIC 是专有设计，我们没有能力进行扩展。然而，我们认为实现 PRISM 原语是可行的，因为它们重用了当今 NIC 上已经存在的底层机制。 讨论了 NIC 怎么做，但是看样子没实现，而且基本都是猜测可行性 ","date":"2024-09-22","objectID":"/posts/paper-prism/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#hardware-nic-feasibility"},{"categories":["OS"],"collections":null,"content":"Performance Analysis PRISM 的性能不仅取决于执行成本，还取决于网络延迟，因为其性能优势来自于消除与 RDMA 相比的网络往返。图 1 通过使用直接网络链接，考虑了 PRISM 的最坏情况。图 2 通过比较 PRISM 的间接读取与执行两次 RDMA 读取的成本，评估了网络延迟的影响。我们考虑了单机架部署（一个交换机）、具有三级交换机层次结构的集群，以及来自实际数据中心 [12] 的延迟数据，这些数据也反映了网络拥塞。在每种情况下，尽管使用 CPU 增加了额外成本，PRISM 的软件实现仍然优于 RDMA 基准。 减少 round trip 应该能大幅增加吞吐吧，应该测吞吐要比延迟来的更明显？ 但是论文的测试基本都是延迟，而且 RDMA 不知道为什么明显高很多 基本都是 10+us 而 ","date":"2024-09-22","objectID":"/posts/paper-prism/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#performance-analysis"},{"categories":["OS"],"collections":null,"content":"Applications Overview 为了展示 PRISM 的潜在优势，我们使用了三个代表性分布式应用程序的案例研究： PRISM-KV：一个实现 RDMA 读写操作的键值存储系统。（§6） PRISM-RS：一个实现 ABD [4] 仲裁复制协议的复制存储系统。（§7） PRISM-TX：一个使用 PRISM 原语实现基于时间戳的乐观并发控制协议的事务存储系统。（§8） 比较早的其实就是 FaRM, Pilaf 这几个 RDMA kv 分布式事务也比较有意思 ","date":"2024-09-22","objectID":"/posts/paper-prism/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#applications-overview"},{"categories":["OS"],"collections":null,"content":"PRISM-KV: Key-Value Storage 键值存储是一种广泛使用的基础设施，是利用 RDMA 加速的自然机会。我们首先考虑一个简单的远程、未复制的键值存储，类似于 memcached。它提供了一个 GET/PUT 接口，其中键和值可以是任意长度的字符串。 作为一个使用 RDMA 实现键值存储的挑战示例，再次考虑 Pilaf [31]。Pilaf 仅使用单边操作来实现 GET 操作；PUT 操作通过双边 RPC 机制发送并由服务器 CPU 执行。它存储一个固定大小的哈希表，该表仅包含一个有效位和一个指向键值存储的指针，该存储在单独的 extents 区域中。要执行 GET 操作，Pilaf 客户端计算键的哈希值，并执行单边 READ 到哈希表，然后执行第二个 READ 到它指向的数据。哈希冲突使用线性探测或布谷鸟哈希解决，这可能需要读取额外的指针。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-kv-key-value-storage"},{"categories":["OS"],"collections":null,"content":"PRISM-KV Design 单边操作实现了 GET 和 PUT 操作 它维护一个包含指向数据项指针的哈希表索引；这些数据项现在使用 PRISM 的 ALLOCATE 原语分配的缓冲区中。 感觉就是加入了更多缓冲区，然后来支持一些同步原语 要执行 PUT 操作，客户端首先如上所述确定正确的哈希表槽。然后，客户端使用 PRISM 原语链写入新值并更新哈希表槽。首先，客户端使用 ALLOCATE 将值写入新缓冲区，并将地址重定向到临时位置。然后，客户端在哈希表槽执行 CAS，如果自客户端确定正确槽以来未更改，则将旧地址替换为存储在临时位置的地址。2 为此最后操作设置了适当的间接位和条件标志。如果 CAS 失败，这表明并发客户端随后用较新的值覆盖了相同的键。如果成功，客户端异步通知服务器将旧版本的缓冲区返回到空闲列表。 PRISM-KV 确保在并发更新期间的正确性，因为对象被写入单独的缓冲区，并且这些缓冲区的指针被原子地安装到适当的哈希表槽中。与更新同时进行的读取也不会违反安全性，因为哈希表槽的间接读取保证读取格式良好的地址，并且地址适合缓存行。请注意，刚刚完成更新的客户端可能会尝试释放缓冲区，而间接读取尝试读取它。这对正确性不是问题，因为 PRISM 在将缓冲区添加回空闲列表之前等待并发 NIC 操作完成。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-kv-design"},{"categories":["OS"],"collections":null,"content":"Evaluation 这个结果就看上去好很多，吞吐量在写多的情况下还是很不错的，但是写只要稍微多一些就不行了和 pilaf 没太大差别，主要还是 cow + rcu 的组合不太能适应写多的场景？ 又提了硬件实现，又是预测。。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#evaluation"},{"categories":["OS"],"collections":null,"content":"PRISM-RS: Replicated Block Storage 块存储 + replica PRISM-RS 是我们的块存储设计，它保证了线性化，只要不超过 𝑓 个副本失败，就可以保持可用性，并且需要最少的副本 CPU 参与。它通过使用 PRISM 操作实现 ABD [4] 原子寄存器协议的变体来实现这一点。 ABD 协议 Amotz Bar-Noy 非常早提出的一个分布式同步协议？ 为什么要用这么早的，不能支持更现代的吗 ","date":"2024-09-22","objectID":"/posts/paper-prism/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-rs-replicated-block-storage"},{"categories":["OS"],"collections":null,"content":"PRISM-TX: Distributed Transactions 我们能否构建一个分布式事务协议，使用远程操作实现其执行和提交阶段？使用 PRISM 的新原语，特别是增强的 CAS 操作，我们在一个称为 PRISM-TX 的协议中实现了乐观并发控制检查。 乐观并发协议，能证明可序列化（但是没写） 使用了一些简单的测试，YCSB-T low contention ","date":"2024-09-22","objectID":"/posts/paper-prism/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#prism-tx-distributed-transactions"},{"categories":["OS"],"collections":null,"content":"Conclusion 看完也不知道看了什么 减少网络往返：PRISM 通过在单边操作中引入更复杂的操作，减少了额外的网络往返次数，从而提高了性能。 ","date":"2024-09-22","objectID":"/posts/paper-prism/:11:0","tags":["Paper Reading"],"title":"Paper Reading: Rethinking the RDMA Interface for Distributed Systems","uri":"/posts/paper-prism/#conclusion"},{"categories":["OS"],"collections":null,"content":"The eXpress Data Path: Fast Programmable Packet Processing in the Operating System Kernel ACM coNEXT 2018，在内核层面快速处理 packet，开源很完善，与 dpdk 之类的区别在于不是纯在用户态处理，也不是用户态协议栈。 而是利用 eBPF 在内核态执行。 明明不算很高强度看了两周论文，已经不适了。可能是不太懂 OS + Network 相关的吧，实在是太难了，很多概念都没接触过，只是入个门，比如这篇的 BPF 就没接触过。没写过没认真研究过的东西需要在几个小时弄明白太困难了。 XDP 是可以在 Linux 开启的，应该是应用比较广泛的，https://www.datadoghq.com/blog/xdp-intro/，分三种，一个是加载到网卡驱动，一个是加载到网卡，一个是 Linux 协议栈入口（性能-通用 tradeoff） AF_XDP 是 XDP 技术的一种应用场景，AF_XDP 是一种高性能 Linux socket。 主要几个缺点是，没有缓存队列，可能不太适合高延迟的设备，而且没有网络栈通用吧 https://github.com/facebookincubator/katran Meta 基于 BPF + XDP 实现的高性能 load balancer，尤其是在 XDP 驱动模式下表现非常快。 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:1:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#the-express-data-path-fast-programmable-packet-processing-in-the-operating-system-kernel"},{"categories":["OS"],"collections":null,"content":"ABSTRACT Programmable packet processing is increasingly implemented using kernel bypass techniques, where a userspace application takes complete control of the networking hardware to avoid expensive context switches between kernel and userspace application isolation and security mechanisms and well-tested configuration, deployment and management tools cease to function 绕过操作系统会带来什么，隔离、安全、配置？有具体例子吗，比如防火墙、ACL、还是权限、进程隔离？ we present the design of a novel approach to programmable packet processing, called the eXpress Data Path (XDP) XDP is part of the mainline Linux kernel and provides a fully integrated solution working in concert with the kernel’s networking stack. Applications are written in higher level languages such as C and compiled into custom byte code which the kernel statically analyses for safety, and translates into native instructions ","date":"2024-09-18","objectID":"/posts/paper-xdp/:2:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#abstract"},{"categories":["OS"],"collections":null,"content":"INTRODUCTION DPDK bypass the operating sys- tem completely, instead passing control of the network hardware directly to the network application and dedicating one, or several, CPU cores exclusively to packet processing. 内核旁路方法可以显著提高性能，但是有一个缺点，那就是更难与现有系统集成，应用程序必须重新实现操作系统网络栈提供的功能，比如路由表和更高级别的协议。 在最坏的情况下，这将导致一种场景，即数据包处理应用程序在一个完全独立的环境中运行，由于需要直接的硬件访问，操作系统提供的熟悉的工具和部署机制无法使用。这会增加系统的复杂性，模糊操作系统内核强制执行的安全边界。 基础设施正在转向基于容器的工作负载和编排系统(如 Docker 或 Kubernetes) ，在这些系统中，内核在资源抽象和隔离方面发挥着主导作用 容器化和虚拟化的区别是什么。 XDP 以运行 eBPF 代码的虚拟机的形式定义一个有限的执行环境，eBPF 是原始 BSD 包过滤器 (BPF) 字节码格式的扩展版本。这个环境在内核本身接触数据包数据之前直接在内核上下文中执行定制程序，这使得在从硬件接收数据包之后尽可能早地进行自定义处理(包括重定向)。内核通过在加载时静态验证自定义程序来确保它们的安全性; 并且程序被动态地编译成本地机器指令以确保高性能 虽然这并不完全符合在相同硬件上基于 DPDK 的应用程序的最高可实现性能，但我们认为 XDP 系统通过提供几个比 DPDK 和其他内核旁路解决方案更引人注目的优势来弥补这一点 性能还是不如 DPDK 与常规网络堆栈协同集成，在内核中保留对硬件的完全控制。这保留了内核的安全边界，并且不需要对网络配置和管理工具进行任何更改。此外，任何具有 Linux 驱动程序的网络适配器都可以被 XDP 支持；不需要特殊的硬件功能，现有的驱动程序只需要进行修改以添加 XDP 执行钩子。 使得可以选择性地利用内核网络堆栈功能，如路由表和 TCP 堆栈，保持相同的配置接口，同时加速关键性能路径。 保证 eBPF 指令集和与其一起公开的编程接口（API）的稳定性。 在与基于正常套接层的工作负载交互时，不需要将数据包从用户空间重新注入内核空间，从而避免了昂贵的操作。 对主机上运行的应用程序是透明的，使得新的部署场景成为可能，例如在服务器上进行内联保护以抵御拒绝服务攻击。 可以动态重新编程而不会中断任何服务，这意味着可以在不中断网络流量的情况下动态添加或完全移除功能，并且处理可以根据系统其他部分的状况动态反应。 不需要将完整的 CPU 核心专用于数据包处理，这意味着较低的流量水平直接转化为较低的 CPU 使用率。这对效率和节能有重要影响。 不需要特殊 network adapter 和 driver 是很不错的一个点 re-injection from user space into kernel space when interacting with workloads based on the normal socket layer 这是在哪发生的？是 dpdk 吗 dedicating full CPU cores 也是一个很奇怪的点，low traffic 会占用 dpdk 一个核 100% 吗？ ","date":"2024-09-18","objectID":"/posts/paper-xdp/:3:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#introduction"},{"categories":["OS"],"collections":null,"content":"RELATED WORK Examples of such applica- tions include those performing single functions, such as switch- ing [47], routing [19], named-based forwarding [28], classifica- tion [48], caching [33] or traffic generation 为了在 Common Off The Shelf 通用现货(COTS)硬件上实现高性能的数据包处理，有必要消除网络接口卡 (NIC) 和执行数据包处理的程序之间的任何瓶颈。由于性能瓶颈的主要来源之一是操作系统内核和运行在其上的用户空间应用程序之间的 interface (因为系统调用的高开销和底层功能丰富的通用堆栈的复杂性) ，低级数据包处理框架必须以这样或那样的方式管理这种开销。支持上面提到的应用程序的现有框架采用多种方法来确保高性能; XDP 基于其中几种方法的技术。在接下来的文章中，我们将简要概述 XDP 与现有最常用框架之间的异同。 DataPlanDevelopmentKit (DPDK)[16]可能是用于高速数据包处理的最广泛使用的框架。它最初是一个 Intel 专用的硬件支持包，但是在 Linux 基金会的管理下已经得到了广泛的应用。DPDK 是一种所谓的内核旁路框架，它将网络硬件的控制从内核移到网络应用程序中，完全消除了内核-用户空间边界的开销。 however, as mentioned in the introduction, it has significant management, maintenance and security drawbacks. 还是不懂有什么安全和管理问题，能够具体一点吗，intro 也只是说需要重新实现网络栈、难以集成？ XDP 采用了一种与绕过内核相反的方法: 不是将网络硬件的控制移出内核，而是将对性能敏感的数据包处理操作移入内核，并在操作系统网络堆栈开始处理之前执行。这保留了去除网络硬件和包处理代码之间的内核-用户空间边界的优点，同时保持了内核对硬件的控制，从而保留了管理接口和操作系统提供的安全保证。实现这一点的关键创新是使用一个虚拟执行环境来验证加载的程序不会损害或使内核崩溃 提前处理？还是使用了虚拟环境 virtual execution environment 在引入 XDP 之前，将数据包处理功能作为内核模块实现是一种高成本的方法，因为错误可能使整个系统崩溃，而且内部内核 API 经常发生变化 包处理怎么引起系统崩溃？ XDP 通过提供一个安全的执行环境，并得到内核社区的支持，极大地降低了将处理迁移到内核中的应用程序的成本，从而提供了与内核向用户空间公开的其他接口相同的 API 稳定性保证。此外，XDP 程序可以完全绕过网络堆栈，这比需要挂接到现有堆栈的传统内核模块提供了更高的性能。 bypass the networking stack 是怎么做，有类似的吗，是零拷贝吗？AF_XDP？不使用内核的网络栈处理数据包，自己处理？ 虽然 XDP 允许数据包处理进入操作系统以获得最大的性能，但它也允许加载到内核的程序有选择地将数据包重定向到一种特殊的用户空间套接字类型，这种套接字类型绕过了正常的网络堆栈，甚至可以以零拷贝模式进行操作以进一步降低开销。 普通 linux NIC 接收到数据包，中断，缓冲区-内核空间，内核空间走网络栈（链路层、网络层、协议层）处理（校验、路由等等）、内核缓冲-用户空间缓冲、socket 接口。 XDP NIC 接收到数据包，中断，缓冲区-内核空间复制，XDP hook 处理包， programmable hardware achieve high-performance packet processing, NetFPGA, In a sense, XDP can be thought of as a “software offload”, where performance-sensitive processing is offloaded to increase performance, while applications otherwise interact with the regular networking stack XDP 怎么知道是 performance-sensitive，还是说全部这么处理？额外处理不需要时间吗 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:4:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#related-work"},{"categories":["OS"],"collections":null,"content":"THE DESIGN OF XDP This deep integration with the kernel obviously imposes some design constraints 什么限制？ 在数据包到达时，在接触数据包数据之前，设备驱动程序在主 XDP 挂钩中执行 eBPF 程序。这个程序可以选择丢弃数据包; 将它们发送回原来接收到的接口; 将它们重定向到另一个接口(包括虚拟机的 vNIC) ，或者通过特殊的 AF _ XDP 套接字发送到用户空间; 或者允许它们进入常规的网络堆栈， 问题是，hook 的耗时会不会影响正常的包？ The XDP driver hook is the main entry point for an XDP program, and is executed when a packet is received from the hardware. The eBPF virtual machine executes the byte code of the XDP program, and just-in-time-compiles it for increased performance. BPF maps are key/value stores that serve as the primary communication channel to the rest of the system. The eBPF verifier statically verifies programs before they are loaded to make sure they do not crash or corrupt the running kernel. 当数据包到达时，程序首先解析数据包头以提取它将对之作出反应的信息。然后，它从多个源之一读取或更新元数据。最后，可以重写一个数据包，并确定对该数据包的最终判决。该程序可以在数据包解析、元数据查找和重写之间进行交替，所有这些都是可选的。 map 是干嘛的 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:5:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#the-design-of-xdp"},{"categories":["OS"],"collections":null,"content":"The XDP Driver Hook XDP programs run in the Extended BPF (eBPF) virtual machine. eBPF is an evolution of the original BSD packet filter (BPF) [37] which has seen extensive use in various packet filtering applications over the last decades. BPF uses a register-based virtual machine to describe filtering actions register-based virtual machine 会不会出现问题，比如 容量不够什么的 程序通常首先解析数据包数据，并通过尾调用将控制权传递给不同的 XDP 程序，从而将处理分成逻辑子单元（例如，基于 IP 头版本）。 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:5:1","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#the-xdp-driver-hook"},{"categories":["OS"],"collections":null,"content":"PERFORMANCE EVALUATION 现有系统中有许多用于高性能数据包处理的解决方案，并且在本文范围内对所有这些系统进行基准测试是不现实的。相反，我们注意到 DPDK 是现有解决方案中性能最高的[18]，并将其作为高速软件数据包处理当前技术水平的基准进行比较（使用 DPDK 18.05 版本附带的 testpmd 示例应用程序）。我们专注于原始数据包处理性能，使用合成基准测试，并将其与 Linux 内核网络堆栈的性能进行比较，以展示 XDP 在同一系统中提供的性能改进。在下一节中，我们将通过一些在 XDP 之上实现的实际应用示例来补充这些原始性能基准测试，以展示其在编程模型中的可行性。 Packet drop performance 数据包丢弃性能。为了展示最大数据包处理性能，我们测量丢弃传入数据包的最简单操作的性能。这实际上测量了整个系统的开销，并作为实际数据包处理应用程序预期性能的上限。 CPU usage CPU 使用率。如引言中所述，XDP 的优点之一是它根据数据包负载扩展 CPU 使用率，而不是专门为数据包处理分配 CPU 核心。我们通过测量 CPU 使用率如何随提供的网络负载扩展来量化这一点。 Packet forwarding performance 数据包转发性能。一个不能转发数据包的数据包处理系统实用性有限。由于转发引入了与简单处理情况相比的额外复杂性（例如，与多个网络适配器交互，重写链路层头等），因此单独评估转发性能是有用的。我们在转发评估中包括吞吐量和延迟。 奇怪的指标， 我们已经验证，在全尺寸（1500 字节）数据包的情况下，我们的系统可以在半空闲的单个核心上以线速（100 Gbps）处理数据包。这清楚地表明，挑战在于每秒处理大量数据包，正如其他人也指出的那样[46]。因此，我们使用最小尺寸（64 字节）数据包进行所有测试，并测量系统可以处理的最大数据包数每秒。为了测量性能如何随 CPU 核心数量扩展，我们使用越来越多的专门用于数据包处理的核心重复测试。对于 XDP 和 Linux 网络堆栈（它们没有提供明确的方式来专门为数据包处理分配核心），我们通过配置硬件接收端扩展（RSS）功能，将流量引导到每个测试所需的多个核心来实现这一点。 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:6:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#performance-evaluation"},{"categories":["OS"],"collections":null,"content":"Packet Drop Performance ","date":"2024-09-18","objectID":"/posts/paper-xdp/:6:1","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#packet-drop-performance"},{"categories":["OS"],"collections":null,"content":"CPU Usage ","date":"2024-09-18","objectID":"/posts/paper-xdp/:6:2","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#cpu-usage"},{"categories":["OS"],"collections":null,"content":"Packet Forwarding Performance 适用案例： 软件路由（XDP routing） Linux 内核实现了一个功能完全的路由表，生态系统功能丰富，结合 XDP 包处理框架实现了一个完美的路由功能。其性能与常规的 Linux 内核网络栈相比提升了 2.5 - 3 倍左右。 ACL/DDoS 防御 XDP 可以直接在应用服务器上部署包过滤程序来防御此类攻击，无须修改应用代码。如果应用部署在虚拟机里，XDP 程序还可以部署在宿主机上，保护机器上所有的虚拟机。其性能单核可以轻松处理 10Gbps 的最小包 Dos 流量。这种 DDOS 防御的部署更加灵活。 相比 iptables 相对较晚的 hook 点，XDP 的丢包速率要比 iptables 高 4 倍左右。 负载均衡（load balancing） 其原理是通过对包头进行哈希，以此选择目标应用服务器，然后将数据包进行封装，发送给应用服务器，应用解封，处理请求，会包给客户端。在次过程中，XDP 服务哈希，封包发送。通过 bpf map 进行配置，其性能比 Linux 内核 IPVS 高 4 倍左右。 XDP 允许使用 BPF（Berkeley Packet Filter）程序进行数据包处理，这些程序可以动态加载和更新，提供了极大的灵活性。 适用性强。高于 4.8 版本的内核和绝大多数高速网卡都是支持 XDP 的，无需专有硬件的支持。 2021 年，Yoann Ghigoff 等人更是基于 eBPF 和 XDP、TC 在内核中实现了一层 Memcached 的缓存，达到了比 DPDK 内核旁路方案还要高的性能。智能网卡也开始对 eBPF 卸载进行了支持，将包处理进一步从网卡驱动层卸载到了网卡，释放了更多的主机 CPU 资源，实现更高的性能。我们常用的虚拟交换机 OVS 的团队也在 2.12.0 版本就开始对 AF_XDP 进行探索，在 2021 年 SIGCOMM 会议上，发表了这些年他们对于数据面的探索，将 AF_XDP 选型用于其数据面，解决了很多 DPDK 解决不了的问题。其他的应用场景如负载均衡、流采样和监控……更多的可能正在被学术和工业界探索。 https://cloud.tencent.com/developer/article/1909298 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:6:3","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#packet-forwarding-performance"},{"categories":["OS"],"collections":null,"content":"Conclusion XDP 论文看起来只是 CPU 消耗好很多，但他的意义在于，真的合入了内核，开源，而且属于是里程碑真的引起了后续的多种技术发展，AF_XDP 等等 ","date":"2024-09-18","objectID":"/posts/paper-xdp/:7:0","tags":["Paper Reading"],"title":"Paper Reading: The eXpress Data Path","uri":"/posts/paper-xdp/#conclusion"},{"categories":["OS"],"collections":null,"content":"Arrakis: The Operating System is the Control Plane 和 IX 同样是 OSDI 14 的文章，Arrakis 应该也是 Dune 沙丘里的名字吧，但好像不是同一批人 Adam Belay Standford 的人做的，应该很类似。 同样是用虚拟化技术， ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#arrakis-the-operating-system-is-the-control-plane"},{"categories":["OS"],"collections":null,"content":"Abstract 应用程序可以直接访问虚拟化的 I/O 设备，允许大多数 I/O 操作完全跳过内核，而内核被重新设计以提供网络和磁盘保护， ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 高速以太网和低延迟持久内存的结合显著提高了 I/O 密集型软件的效率标准 许多服务器的大部分时间都用于执行操作系统代码：传递中断、demultiplexing 多路分解、和复制网络数据包，以及维护文件系统元数据。服务器应用程序通常执行非常简单的功能，如键值表的查找和存储，但在每次客户端请求时都会多次穿越操作系统内核。 这些趋势导致了一系列针对各种用例优化内核代码路径的研究：消除内核中的冗余副本[45]，减少大量连接的开销[27]，协议专业化[44]，资源容器[8, 39]，磁盘和网络缓冲区之间的直接传输[45]，中断转向[46]，系统调用批处理[49]，硬件 TCP 加速等。其中许多技术已被主流商业操作系统采用，但这是一场失败的战斗：我们展示了 Linux 网络和文件系统堆栈的延迟和吞吐量远不如硬件原始性能。 I/O 密集型，比如网络服务器？ 二十年前，研究人员提出通过将网络硬件直接映射到用户空间来简化并行计算中的数据包处理，以实现工作站网络的并行计算[19, 22, 54]。尽管在当时商业上并不成功，但虚拟化市场的兴起促使硬件供应商重新考虑这一想法[6, 38, 48]，并将其扩展到磁盘[52, 53]。 磁盘是什么做法？ 本文探讨了在几乎所有 I/O 操作中将内核从数据路径中移除的操作系统含义。我们认为，这样做必须为应用程序提供与传统设计相同的安全模型；通过扩展可信计算基础以包括应用程序代码，例如允许应用程序未经滤直接访问网络/磁盘，很容易获得良好的性能。我们证明了操作系统的保护与高性能并不矛盾。在我们的原型实现中，对 Redis 持久化 NoSQL 存储的客户端请求在读取延迟方面提高了 2 倍，写入延迟提高了 5 倍，写入吞吐量提高了 9 倍，相比 Linux。 和 IX 或者说内核态网络栈一样的思路？但是优化了 Redis 看着很有意思，一个内存 NoSQL 我们做出了三个具体的贡献： • 我们提出了一个架构，用于划分设备硬件、内核和运行时在非特权进程直接进行网络和磁盘 I/O 时的分工，并展示了如何高效地模拟我们的模型，以适应不完全支持虚拟化的 I/O 设备（§3）。 • 我们将我们的模型实现为一个开源 Barrelfish 操作系统的修改集，运行在商业可用的多核计算机和 I/O 设备硬件上（§3.8）。 • 我们使用我们的原型来量化用户级 I/O 对几种广泛使用的网络服务的潜在好处，包括分布式对象缓存、Redis、IP 层中间盒和 HTTP 负载均衡器（§4）。我们展示了在许多情况下，相对于 Linux，在延迟和可扩展性方面可以获得显著的提升，而无需修改应用程序编程接口；通过改变 POSIX API 可以获得额外的收益（§4.3）。 基于 Barrelfish 做的，也就是 exokernel，是 Multikernel，利用信息传递，也是区分了 user space 和 kernel space，每个核心有自己的 kernel 和 IX 区别是不是基于 Dune，不过同样的都是做了分布式对象缓存、Redis、IP 层中间盒和 HTTP 负载均衡器，延迟优化的同时不需要修改程序接口？为什么？ ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#introduction"},{"categories":["OS"],"collections":null,"content":"Background 我们首先详细分析了当前网络和存储操作中的操作系统和应用程序开销，随后讨论了支持用户级网络和 I/O 虚拟化的当前硬件技术。 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#background"},{"categories":["OS"],"collections":null,"content":"Networking Stack Overheads Linux 进程实现的 UDP 回显服务器 • 网络栈成本：硬件、IP 和 UDP 层的数据包处理。 • 调度器开销：唤醒进程（如果需要），选择它运行，并切换到它。 • 内核穿越：从内核到用户空间，再返回。 • 数据包数据的复制：在接收时从内核复制到用户缓冲区，在发送时返回。 在 Linux 中，处理每个数据包总共花费 3.36 微秒（见表 1），其中近 70%的时间花在网络栈中。这项工作主要是软件多路分解、安全检查以及由于各层间接导致的开销。内核必须验证传入数据包的头部，并在应用程序发送数据包时对提供的参数进行安全检查。栈还在层边界执行检查。 调度器开销在很大程度上取决于接收进程当前是否正在运行。如果是，只有 5%的处理时间花在调度器上；如果不是，从空闲进程上下文切换到服务器进程会增加额外的 2.2 微秒，网络栈的其他部分还会进一步减慢 0.6 微秒。 多核系统上的缓存和锁争用问题增加了进一步的开销，并且由于网络卡可以将传入消息传递到不同的队列，导致它们由不同的 CPU 核心处理，这加剧了问题——这些核心可能与用户级进程调度的核心不同，如图 1 所示。高级硬件支持，如加速接收流转向[4]，旨在减轻这种成本，但这些解决方案本身会带来非同小可的设置成本[46]。 多核心是怎么做的，这个是第一次接触的场景，如何保持一致性？ 通过利用硬件支持将内核从数据平面中移除，Arrakis 可以完全消除某些类别的开销，并最小化其他开销的影响。表 1 还显示了 Arrakis 两种变体的相应开销。Arrakis 完全消除了调度和内核穿越开销，因为数据包直接传递到用户空间。当然，网络栈处理仍然是必需的，但它大大简化了：不再需要为不同应用程序多路分解数据包，用户级网络栈也不需要像内核实现那样广泛地验证用户提供的参数。因为每个应用程序都有单独的网络栈，并且数据包传递到应用程序运行的核心，锁争用和缓存效应减少了。 差不多的思路 在 Arrakis 网络栈中，将数据包数据复制到用户提供的缓冲区并从中复制的时间主导了处理成本，这是 POSIX 接口（Arrakis/P）与 NIC 数据包队列之间不匹配的结果。到达的数据首先由网络硬件放入网络缓冲区，然后复制到 POSIX 读取调用指定的位置。要传输的数据被移动到可以放入网络硬件队列的缓冲区中；然后 POSIX 写入可以返回，允许在数据发送之前重用用户内存。尽管研究人员已经研究了从内核网络栈中消除这种复制的方法[45]，如表 1 所示，内核驻留网络栈的大部分开销在其他地方。一旦消除了穿越内核的开销，就有机会重新思考 POSIX API 以实现更高效的网络。除了 POSIX 兼容接口外，Arrakis 还提供了一个本地接口（Arrakis/N），支持真正的零拷贝 I/O。 同样使用了零拷贝技术，arrakis 自己提供了一个零拷贝，是怎么做的？ ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#networking-stack-overheads"},{"categories":["OS"],"collections":null,"content":"Storage Stack Overheads fsync 测试，实际上就是测 IO 延迟写 传统的 UNIX 实现的内核中都设置有缓冲区或者页面高速缓存，大多数磁盘 IO 都是通过缓冲写的。 当你想将数据 write 进文件时，内核通常会将该数据复制到其中一个缓冲区中，如果该缓冲没被写满的话，内核就不会把它放入到输出队列中。 当这个缓冲区被写满或者内核想重用这个缓冲区时，才会将其排到输出队列中。等它到达等待队列首部时才会进行实际的 IO 操作。 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#storage-stack-overheads"},{"categories":["OS"],"collections":null,"content":"Application Overheads NoSQL 测试 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#application-overheads"},{"categories":["OS"],"collections":null,"content":"Hardware I/O Virtualization Single-Root I/O Virtualization (SR-IOV) 并通过 IOMMU（例如 Intel 的 VT-d [34]）进行访问保护 在 Arrakis 中，我们使用 SR-IOV、IOMMU 和支持适配器来提供对 I/O 设备的直接应用程序级访问。这是 20 年前使用 U-Net [54]实现的一个想法的现代实现，但已推广到闪存存储和以太网网络适配器。 同样使用虚拟化技术，让应用可以直接访问 IO 设备 虽然 RDMA 为并行应用程序提供了用户级网络的性能优势，但将其模型应用于更广泛的客户端-服务器应用程序[21]具有挑战性。最重要的是，RDMA 是点对点的。每个参与者接收一个认证器，授予其远程读/写特定内存区域的权限。由于客户端-服务器计算中的客户端不是相互信任的，硬件需要为每个活动连接保留单独的内存区域。因此，我们在这里不考虑 RDMA 操作。 不考虑 RDMA 为什么，点对点，不信任？ ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:4:4","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#hardware-io-virtualization"},{"categories":["OS"],"collections":null,"content":"Design and Implementation Minimize kernel involvement for data-plane operations: Arrakis 旨在限制或消除内核对大多数 I/O 操作的干预。I/O 请求在应用程序的地址空间之间路由，无需内核参与，同时不牺牲安全性和隔离性。 基本都是消除内核态的 IO 干预 Transparency to the application programmer: Appropriate OS/hardware abstractions: 其他感觉看看就行，都是虚拟化带来的好处 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#design-and-implementation"},{"categories":["OS"],"collections":null,"content":"Architecture Overview ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#architecture-overview"},{"categories":["OS"],"collections":null,"content":"Hardware Model 我们工作的关键要素是开发一个与硬件无关的虚拟化 I/O 层——即提供“理想”硬件特性集的设备模型。这个设备模型捕捉了在硬件中实现传统内核数据平面操作所需的功能。我们的模型类似于一些硬件 I/O 适配器已经提供的内容；我们希望它能为支持安全的用户级网络和存储提供指导 virtual network interface cards VIC 是怎么来的 Queues: Transmit and receive filters: Virtual storage areas: Bandwidth allocators: ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#hardware-model"},{"categories":["OS"],"collections":null,"content":"VSIC Emulation 看不懂 但是比 IX 等更加细节，应该是能复现的 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#vsic-emulation"},{"categories":["OS"],"collections":null,"content":"Control Plane Interface 应用程序与 Arrakis 控制平面之间的接口用于从系统请求资源，并将 I/O 流引导到用户程序。该接口提供的关键抽象是 VICs、门铃、过滤器、VSAs 和速率说明符。 这里的实现很有意思，可以对比 IX 看看，IX 用了 flow consistent 哈希， 最大区别应该是 arrakis 支持 iommu，ix 没有？ ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#control-plane-interface"},{"categories":["OS"],"collections":null,"content":"File Name Lookup VFS + POSIX arrakis 支持文件存储应该很有意思，IX 支持吗？还是纯内存的，应该也是支持的吧，论文提到了 SDD 和 flash。 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:5","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#file-name-lookup"},{"categories":["OS"],"collections":null,"content":"Network Data Plane Interface 在 Arrakis 中，应用程序通过直接与硬件通信来发送和接收网络数据包。因此，数据平面接口在应用程序库中实现，允许它与应用程序共同设计[43]。Arrakis 库为应用程序提供了两个接口。我们描述了本地的 Arrakis 接口，它稍微偏离了 POSIX 标准以支持真正的零拷贝 I/O；Arrakis 还提供了一个支持未修改应用程序的 POSIX 兼容层。 怎么做到真正的零拷贝？ 应用程序在队列上发送和接收数据包，这些队列之前已经分配了过滤器，如上所述。虽然过滤器可以包括 IP、TCP 和 UDP 字段谓词，但 Arrakis 并不要求硬件执行协议处理，只进行多路复用。在我们的实现中，Arrakis 在数据平面接口之上提供了一个用户空间网络栈。该栈旨在最大化延迟和吞吐量。我们保持了数据包传输和接收的三个方面的清晰分离。 首先，数据包使用传统的 DMA 技术通过数据包缓冲区描述符环在网络和主内存之间异步传输。其次，应用程序通过将缓冲区链入硬件描述符环来将传输数据包的所有权转移给网络硬件，并通过反向过程获取接收到的数据包。这是通过两个 VNIC 驱动程序函数完成的。send_packet(queue, packet_array) 在队列上发送数据包；数据包由散布/聚集数组 packet_array 指定，并且必须符合已与队列关联的过滤器。receive_packet(queue) = packet 从队列接收数据包并返回指向它的指针。这两个操作都是异步的。packet_done(packet) 将接收到的数据包的所有权返回给 VNIC。 为了获得最佳性能，Arrakis 栈将通过编译器生成的、针对 NIC 描述符格式优化的代码直接与硬件队列交互，而不是通过这些调用。然而，我们在本文中报告的实现使用函数调用驱动程序。第三，我们使用与队列关联的门铃处理异步事件通知。当应用程序运行时，门铃通过硬件虚拟化中断直接从硬件传递给用户程序，当应用程序不运行时，通过控制平面调用调度器。在后一种情况下，较高的延迟是可以容忍的。门铃通过常规事件传递机制（例如文件描述符事件）暴露给 Arrakis 程序，并完全集成到现有的 I/O 多路复用接口（例如 select）中。它们对于通知应用程序接收队列中数据包的通用可用性以及作为高优先级队列中数据包接收和 I/O 完成的轻量级通知机制都很有用。 这种设计产生了一个协议栈，通过使用描述符环作为缓冲区，尽可能地将硬件与软件解耦，在高速数据包率下最大化吞吐量并最小化开销，从而实现低延迟。在这个本地接口之上，Arrakis 提供了 POSIX 兼容的套接字。这个兼容层允许 Arrakis 支持未修改的 Linux 应用程序。然而，我们表明，通过使用异步本地接口可以实现性能提升 这里的协议栈 和 IX, mTCP 等有什么区别。 而且 arrakis 还有缓冲区？为什么不像 IX 一样直接去掉用共享内存做零拷贝呢。 ","date":"2024-09-16","objectID":"/posts/paper-arrakis/:5:6","tags":["Paper Reading"],"title":"Paper Reading: Arrakis","uri":"/posts/paper-arrakis/#network-data-plane-interface"},{"categories":["OS"],"collections":null,"content":"IX: A Protected Dataplane Operating System for High Throughput and Low Latency OSDI 2014, Dune 的衍生，同样利用硬件虚拟化 VT-x 技术。IX 最重要的是 Data Plane 和 Control Plane 分离了。 不得不说这类型的论文阅读起来太费劲了。 ","date":"2024-09-15","objectID":"/posts/ix-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#ix-a-protected-dataplane-operating-system-for-high-throughput-and-low-latency"},{"categories":["OS"],"collections":null,"content":"Abstract 传统观点认为，激进的网络需求，比如小消息的高包速率和微秒级的尾部延迟，最好在内核之外的用户级网络堆栈中进行处理。我们介绍了 IX，一个数据平面操作系统，它提供了高 I/O 性能，同时保持了现有核心提供的强保护的关键优势。IX 使用硬件虚拟化将内核(控制平面)的管理和调度功能与网络处理(数据平面)分离开来。数据平面体系结构建立在原生的零拷贝 API 之上，通过将硬件线程和网络队列专用于数据平面实例，处理有界批量的数据包以完成，并通过消除一致性流量和多核同步来优化带宽和延迟。我们展示了 IX 在吞吐量和端到端延迟方面都明显优于 Linux 和最先进的用户空间网络堆栈。此外，IX 提高了一个广泛部署的键值存储器的吞吐量达到 3.6 倍，并减少了超过 2 倍的尾延迟。 IX，利用 VTX 将控制平面（内核）和数据平面（网络处理）区分开，后者利用零拷贝处理？ 延迟和吞吐都有提升 ","date":"2024-09-15","objectID":"/posts/ix-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction need for networking stacks that provide more than high streaming performance. The new requirements include high packet rates for short messages, microsecond-level responses to remote requests with tight tail latency guarantees, and support for high connection counts and churn 目前对高性能网络的需求 some systems bypass the kernel and implement the networking stack in user-space . While kernel bypass eliminates context switch overheads, on its own it does not eliminate the difficult tradeoffs between high packet rates and low latency (see §5.2). Moreover, user-level networking suffers from lack of protection. Application bugs and crashes can corrupt the networking stack and impact other workloads. Other systems go a step further by also replacing TCP/IP with RDMA in order to offload network processing to specialized adapters [17, 31, 44, 47]. However, such adapters must be present at both ends of the connection and can only be used within the datacenter. bypass 比如 dpdk, 在用户态处理了，消除了上下文切换开销 并没有消除在 高数据包速率 和 低延迟之间 的艰难权衡，这种 tradeoff 怎么处理？高速率和低延迟看着并不是矛盾的 另一种解决就是 RDMA 设备 We propose IX, an operating system designed to break the 4-way tradeoff between high throughput, low latency, strong protection, and resource efficiency. Its architecture builds upon the lessons from high performance middleboxes, such as firewalls, load-balancers, and software routers [16, 34]. IX separates the control plane, which is responsible for system configuration and coarse-grain resource provisioning between applications, from the dataplanes, which run the networking stack and application logic. IX leverages Dune and virtualization hardware to run the dataplane kernel and the application at distinct protection levels and to isolate the control plane from the dataplane [7]. In our implementation, the control plane is the full Linux kernel and the dataplanes run as protected, library-based operating systems on dedicated hardware threads IX 是 OS，使用了 Dune 和 VT-x 虚拟化技术 打破 4way tradeoff 听上去也太顶级了。 控制平面用于系统配置和资源供应 数据平面用于网络协议栈 IX 实现的控制平面是完整的 Linux 内核，数据平面作为受保护的、基于库的操作系统在专用的硬件线程上运行 The IX dataplane allows for networking stacks that optimize for both bandwidth and latency. It is designed around a native, zero-copy API that supports processing of bounded batches of packets to completion 一些细节，数据平面 optimizes for multi-core，应该是解决了 Dune 存在的一些问题，比如抛弃了 POSIX，使用别的比如事件驱动。 IX 对比 Linux 和 mTCP（用户级 TCP/IP），IX 的吞吐量分别比 Linux 和 mTCP 高出 10 倍和 1.9 倍 听上去和用户级 tcp/ip 可能差距并不大，不知道有没有用户级的 kcp/quic 能否比较一下 两个 IX 服务器之间的空载单向延迟为 5.7 微秒，比标准 Linux 内核之间的延迟好 4 倍， ","date":"2024-09-15","objectID":"/posts/ix-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#introduction"},{"categories":["OS"],"collections":null,"content":"Background and Motivation ","date":"2024-09-15","objectID":"/posts/ix-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#background-and-motivation"},{"categories":["OS"],"collections":null,"content":"Challenges for Datacenter Applications Microsecond tail latency: 将一些服务请求的延迟降低到几十 μs 是至关重要的，我们还必须考虑跨数据中心的 RPC 请求的延迟分布的长尾。虽然尾部公差实际上是一个端到端的挑战，但系统软件栈在加剧问题方面起着重要作用[36]。总的来说，理想情况下，每个服务节点都必须严格限制第 99 百分位的请求延迟 tail-tolerance is actually an end-to-end challenge 应该在分布式很重要 High packet rates: 如果系统软件不能处理大量的连接计数，对应用程序可能会产生重大影响 TCP 为什么无法处理大量连接？是因为握手之类的？ Protection: isolation between applications Resource efficiency: 每个服务节点将使用最少的资源(核心、内存或 IOPS)来满足任何时候的数据包速率和尾延迟需求 ","date":"2024-09-15","objectID":"/posts/ix-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#challenges-for-datacenter-applications"},{"categories":["OS"],"collections":null,"content":"The Hardware – OS Mismatch 硬件发展 商用操作系统 这些文章大部分的 motivation 都来自于硬件发展快速，比如 100Gbe 网卡，商用系统不够专用等等 ","date":"2024-09-15","objectID":"/posts/ix-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#the-hardware--os-mismatch"},{"categories":["OS"],"collections":null,"content":"Alternative Approaches User-space networking stacks: OpenOnload [59] ，mTCP [29]和 Sandstorm [40]等系统在用户空间中运行整个网络栈，以消除内核交叉开销并优化数据包处理，而不会引起内核修改的复杂性 更重要的是，当网络被提升到用户空间，而应用程序错误可能破坏网络堆栈时，安全性权衡就会出现 Alternatives to TCP: RDMA 可以减少延迟，但是需要在连接的两端都有专门的适配器。使用普通的以太网，Facebook 的 memcached 部署使用 UDP 来避免连接可伸缩性限制[46]。即使 UDP 在内核中运行，可靠的通信和拥塞管理仍然委托给应用程序 比如 KCP/QUIC 呢 Alternatives to POSIX API: MegaPipe replaces the POSIX API with lightweight sockets implemented with in-memory command rings [24]. This reduces some software overheads and increases packet rates, but retains all other challenges of using an existing, kernel-based networking stack. 都是自定义了 OS enhancements: Tuning kernel-based stacks provides incremental benefits with superior ease of deployment. Linux SOREUSEPORT allows multi-threaded applications to accept incoming connections in parallel. Affinityaccept reduces overheads by ensuring all processing for a network flow is affinitized to the same core [49]. Recent Linux Kernels support a busy polling driver mode that trades increased CPU utilization for reduced latency [27], but it is not yet compatible with epoll. When microsecond latencies are irrelevant, properly tuned stacks can maintain millions of open connections 不兼容 epoll 是什么意思，busy polling 是增加 CPU 利用率一直轮询，减少延迟。轮询和 epoll 事件通知有区别吧。 ","date":"2024-09-15","objectID":"/posts/ix-paper/:4:3","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#alternative-approaches"},{"categories":["OS"],"collections":null,"content":"IX Design Approach microsecond latency \u0026amp;\u0026amp; high packet rates These requirements have been addressed in the design of middleboxes such as firewalls, load-balancers by integrating the networking stack and the application into a single dataplane middleboxes 集成了网络栈和应用程序到一个单一的数据平面， Separation and protection of control and data plane: 相比之下，商用操作系统将协议处理与应用程序本身解耦，以提供调度和流量控制的灵活性。例如，内核依赖设备和软中断来从应用程序切换到协议处理。类似地，内核的网络栈会在应用程序不消耗数据的情况下生成 TCP ACK 并滑动其接收窗口，直到一定程度。 IX 扩展了数据平面架构，以支持不受信任的通用应用程序 控制和数据平面的分离也使我们能够考虑完全不同的 I/O API，同时允许其他操作系统功能（如文件系统支持）传递到控制平面以实现兼容性。类似于 Exokernel，每个数据平面在一个单一地址空间中运行一个单一应用程序。然而，我们使用现代虚拟化硬件在控制平面、数据平面和不受信任的用户代码之间提供三向隔离。数据平面在虚拟化系统中具有类似客户操作系统的功能。它们管理自己的地址转换，基于控制平面提供的地址空间，并且可以通过使用特权环来保护网络栈免受不受信任的应用程序逻辑的影响。此外，数据平面通过内存映射 I/O 直接访问 NIC 队列 这个 compability 是怎么做的，不同 IO API 怎么兼容。 Run to completion with adaptive batching: The IX dataplane also makes extensive use of batching 有界自适应批处理和运行到完成的结合意味着传入数据包的队列只能在 NIC 边缘建立，在数据平面开始处理数据包之前。网络栈仅以应用程序处理的速度向对等方发送确认。应用程序处理速率的任何减慢都会迅速导致对等方的窗口缩小。数据平面还可以监控 NIC 边缘的队列深度，并向控制平面发出信号以分配额外的资源（更多硬件线程、增加时钟频率），显式通知对等方关于拥塞（例如，通过 ECN），并做出拥塞管理策略决策（例如，通过 RED）。 Native, zero-copy API with explicit flow control: 我们不暴露或模拟 POSIX 网络 API。相反，数据平面内核和应用程序通过存储在内存中的消息在协调的过渡点进行通信。我们的 API 设计为在两个方向上实现真正的零拷贝操作，从而提高延迟和数据包速率。数据平面和应用程序协作管理消息缓冲池。传入的数据包以只读方式映射到应用程序，应用程序可以在稍后点保留消息缓冲并将其返回给数据平面。应用程序向数据平面的发送传输内存位置的分散/聚集列表，但由于内容未被复制，应用程序必须保持内容不变，直到对等方确认接收。数据平面强制执行流量控制正确性，并可能修剪超过滑动窗口可用大小的传输请求，但应用程序控制传输缓冲。 很好奇这个零拷贝怎么做的 Flow consistent, synchronization-free processing: multi-queue NICs provide flow-consistent hashing of incoming traffic to distinct hardware queues ","date":"2024-09-15","objectID":"/posts/ix-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#ix-design-approach"},{"categories":["OS"],"collections":null,"content":"IX Implementation ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#ix-implementation"},{"categories":["OS"],"collections":null,"content":"Overview IX 架构，重点介绍了控制平面和多个数据平面之间的分离 IX 控制平面由完整的 Linux 内核和 IXCP（一个用户级程序）组成。 Linux 内核初始化 PCIe 设备（如网卡），并为数据平面提供资源分配的基本机制，包括核心、内存和网络队列。同样重要的是，Linux 提供了系统调用和服务，这些是与广泛应用程序兼容所必需的，如文件系统和信号支持。 VMX ring 0 中运行 Linux 内核，这种模式通常用于虚拟化系统中的虚拟机管理程序。我们使用 Linux 中的 Dune 模块，使数据平面能够在 VMX non root ring 0 中作为特定于应用程序的操作系统运行，这种模式通常用于虚拟化系统中的客户内核。应用程序像往常一样在 VMX 非根环 3 中运行。这种方法为数据平面提供了直接访问硬件功能（如页表和异常）和网卡的直通访问。此外，它提供了控制平面、数据平面和不受信任的应用程序代码之间的完全三向保护。 每个 IX 数据平面支持一个多线程应用程序。 memcached 是一个高性能的分布式内存对象缓存系统，用于加速动态 Web 应用程序，通过减轻数据库负载来提高性能。 httpd 是 Apache HTTP Server 的守护进程，通常简称为 Apache。它是一个开源的、跨平台的 Web 服务器软件，用于提供 HTTP 服务。httpd 可以配置为负载均衡器，将请求分发到多个后端服务器，以提高系统的性能和可靠性。 例如，图 1a 展示了一个用于多线程 memcached 服务器的数据平面和另一个用于多线程 httpd 服务器的数据平面。控制平面以粗粒度方式为每个数据平面分配资源。核心分配通过实时优先级和 cpusets 控制；内存以大页面分配；每个 NIC 硬件队列分配给单个数据平面。这种方法避免了在需求应用程序之间进行细粒度时间复用资源的额外开销和不稳定性。 app 都跑在 ring3， IX 在 ring 0 non-root, Dune 和内核 也就是控制平面是在 ring0 vmx-root 为什么需要大页内存，减少 TLB？一般网络数据包处理都需要大页内存，减少页表遍历次数。例如，如果使用 4KB 的页面，一个 2MB 的内存区域需要 512 个 TLB 条目；而如果使用 2MB 的大页，只需要一个 TLB 条目。 每个 IX 数据平面作为一个单一地址空间操作系统 single address-space OS 运行，并在共享的用户级地址空间中支持两种线程类型： (i) 弹性线程 elastic threads，与 IX 数据平面交互以启动和消费网络 I/O； (ii) 后台线程 background threads。 弹性线程和后台线程都可以发出任意 POSIX 系统调用，这些调用在转发到 Linux 内核之前由数据平面进行中介和安全验证。弹性线程预计不会发出阻塞调用，因为延迟的数据包处理会对网络行为产生不利影响。每个弹性线程独占使用分配给数据平面的核心或硬件线程，以实现高预测延迟的高性能。相比之下，多个后台线程可以分时共享分配的硬件线程。例如，如果一个应用程序分配了四个硬件线程，它可以将所有线程用作弹性线程来服务外部请求，或者它可以暂时过渡到三个弹性线程，并使用一个后台线程执行任务，如垃圾收集。当控制平面使用类似于 Exokernel 中的协议撤销或分配额外的硬件线程时，数据平面调整其弹性线程的数量。 POSIX 旨在确保不同操作系统之间的兼容性，Portable，比如 I/O 之类的。POSIX 定义了网络编程的接口，如 socket 编程。 弹性线程，网络 IO。后台线程，垃圾回收？ ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:1","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#overview"},{"categories":["OS"],"collections":null,"content":"The IX Dataplane IX 数据平面，它与典型的内核不同，因为它专门用于高性能网络 I/O，并且只运行一个应用程序，类似于库操作系统，但具有内存隔离。我们的数据平面仍然提供了许多熟悉的内核级服务。 library OS, 数据平面还管理自己的虚拟地址转换，通过嵌套分页支持。与当代操作系统相比，它仅使用大页面（2MB）。我们倾向于使用大页面 large pages，因为它们的地址转换开销较小 [5, 7]，并且现代服务器中物理内存资源相对丰富。数据平面仅维护一个地址空间；内核页面通过超级用户位进行保护。我们故意选择不支持可交换内存，以避免增加性能变异性。 这个大页面，在 dpdk 也能看到，但是 not to support swappable memory 是什么意思，也就是纯内存的？应该很多都是纯内存的把 We provide a hierarchical timing wheel implementation for managing network timeouts, 我们当前的 IX 数据平面实现基于 Dune，并需要 Intel x86-64 系统上可用的 VT-x 虚拟化功能 [62]。然而，它可以移植到任何具有虚拟化支持的架构，例如 ARM、SPARC 和 Power。 IX 数据平面目前由 39K SLOC [67] 组成，并利用了一些现有代码库：41% 来自 Intel NIC 设备驱动程序的 DPDK 变体 [28]，26% 来自 lwIP TCP/IP 堆栈 [18]，15% 来自 Dune 库。 硬件限制还是很严重的，看着像缝合怪？lwIP 是 lightweight IP 开源 TCP/IP 协议栈，为什么不基于 UDP 呢，会不会吞吐和延迟更好看？还是说受限于 TCP，因为大部分 NIC 或者 middleboxes 都优化了 TCP 还有就是 batch 能不能变成 streaming？毕竟 TCP 底层应该是流的，字节流 ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:2","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#the-ix-dataplane"},{"categories":["OS"],"collections":null,"content":"Dataplane API and Operation 应用程序的 elastic threads 弹性线程通过三种异步、非阻塞机制与 IX 数据平面进行交互 弹性线程 处理 IO 这些机制总结在表 1 中：它们向数据平面发出批量系统调用；它们消耗数据平面生成的事件条件；它们可以直接、安全地访问包含传入有效载荷的（mbuf）。后者允许对传入的网络流量进行零拷贝访问。应用程序可以保留 mbuf，直到它通过 recv done 批量系统调用请求数据平面释放它们。 Both batched system calls and event conditions are passed through arrays of shared memory 我们构建了一个用户级库，称为 libix，它抽象了我们底层 API 的复杂性。它为遗留应用程序提供了一个兼容的编程模型，并显著简化了新应用程序的开发。libix 目前包括一个与 libevent 和非阻塞 POSIX 套接字操作非常相似的接口。它还包括新的零拷贝读写操作接口，这些接口更高效，但需要对现有应用程序进行更改。libix 自动将多个写请求合并到每个批处理轮次中的单个 sendv 系统调用中。这提高了局部性，简化了错误处理，并确保了正确的行为，因为它即使在传输失败时也保留了数据流顺序。合并还促进了传输流控制，因为我们可以使用传输向量（sendv 的参数）来跟踪传出数据缓冲区，并在必要时在传输窗口有更多可用空间时重新发出写操作，如发送事件条件所通知的那样。我们目前的缓冲区大小策略非常基本；我们强制执行最大挂起发送字节限制，但我们计划在未来使这更加动态[21]。 这里的零拷贝是怎么实现的？用户态网络协议应该都跟零拷贝有关把，都需要共享内存，通过零拷贝直接访问内存缓冲，这样数据传输开销减少。 图 1b 说明了 IX 数据平面中弹性线程的运行到完成操作。NIC 接收缓冲区映射在服务器的内存中，NIC 的接收描述符环填充了一组缓冲区描述符，允许它使用 DMA 传输传入的数据包。弹性线程（1）轮询接收描述符环，并可能向 NIC 发布新的缓冲区描述符，以便用于未来的传入数据包。然后，弹性线程（2）通过 TCP/IP 网络堆栈处理有限数量的数据包，从而生成事件条件。接下来，线程（3）切换到用户空间应用程序，该应用程序消耗所有事件条件。假设传入的数据包包括远程请求，应用程序处理这些请求并以一批系统调用进行响应。在从用户空间返回控制权后，线程（4）处理所有批量系统调用，特别是那些指导传出 TCP/IP 流量的调用。线程还（5）运行所有内核定时器，以确保符合 TCP 行为。最后（6），它将传出的以太网帧放入 NIC 的传输描述符环中进行传输，并通过更新传输环的尾部寄存器通知 NIC 启动这些帧的 DMA 传输。在单独的传递中，它还根据传输环的头部位置释放任何已完成传输的缓冲区，可能会生成发送事件条件。该过程在一个循环中重复，直到没有网络活动。在这种情况下，线程进入一个静默状态，该状态涉及超线程友好的轮询或可选地进入一个节能的 C 状态，代价是一些额外的延迟。 完整的流程，使用的是轮询，IX 能实现更高效的 epoll 或者其他事件驱动机制吗？IX 应该是有 Multi-queue 的, IX 框架中的弹性线程负责处理网络数据包。每个弹性线程可以绑定到一个特定的 CPU 核心， 提高并行处理能力 ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:3","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#dataplane-api-and-operation"},{"categories":["OS"],"collections":null,"content":"Multi-core Scalability IX 数据平面针对多核可扩展性进行了优化，因为弹性线程在常见情况下以无同步和无一致性的方式运行。这比无锁同步的要求更强，无锁同步即使在单个线程是特定数据结构的主要消费者时，也需要昂贵的原子指令[13]。这是通过一系列有意识的设计和实现权衡实现的。 不知道弹性线程是怎么实现的，应该是修复了 Dune 不好支持多线程的问题 API 的实现经过了精心优化。每个弹性线程管理自己的内存池、硬件队列、事件条件数组和批量系统调用数组。事件条件和批量系统调用的实现直接受益于 IX 和应用程序之间的显式协作控制转移。由于生产者和消费者之间没有并发执行，事件条件和批量系统调用是基于原子操作的无同步实现。 弹性线程应该就是用来轮询的，应该有队列？ 第三，在网卡上使用流一致性哈希确保每个弹性线程操作一个不相交的 TCP 流子集。因此，在处理服务器应用程序的传入请求时，不会发生同步或一致性。对于具有出站连接的客户端应用程序，我们需要确保回复被分配到发出请求的同一弹性线程。由于我们无法反转 RSS 使用的 Toeplitz 哈希[43]，我们只需探测临时端口范围以找到一个会导致所需行为的端口号。请注意，这意味着客户端中的两个弹性线程不能共享到服务器的流。 flow-consistent hashing 是什么，这个会导致冲突吗，流一致性哈希是一种用于网络数据包处理的哈希算法，其主要目的是确保同一网络流（如 TCP 连接）的数据包始终被分配到同一个处理单元 IX 确实有一些需要同步更新的共享结构。例如，ARP 表由所有弹性线程共享，并受 RCU 锁保护[41]。因此，常见情况下的读取是无一致性的，但罕见的更新不是。RCU 对象在每个弹性线程完成一个运行到完成周期的时间段后进行垃圾收集。 还是需要一致性，更新很罕见？ ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:4","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#multi-core-scalability"},{"categories":["OS"],"collections":null,"content":"Security Model IX API 及其实现采用了一种应用程序代码与网络处理栈之间的协作流控模型。与用户级栈不同，在用户级栈中，应用程序被信任以正确执行网络行为，而 IX 的保护模型对应用程序的假设很少。恶意或行为不端的应用程序只能伤害自身，而不能破坏网络栈或影响其他应用程序。IX 中的所有应用程序代码都在用户模式下运行，而数据平面代码则在受保护的 Ring 0 中运行。应用程序无法访问数据平面内存，除非是只读的消息缓冲区。无法通过批量系统调用或其他用户级操作来违反对 TCP 和其他网络规范的正确遵循。此外，数据平面可用于强制执行网络安全策略，如防火墙和访问控制列表。IX 的安全模型与传统的基于内核的网络栈一样强大，这是所有最近提出的用户级栈所不具备的特性。 IX 利用了 vt-x ring3 和 ring0 吧，应该是比 dune 更安全？分离也使得更安全 IO MMU 没有用到？ ","date":"2024-09-15","objectID":"/posts/ix-paper/:6:5","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#security-model"},{"categories":["OS"],"collections":null,"content":"Evaluation evaluation 表现都非常好，对比了 Linux 和用户态的 mTCP， 都使用了 TCP ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#evaluation"},{"categories":["OS"],"collections":null,"content":"Experimental Methodology 我们启用了超线程，因为它可以提高性能 我们的基准测试的 Linux 客户端和服务器实现使用了 libevent 框架和 epoll 系统调用。我们从公共领域发布中下载并安装了 mTCP [30]，但必须使用 mTCP API 自己编写基准测试。我们使用 2.6.36 Linux 内核运行 mTCP，因为这是最新支持的内核版本。我们只报告了 mTCP 的 10GbE 结果，因为它不支持 NIC 绑定。对于 IX，我们将最大批处理大小绑定到每次迭代 B=64 个数据包，这在微基准测试中最大化吞吐量 ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:1","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#experimental-methodology"},{"categories":["OS"],"collections":null,"content":"Latency and Single-flow Bandwidth NetPIPE ping-pong benchmark IX 服务器的单向延迟为 5.7μs, 并实现了 5Gbps 的吞吐量，这是最大值的一半，消息大小仅为 20KB 两个 Linux 服务器的单向延迟为 24μs, 385KB messages to achieve 5Gbps Linux 是因为中断，带来延迟 mTCP 使用积极的批处理来抵消上下文切换的成本，这在特定测试中以更高的延迟为代价，高于 IX 和 Linux。 IX 消息小，处理很快，能达到高吞吐 5Gbps，单项延迟很低 ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:2","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#latency-and-single-flow-bandwidth"},{"categories":["OS"],"collections":null,"content":"Throughput and Scalability 对于所有三个测试（核心扩展、消息计数扩展、消息大小扩展），IX 的扩展性都比 mTCP 和 Linux 更激进。图 3a 显示，IX 仅需要 3 个核心即可饱和 10GbE 链路，而 mTCP 需要所有 8 个核心。在图 3b 中，对于每个连接 1024 次往返，IX 每秒交付 880 万条消息，这是 mTCP 吞吐量的 1.9 倍，是 Linux 的 8.8 倍。在这个数据包速率下，IX 达到了线路速率，仅受限于 10GbE 带宽。 IX 几乎是线性扩展，核心越多吞吐量越大，估计分离和弹性线程绑定 CPU + 批处理（减少上下文切换？）带来了很多好处 ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:3","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#throughput-and-scalability"},{"categories":["OS"],"collections":null,"content":"Connection Scalability 能处理大量连接 a large number of concurrent connections 最多 250,000 个连接，这是我们可用客户端机器的上限。正如预期的那样，吞吐量随着连接并发度的增加而增加，但对于非常大的连接数，由于在打开的连接之间进行多路复用的成本越来越高，吞吐量会下降。在峰值时，IX 的性能比 Linux 好 10 倍，与图 3b 的结果一致。在 250,000 个连接和 4x10GbE 的情况下，IX 能够达到其峰值吞吐量的 47%。 Linux 应该很难做到，但 IX 也有大量的 L3 缓存未命中？可以通过进一步优化 lwIP 和 TCP/IP 协议控制块结构大小和访问模式（这里没看懂） ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:4","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#connection-scalability"},{"categories":["OS"],"collections":null,"content":"Memcached Performance 平均和第 99 百分位延迟作为实现吞吐量 第 99 百分位延迟捕捉尾部延迟问题，是数据中心应用程序最相关的指标 [14]。大多数商业 memcached 部署都为每个服务器配置，以使第 99 百分位延迟不超过 200μs 到 500μs。 我们尚未尝试调整 memcached 的内部可扩展性 [20] 或支持零拷贝 I/O 操作 IX 显著减少了未加载的延迟，大约减少了一半 这里很奇怪，为什么没支持零拷贝？但表现仍好于 Linux 而且好太多了 ","date":"2024-09-15","objectID":"/posts/ix-paper/:7:5","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#memcached-performance"},{"categories":["OS"],"collections":null,"content":"Discussion What makes IX fast: a networking stack can be implemented in a protected OS kernel and still deliver wire-rate performance for most benchmark IX 的好处不仅仅是最小化内核开销，零拷贝方法也有所帮助 没有中间缓冲区允许高效、特定于应用程序的 I/O 抽象实现，例如 libix 事件库 没有中间缓冲区是什么意思，传统网络栈应该是有的，而且需要在中间缓冲区做拷贝 IX 去掉中间缓冲区，用零拷贝、共享内存来提高性能，又用 Dune/VT-X 来保护虚拟内存？ 最后，我们仔细调整了 IX 以实现多核可扩展性，消除了引入同步或一致性流量的构造。 Dune 对多核的支持不太好吧，不知道 IX 是如何解决的？是弹性线程吗 IX 数据平面优化——完成运行、自适应批处理和零拷贝 API——也可以在用户级网络栈中实现，以获得类似的吞吐量和延迟方面的优势。虽然用户级实现会消除保护域交叉，但这不会导致比 IX 显著的性能提升。VMX 非根模式内的保护域交叉只会增加少量的额外开销，大约相当于一次 L3 缓存未命中 [7]。此外，这些开销在更高的数据包速率下很快被分摊。 这一段很重要， IX 比用户级网络栈快的原因到底是什么。用户级消除了什么，是进程上下文切换吗，VMX non-root 切换应该也有额外开销吧 批处理通常被理解为在低负载时以更高的延迟换取高负载时更好的吞吐量。IX 使用自适应、有界的批处理来实际改善这两个指标。图 6 比较了不同批处理大小上限 B 对 USR memcached 工作负载（图 5）的延迟与吞吐量的影响。在低负载下，B 不会影响尾部延迟，因为自适应批处理不会延迟挂起数据包的处理。在较高负载下，较大的 B 值提高了吞吐量，从 B=1 到 B=16 提高了 29%。对于这个工作负载，B≥16 最大化吞吐量。 在调整 IX 性能时，我们遇到了一个意外的硬件限制，该限制在高数据包速率和小平均批处理大小（即在数据平面饱和之前）时被触发：每次迭代发布新描述符所需的 PCIe 写入率高导致性能下降，因为我们扩展了核心数量。为了避免这个瓶颈，我们简单地在接收路径上合并 PCIe 写入，以便我们每次至少补充 32 个描述符条目。幸运的是，我们不必在发送路径上合并 PCIe 写入，因为这会影响延迟。 上界 B 在哪里调整的，感觉论文没有仔细讨论这一块，零拷贝好像也没怎么讨论 到目前为止，我们只测试了静态配置。在未来的工作中，我们将探索控制平面问题，包括动态运行时，以在保持吞吐量和延迟约束的同时，在可用弹性线程之间重新平衡网络流。 除了 TCP 应该还可以做 UDP？ ","date":"2024-09-15","objectID":"/posts/ix-paper/:8:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#discussion"},{"categories":["OS"],"collections":null,"content":"Related Work 与 IX 类似，Arrakis 使用硬件虚拟化将 I/O 数据平面与控制平面分离 [50]。IX 的不同之处在于它使用完整的 Linux 内核作为控制平面；在控制平面、网络栈和应用程序之间提供三向隔离；并提出了一种数据平面架构，该架构针对高吞吐量和低延迟进行了优化。另一方面，Arrakis 使用 Barrelfish 作为控制平面 [6]，并包括对 IOMMU 和 SR-IOV 的支持。 ","date":"2024-09-15","objectID":"/posts/ix-paper/:9:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#related-work"},{"categories":["OS"],"collections":null,"content":"Conclusion memcached 移植到 IX 可以消除内核瓶颈，并将吞吐量提高多达 3.6 倍，同时将尾部延迟减少超过 2 倍。 ","date":"2024-09-15","objectID":"/posts/ix-paper/:10:0","tags":["Paper Reading"],"title":"Paper Reading: IX: A Protected Dataplane Operating System","uri":"/posts/ix-paper/#conclusion"},{"categories":["OS"],"collections":null,"content":"Dune: Safe User-level Access to Privileged CPU Features 是 MIT OS 课上的讲座，也是一篇很有意思的论文，很类似 exokernel，不过更像是内核上的功能，利用虚拟化使得进程可以访问一些内核态的功能，比如暴露 page table，使得 jvm 可以进行更高级的 GC。后续也有 IX，ZygOS 等相关论文。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#dune-safe-user-level-access-to-privileged-cpu-features"},{"categories":["OS"],"collections":null,"content":"Abstract Dune 是一个系统，它为应用程序提供了直接但安全的硬件特性访问，例如环保护、页表和标记 TLB，同时保留了现有操作系统对进程的接口。Dune 利用现代处理器中的虚拟化硬件为进程提供抽象，而不是机器抽象。它由一个小的内核模块组成，该模块初始化虚拟化硬件并与内核交互，以及一个用户级库，帮助应用程序管理特权硬件功能。我们介绍了 Dune 在 64 位 x86 Linux 上的实现。我们使用 Dune 实现了三个可以从访问特权硬件中受益的用户级应用程序：一个用于不受信任代码的沙箱、一个特权分离设施和一个垃圾收集器。使用 Dune 极大地简化了这些应用程序的实现，并提供了显著的性能优势。 和 exokernel 很类似 ","date":"2024-09-11","objectID":"/posts/dune-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction 许多应用程序可以从访问“仅内核可用”的硬件特性中受益。例如，Azul Systems 通过使用分页硬件显著加速了垃圾回收 [15, 36]。另一个例子是进程迁移，尽管可以在用户程序中实现，但如果能访问页错误 [40] 和系统调用 [32]，将会受益匪浅。在某些情况下，甚至可能需要完全替换内核以满足特定应用程序的需求。例如，IBOS 通过将浏览器抽象移到最低的操作系统层来提高浏览器安全性 [35]。 感觉最主要的好处就是帮助 GC 做得更好，能知道页错误出了什么问题 这类系统需要对内核进行修改，因为出于安全和隔离的原因，用户空间的硬件访问是受限的。不幸的是，在实践中修改内核并不理想，因为内核的修改可能会相当侵入性，如果操作不当，会影响整个系统的稳定性。此外，如果多个应用程序需要内核修改，无法保证这些修改能够兼容。另一种策略是将应用程序打包到带有专用内核的虚拟机镜像中 [4, 14]。许多现代 CPU 包含虚拟化硬件，使客户操作系统能够安全高效地访问内核硬件特性。此外，虚拟机提供了类似于进程的故障隔离——即，错误或恶意行为不应导致整个物理机器崩溃。 生产内核如 Linux 复杂且难以修改。然而，实现一个带有简单虚拟内存层的专用内核也同样具有挑战性。除了虚拟内存，还必须支持文件系统、网络栈、设备驱动程序和引导过程。 本文介绍了一种新的应用程序使用内核硬件特性的方法：使用using virtualization hardware to provide a process，而不是machine abstraction。我们在 64 位 Intel CPU 上的 Linux 系统中实现了这种方法，称为 Dune。Dune 提供了一个可加载的内核模块，该模块与未修改的 Linux 内核配合工作。该模块允许进程进入“Dune 模式”，这是一个不可逆的转换，通过虚拟化硬件，安全快速地访问特权硬件特性，包括特权模式、虚拟内存寄存器、页表和中断、异常及系统调用向量。我们提供了一个用户级库 libDune，以促进这些特性的使用。 基于 Linux 实现的 对于符合其范式的应用程序，Dune 比虚拟机提供了几个优势。首先，Dune 进程是一个正常的 Linux 进程，唯一的区别是它使用 VMCALL 指令来调用系统调用。这意味着 Dune 进程可以完全访问系统的其余部分，并且是系统的一部分，Dune 应用程序易于开发（像应用程序编程，而不是内核编程）。其次，由于 Dune 内核模块不试图提供机器抽象，模块可以更简单且更快。特别是，虚拟化硬件可以配置为避免保存和恢复虚拟机所需的几种硬件状态。 VMCALL 是什么，类似 hyper-v 之类的吗？看上去只有 x86 支持的比较好？是不是一个问题呢。VMCALL 适用于所有支持 Intel VT-x 技术的虚拟化环境，包括 KVM、Xen 等。那么 Dune 和 KVM 的区别又是什么？后者全虚拟化，提供抽象硬件，虚拟机能运行在物理硬件上，是一个内核？结合了 QEMU，类似虚拟机而不是特权访问。 通过 Dune，我们做出了以下贡献： 我们提出了一种设计，利用硬件辅助虚拟化安全高效地向用户程序暴露特权硬件特性，同时保留标准操作系统抽象。 我们详细评估了三种硬件特性，并展示了它们如何使受益于用户程序：异常、分页和特权模式。 我们通过实现和评估三个用例（沙箱、特权分离和垃圾回收）展示了 Dune 的端到端实用性。 虚拟化技术感觉没什么缺点，又安全，性能也不差，可能也就开销比较大吧，再就是把负担给到开发者，需要熟悉虚拟化技术、特权访问等等， ","date":"2024-09-11","objectID":"/posts/dune-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#introduction"},{"categories":["OS"],"collections":null,"content":"Virtualization and Hardware Dune 能够暴露哪些特权硬件特性 我们以 x86 CPU 和 Intel VT-x 为例描述 Dune。然而，这并不是我们设计的基础，在第 7 节中，我们将扩展讨论，包括未来可能支持的其他架构。 问题之一把，不是很好跨平台 ","date":"2024-09-11","objectID":"/posts/dune-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#virtualization-and-hardware"},{"categories":["OS"],"collections":null,"content":"The Intel VT-x Extension Intel x86 (ISA) 的虚拟化，但是 AMD 的不同 虚拟内存可能是 VMM 最难安全暴露的硬件特性。 可能暴露是比较困难的，难度比较大，要保证安全可用 ","date":"2024-09-11","objectID":"/posts/dune-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#the-intel-vt-x-extension"},{"categories":["OS"],"collections":null,"content":"Supported Hardware Features Dune 使用 VT-x 为 x86 保护硬件提供用户程序的完全访问权限。这包括三个特权硬件特性： 异常、虚拟内存和特权模式。 表 1 显示了为每个特性提供的相应特权指令。Dune 还暴露了分段，但我们不再进一步讨论，因为它是现代 x86 CPU 上的主要遗留机制。 用户程序还可以从快速灵活的虚拟内存访问中受益 User programs can also benefit from fast and flexible access to virtual memory Dune 还赋予用户程序手动控制 TLB 失效的能力 Dune also gives user programs the ability to manually control TLB invalidations， 这允许单个用户程序高效地在多个页表之间切换。总的来说，我们展示了使用 Dune 在 Appel 和 Li 的用户级虚拟内存基准测试 [5] 中比 Linux 快 7 倍 最后，Dune 暴露了对特权模式的访问 Dune exposes access to privilege modes 尽管 Dune 暴露的硬件特性足以支持我们的动机用例，但其他几个硬件特性，如缓存控制、调试寄存器和访问 DMA 设备的能力，也可以通过虚拟化硬件安全地暴露。我们将这些留给未来的工作，并在第 7 节中讨论它们的潜力。 想知道这个缓存控制、访问 DMA 是怎么做的，如果可以虚拟化 DMA 会怎么样 TLB 硬件存储最近的虚拟地址和物理地址映射，一般用内核管理 TLB 失效，如果让程序来管理，手动失效可以更快更新？比如进程切换？但是会不会比较危险呢，把别人的 TLB 弄丢了，一致性也可能会存在问题，导致内存访问错误。而 TLB 失效也是能够优化 GC 的一点吧。 特权模式比如 syscall，创建内存分配，DMA 等等，还有就是页中断、异常处理、系统调用等等 ","date":"2024-09-11","objectID":"/posts/dune-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#supported-hardware-features"},{"categories":["OS"],"collections":null,"content":"Kernel Support for Dune ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#kernel-support-for-dune"},{"categories":["OS"],"collections":null,"content":"System Overview 图 1 展示了 Dune 架构的高层次视图。Dune 通过一个模块扩展内核，该模块启用 VT-x，将内核置于 VMX root 模式。使用 Dune 的进程通过在 VMX non-root 模式下运行，获得对特权硬件的直接但安全的访问权限。Dune 模块拦截 VM 退出，这是 Dune 进程访问内核的唯一方式，并执行任何必要的操作，如服务页面故障、调用系统调用或在 HLT 指令后让出 CPU。Dune 还包括一个库，称为 libDune，用于协助在用户空间中管理特权硬件特性，这在第 4 节中进一步讨论。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#system-overview"},{"categories":["OS"],"collections":null,"content":"Threat Model 我们假设 CPU 没有缺陷，尽管我们承认在极少数情况下已经发现了可利用的硬件缺陷 不太懂这个假设 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#threat-model"},{"categories":["OS"],"collections":null,"content":"Comparing to a VMM 具体来说，Dune 暴露了一个进程环境，而不是机器环境。因此，Dune 无法支持正常的客户操作系统，但这使得 Dune 更轻量级和更灵活。一些最显著的区别如下 所以和 KVM 等的最大区别就是 dune 是个进程，更加轻量级 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#comparing-to-a-vmm"},{"categories":["OS"],"collections":null,"content":"Memory Management 每次内存管理的实现细节就一脸懵，dune 自己做了个页表转换？ EPT（Extended Page Table，扩展页表）是 Intel VT-x 虚拟化技术中的一种硬件机制，用于在虚拟机（Guest OS）和虚拟机监控程序（Hypervisor）之间提供安全的内存管理。EPT 的主要作用是增强虚拟机的内存隔离和安全性，同时提高内存访问的效率。 内存管理是 Dune 模块的最大责任之一。挑战在于向用户程序暴露直接的页表访问，同时防止对物理内存的任意访问。此外，我们的目标是默认提供一个正常的进程内存地址空间，允许用户程序仅添加他们需要的功能，而不是完全替换内核级的内存管理。 EPT format incompatibility 好处，直接页表访问 + 内存隔离 + 灵活+ 性能 但问题是 intel vt-x EPT 需要和 x86 页表做兼容，可能会出现一些程序不兼容？内存管理也比较复杂吧，开发人员也需要知道 ept 是什么意思 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#memory-management"},{"categories":["OS"],"collections":null,"content":"Exposing Access to Hardware x86 包括各种控制寄存器， Dune 出于性能原因限制了对硬件寄存器的访问。例如，Dune 不允许修改 MSR， VT-x（Virtualization Technology for x86） 是 Intel 开发的一种硬件虚拟化技术，用于在 x86 架构上实现虚拟化。 VMX root 模式： 用于运行虚拟机监控程序（VMM），不改变 CPU 行为，除了启用对 VT-x 管理的新指令的访问。 VMX non-root 模式： 用于运行虚拟化的客户操作系统（Guest OS），限制了 CPU 行为，旨在运行虚拟化的客户操作系统。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:5","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#exposing-access-to-hardware"},{"categories":["OS"],"collections":null,"content":"Preserving OS Interfaces Dune 还保留了对操作系统系统调用的访问 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:6","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#preserving-os-interfaces"},{"categories":["OS"],"collections":null,"content":"Implementation Dune 目前支持在 Intel x86 处理器上以 64 位长模式运行的 Linux。对 AMD CPU 和 32 位模式的支持是未来的可能扩展 兼容性很大问题，因为是基于 VT-x 来做的，然而，高级代码不与 KVM 共享，因为 Dune 的操作方式与 VMM 不同。此外，我们的 Dune 模块比 KVM 更简单，仅由 2,509 行代码组成 ","date":"2024-09-11","objectID":"/posts/dune-paper/:5:7","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#implementation"},{"categories":["OS"],"collections":null,"content":"User-mode Environment 使用 Dune 的进程的执行环境与正常进程有一些差异。由于特权环是暴露的硬件特性，一个差异是用户代码在环 0 中运行。尽管改变了某些指令的行为，但这通常不会导致现有代码的不兼容性。环 3 也可用，并且可以选择用于限制不受信任的代码。另一个差异是系统调用必须作为超调用执行。为了简化支持这种更改，我们提供了一种机制，可以检测何时从环 0 执行系统调用，并自动将其重定向到内核作为超调用。这是 libDune 中包含的许多功能之一。 还是兼容问题吧，ring0 是最高特权级别，几乎就是内核和设备操作，ring3 是用户态了 而用户代码在环 0 中运行： 在 Dune 中，用户代码在环 0（Ring 0）中运行，而不是在传统的环 3（Ring 3）中运行。这意味着用户代码具有更高的特权级别，可以直接访问特权硬件特性 那需不需要切换到 ring3 呢？而且 syscall 变成了 hypercall ","date":"2024-09-11","objectID":"/posts/dune-paper/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#user-mode-environment"},{"categories":["OS"],"collections":null,"content":"Bootstrapping 在许多方面，将进程转换到 Dune 模式类似于启动操作系统。第一个问题是，在启用 Dune 之前，必须提供有效的页表。因为尽管目标是使进程地址在转换前后保持一致，但必须考虑 EPT 的压缩布局。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#bootstrapping"},{"categories":["OS"],"collections":null,"content":"Limitations 但 libDune 仍然缺少一些功能 但我们尚未完全集成对信号的支持，其次，尽管我们支持 pthreads，但 libDune 中的一些实用程序，如页表管理，尚未线程安全。这两个问题都可以通过进一步的实现来解决。 最大的问题还是线程不安全，可以执行较低，不支持非 x86 吧，而且要先提供页表，保证地址有效 ","date":"2024-09-11","objectID":"/posts/dune-paper/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#limitations"},{"categories":["OS"],"collections":null,"content":"Applications ","date":"2024-09-11","objectID":"/posts/dune-paper/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#applications"},{"categories":["OS"],"collections":null,"content":"Sandboxing 沙盒 我们尚未支持所有系统调用，但我们支持足够多的系统调用以运行大多数单线程 Linux 应用程序。然而，未来支持多线程程序没有任何障碍。 不支持多线程 ","date":"2024-09-11","objectID":"/posts/dune-paper/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#sandboxing"},{"categories":["OS"],"collections":null,"content":"Wedge 不懂 ","date":"2024-09-11","objectID":"/posts/dune-paper/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#wedge"},{"categories":["OS"],"collections":null,"content":"Garbage Collection GC 应该是最有用的 Fast faults Dirty bits Page table TLB control 我们修改了 Boehm GC [12] 以使用 Dune 来提高性能。Boehm GC 是一个健壮的标记-清除收集器，支持并行和增量收集。它被设计为要么与 C/C++ 程序一起使用作为保守收集器，要么由编译器和运行时后端使用，其中保守性可以控制。它被广泛使用，包括 Mono 项目和 GNU Objective C。 https://zhuanlan.zhihu.com/p/365571886 ","date":"2024-09-11","objectID":"/posts/dune-paper/:7:3","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#garbage-collection"},{"categories":["OS"],"collections":null,"content":"Evaluation ","date":"2024-09-11","objectID":"/posts/dune-paper/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#evaluation"},{"categories":["OS"],"collections":null,"content":"Overhead from Running in Dune Dune 的性能受到两个主要开销来源的影响。首先，VT-x 增加了进入和退出内核的成本——VM 入口和 VM 退出比快速系统调用指令或异常更昂贵。因此，系统调用和其他类型的故障（例如，页面故障）在 Dune 中必须支付固定成本。其次，使用 EPT 使 TLB 未命中的成本更高，因为在某些情况下，硬件页面遍历器必须遍历两个页表而不是一个。 getpid, page fault, page walk 开销也挺大的，看上去不仅是两三倍了 其他比如 trap, ptrace 就快得多了 ","date":"2024-09-11","objectID":"/posts/dune-paper/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#overhead-from-running-in-dune"},{"categories":["OS"],"collections":null,"content":"Optimizations Made Possible by Dune ptrace 是系统调用拦截性能的度量。这是 Linux 进程使用 ptrace 拦截系统调用（getpid）、将系统调用转发到内核并返回结果的成本。在 Dune 中，这是在 VMX non-root 模式下使用环保护直接拦截系统调用、通过 VMCALL 转发系统调用并返回结果的成本。 PTRACE SYSEMU 是应用程序希望拦截系统调用但不将其转发到内核而是仅在内部实现它们时的最有效机制。由于 ptrace 需要将调用转发到内核，因此 PTRACE SYSEMU 是最有效的机制。使用 PTRACE SYSEMU 拦截系统调用的延迟为 13,592 个周期。在 Dune 中，这可以通过直接处理硬件系统调用陷阱来实现，延迟仅为 180 个周期。这表明 Dune ptrace 基准测试的大部分开销实际上是通过 VMCALL 转发 getpid 系统调用，而不是拦截系统调用。 trap 表示进程获取页面故障异常所需的时间。我们将 Linux 中 SIGSEGV 信号的延迟与 Dune 中硬件生成的页面故障进行比较。 appel1 是用户级虚拟内存管理性能的度量。它对应于 [5] 中描述的 TRAP、PROT1 和 UNPROT 测试，其中访问了 100 个受保护的页面，导致故障。然后，在故障处理程序中，故障页面被解除保护，并保护了一个新页面。 appel2 是用户级虚拟内存管理的另一个度量。它对应于 [5] 中描述的 PROTN、TRAP 和 UNPROT 测试，其中保护了 100 个页面。然后访问每个页面，故障处理程序解除故障页面的保护。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#optimizations-made-possible-by-dune"},{"categories":["OS"],"collections":null,"content":"Application Performance \u0026hellip; 可以看到 EPT 带来的问题还是很大的 忽略，直接看 GC Garbage Collector 这些基准测试的结果如表 6 所示。直接移植显示了混合结果，由于内存保护和故障处理程序的改进，但也受到 EPT 开销的减速。一旦我们开始使用更多硬件特性，我们就会看到明显的性能改进。除了 XML Parser 之外，TLB 版本在 10.9% 到 23.8% 之间提高了性能，脏位版本在 26.4% 到 40.7% 之间提高了性能。 XML 基准测试很有趣，因为它显示了所有三个版本在 Dune 下的减速：直接版本慢 19.0%，TLB 版本慢 12.2%，脏位版本慢 0.2%。这似乎是由 EPT 开销引起的，因为基准测试没有产生足够的垃圾来受益于我们对 Boehm GC 所做的修改。这在表 6 中有所体现；总分配量几乎等于最大堆大小。我们通过修改基准测试来验证这一点，改为处理一系列 XML 文件，依次处理每个文件，以便回收内存。然后，我们看到随着文件数量的增加，Dune 版本相对于基线的线性改进。使用十个 150MB XML 文件作为输入，Boehm GC 的脏位版本显示执行时间比基线提高了 12.8%。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:8:3","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#application-performance"},{"categories":["OS"],"collections":null,"content":"Application Performance \u0026hellip; 可以看到 EPT 带来的问题还是很大的 忽略，直接看 GC Garbage Collector 这些基准测试的结果如表 6 所示。直接移植显示了混合结果，由于内存保护和故障处理程序的改进，但也受到 EPT 开销的减速。一旦我们开始使用更多硬件特性，我们就会看到明显的性能改进。除了 XML Parser 之外，TLB 版本在 10.9% 到 23.8% 之间提高了性能，脏位版本在 26.4% 到 40.7% 之间提高了性能。 XML 基准测试很有趣，因为它显示了所有三个版本在 Dune 下的减速：直接版本慢 19.0%，TLB 版本慢 12.2%，脏位版本慢 0.2%。这似乎是由 EPT 开销引起的，因为基准测试没有产生足够的垃圾来受益于我们对 Boehm GC 所做的修改。这在表 6 中有所体现；总分配量几乎等于最大堆大小。我们通过修改基准测试来验证这一点，改为处理一系列 XML 文件，依次处理每个文件，以便回收内存。然后，我们看到随着文件数量的增加，Dune 版本相对于基线的线性改进。使用十个 150MB XML 文件作为输入，Boehm GC 的脏位版本显示执行时间比基线提高了 12.8%。 ","date":"2024-09-11","objectID":"/posts/dune-paper/:8:3","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#garbage-collector"},{"categories":["OS"],"collections":null,"content":"Reflections on Hardware ","date":"2024-09-11","objectID":"/posts/dune-paper/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#reflections-on-hardware"},{"categories":["OS"],"collections":null,"content":"Related Work ","date":"2024-09-11","objectID":"/posts/dune-paper/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#related-work"},{"categories":["OS"],"collections":null,"content":"Conclusion ","date":"2024-09-11","objectID":"/posts/dune-paper/:11:0","tags":["Paper Reading"],"title":"Paper Reading: Dune","uri":"/posts/dune-paper/#conclusion"},{"categories":["OS"],"collections":null,"content":"Exokernel: An Operating System Architecture for Application-Level Resource Management 微内核？搜了一下发现上交的操作系统课甚至会讲这篇论文，但这篇论文也太早了 1995 年的，MIT 6828 (现 6.1810) 应该也会讲 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#exokernel-an-operating-system-architecture-for-application-level-resource-management"},{"categories":["OS"],"collections":null,"content":"Abstract operating system abstractions such as interprocess com- munication and virtual memory 传统 OS 会通过固定这些接口来限制程序的性能、灵活性、功能。Exokernel 操作系统架构通过提供应用程序级别的物理资源管理来解决这个问题。在 Exokernel 架构中，一个小的内核通过低级接口将所有硬件资源安全地导出给不受信任的库操作系统。库操作系统使用这个接口来实现系统对象和策略。这种资源保护与管理的分离允许对传统操作系统抽象进行应用程序特定的定制，通过扩展、专门化甚至替换库来实现。 The exokernel operating system architecture addresses this problem by providing application-level management of physical resources. 例如，虚拟内存和进程间通信抽象完全在应用程序级别的库中实现 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#abstract"},{"categories":["OS"],"collections":null,"content":"Introduction Operating systems define the interface between applications and physical resources 比如内存管理，文件系统？进程/线程管理？ Traditionally, operating systems hide information about machine resources behind high-level abstractions such as processes, files, address spaces and interprocess communication We believe these problems can be solved through application- level (i.e., untrusted) resource management. To this end, we have designed a new operating system architecture, exokernel, in which traditional operating system abstractions, such as virtual memory (VM) and interprocess communication (IPC), are implemented en- tirely at application level by untrusted software 资源管理交给程序来解决？内核，exokernel 的 vm 和 ipc 也由 app 实现？ Application writers select libraries or implement their own. New implementations of library operating systems are incorporated by simply relinking application executables 应用程序级别的文件缓存控制可以将应用程序运行时间减少 45%，应用程序特定的虚拟内存策略提高应用程序性能，不适当的文件系统实现决策会对数据库的性能产生巨大影响，通过将信号处理推迟到应用程序，异常处理可以快一个数量级。 这里的论文也都很老，文件系统决策会影响数据库性能是什么意思呢，缓存替换策略？难道这些不是数据库来实现吗，数据库可以有自己的缓存挂哪里，还是需要定制文件系统呢，比如直接 IO，InnoDB 也有自己的缓存管理系统，文件系统的 IO 接口应该也是优化过的 The exokernel architecture is founded on and motivated by a single, simple, and old observation: the lower the level of a primitive, the more efficiently it can be implemented, and the more latitude it grants to implementors of higher-level abstractions. 还是很有道理的，应用程序自己做优化就好了 Therefore, an exokernel uses a different approach: it exports hardware resources rather than emulating them, which allows an efficient and simple implementation. secure bindings, visible re-source revocation, abort protocol (exokernel can break secure bindings ) (1) exokernels can be made efficient due to the limited number of simple primitives they must provide; (2) low-level secure multiplexing of hardware resources can be provided with low overhead; (3) traditional abstractions, such as VM and IPC, can be implemented efficiently at application level, where they can be easily extended, specialized, or replaced; (4) applications can create special-purpose implementations of abstractions, tailored to their functionality and performance needs. Aegis also gives ExOS (and other application-level software) flexibility that is not available in microkernel-based systems 例如，虚拟内存在应用程序级别实现，可以与分布式共享内存系统和垃圾收集器紧密集成。Aegis 的高效保护控制转移允许应用程序通过牺牲性能来换取额外功能，构建一系列高效的 IPC 原语 微内核又是什么，分布式共享内存又是什么 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#introduction"},{"categories":["OS"],"collections":null,"content":"Motivation for Exokernels Typically, the abstractions include processes, files, address spaces, and interprocess communication. ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#motivation-for-exokernels"},{"categories":["OS"],"collections":null,"content":"The cost of Fixed High-Level Abstractions abstractions in traditional operating systems -\u0026gt; general 固定的高级抽象会损害应用程序的性能，因为没有一种单一的方式来抽象物理资源或实现对所有应用程序都最佳的抽象。在实现抽象时，操作系统被迫在稀疏或密集地址空间、读密集或写密集工作负载等之间进行权衡。任何这样的权衡都会惩罚某些类别的应用程序。例如，关系数据库和垃圾收集器有时具有非常可预测的数据访问模式，当操作系统强加如 LRU 这样的通用页面替换策略时，它们的性能会受到影响。这种应用程序特定策略的性能改进可能是显著的；Cao 等人 [10] 测量到，应用程序控制的文件缓存可以将应用程序运行时间减少多达 45%。 固定的高级抽象会隐藏应用程序的信息。例如，大多数当前系统不会将低级异常、定时器中断或原始设备 I/O 直接提供给应用程序级软件。不幸的是，隐藏这些信息使得应用程序难以或不可能实现自己的资源管理抽象。例如，数据库实现者必须在文件系统之上努力模拟随机访问记录存储，因为操作系统隐藏了页面错误和定时器中断 还是 trade off 吧，认为抽象不利于性能，隐藏了信息，限制了应用程序的功能。数据库需要随机访问存储，是怎么模拟的？文件系统一开始不是为了 数据库设计的，应该是这意思吧，比如文件系统是文件为单位的，也有通用的缓存策略，所以数据库系统都基于文件系统实现了自己的 B+Tree 结构等索引结构？ 操作系统 OS 应该尽量暴露 low level 的原语 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#the-cost-of-fixed-high-level-abstractions"},{"categories":["OS"],"collections":null,"content":"Exokernels: An End-to-End Argument 为了提供最大的应用程序级别资源管理机会，Exokernel 架构由一个薄的 Exokernel 外壳组成，通过一组低级原语安全地复用和导出物理资源。库操作系统使用低级的 Exokernel 接口实现更高层次的抽象，并且可以定义专门用途的实现，以最好地满足应用程序的性能和功能目标.应用程序可以选择一个具有特定页表实现的库，该实现最适合其需求。据我们所知，没有其他安全的操作系统架构允许应用程序如此多的有用自由。 此外，安全复用不需要复杂的算法；它主要需要表来跟踪所有权。因此，Exokernel 的实现可以很简单。简单的内核提高了可靠性和维护的便利性，消耗较少的资源，并能够快速适应新的需求（例如，千兆位网络）。此外，正如 RISC 指令的情况一样，Exokernel 操作的简单性使它们能够高效地实现。 这样做的问题在哪？每个应用程序都得实现自己的页表？不安全？上下文切换更复杂 ？缓存一致性？ ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#exokernels-an-end-to-end-argument"},{"categories":["OS"],"collections":null,"content":"Library Operating Systems library operating systems, 不受 Exokernel 的信任, 可以自由地信任应用程序 例如，如果应用程序向库传递错误的参数，只会影响该应用程序。最后，Exokernel 系统中的内核交叉次数可以更少，因为大部分操作系统运行在应用程序的地址空间中。 库操作系统可以提供所需的尽可能多的可移植性和兼容性。直接使用 Exokernel 接口的应用程序将不具备可移植性，因为接口将包含硬件特定的信息。使用实现标准接口（例如 POSIX）的库操作系统的应用程序将在任何提供相同接口的系统上具有可移植性。在 Exokernel 上运行的应用程序可以自由地替换这些库操作系统，而无需任何特殊权限，这简化了新标准和功能的添加和开发。 与微内核系统一样，Exokernel 可以通过三种方式提供向后兼容性：一是操作系统和其程序的二进制仿真；二是在 Exokernel 之上实现其硬件抽象层；三是在 Exokernel 之上重新实现操作系统的抽象。 可移植性也是一个重要的因素吧，还是得使用标准库。 微内核又是什么，微内核是将服务转移到进程上的一种内核模式。宏内核是一种传统的内核结构，它将进程管理，内存管理等各项服务功能都放到内核中去，通常用在通用式的内核上，如 unix，linux 等。微内核的代表：Minix，在 Minix 中，操作系统的内核，内存管理，系统管理都有自己的进程表，每个部分的表包含了自己需要的域。表象是精确对应的，为了保持同步，在进程创建或结束时，这三个部分都要更新各自的表。 微内核怎么做进程管理，信息传递，开发难度是不是很高，更安全？崩溃不会影响别的？IPC 怎么做，性能是不是更差？ ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#library-operating-systems"},{"categories":["OS"],"collections":null,"content":"ExoKernel Design 引用 mit os 的课，但是很多嵌入式系统，例如 Minix，Cell，这些都是微内核设计。这两种设计都很流行，如果你从头开始写一个操作系统，你可能会从一个微内核设计开始。但是一旦你有了类似于 Linux 这样的宏内核设计，将它重写到一个微内核设计将会是巨大的工作。并且这样重构的动机也不足，因为人们总是想把时间花在实现新功能上，而不是重构他们的内核。 设计主要关注点是减少内核中的代码，它被称为 Micro Kernel Design（微内核）。在这种模式下，希望在 kernel mode 中运行尽可能少的代码。所以这种设计下还是有内核，但是内核只有非常少的几个模块，例如，内核通常会有一些 IPC 的实现或者是 Message passing；非常少的虚拟内存的支持，可能只支持了 page table；以及分时复用 CPU 的一些支持。 会很依赖 IPC？现在，对于任何文件系统的交互，都需要分别完成 2 次用户空间\u0026lt;-\u0026gt;内核空间的跳转。与宏内核对比，在宏内核中如果一个应用程序需要与文件系统交互，只需要完成 1 次用户空间\u0026lt;-\u0026gt;内核空间的跳转，所以微内核的的跳转是宏内核的两倍。通常微内核的挑战在于性能更差，这里有两个方面需要考虑： 在 user/kernel mode 反复跳转带来的性能损耗。 在一个类似宏内核的紧耦合系统，各个组成部分，例如文件系统和虚拟内存系统，可以很容易的共享 page cache。而在微内核中，每个部分之间都很好的隔离开了，这种共享更难实现。进而导致更难在微内核中得到更高的性能。 在将保护与管理分离的过程中，Exokernel 执行三项重要任务： tracking ownership of resources ensuring protection by guarding all resource usage or binding points, and revoking access to resources Exokernel 采用了三种技术。首先，使用安全绑定，库操作系统可以安全地绑定到机器资源。其次，可见撤销允许库操作系统参与资源撤销协议。第三，中止协议由 Exokernel 使用，通过强制手段打破不合作库操作系统的安全绑定。 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#exokernel-design"},{"categories":["OS"],"collections":null,"content":"Design Principles An exokernel specifies the details of the interface that library operating systems use to claim, release, and use machine resources. 安全地暴露硬件。导出的资源是底层硬件提供的资源：物理内存、CPU、磁盘内存、转换后备缓冲区（TLB）和地址上下文标识符。这一原则的动机是我们认为分布式、应用程序特定的资源管理是构建高效灵活系统的最佳方式。后续原则处理实现这一目标的细节。 暴露分配。 暴露名称。physical names 暴露撤销，利用可见的资源撤销协议， 尽可能地减少原语，暴露低级原语，同时保证安全和灵活， exokernel hands over resource policy decisions to library operating systems 资源管理也交给库系统？让 app 决定怎么使用资源， ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#design-principles"},{"categories":["OS"],"collections":null,"content":"Secure Bindings Exokernel 的主要任务之一是安全地复用资源，为相互不信任的应用程序提供保护。为了实现保护，Exokernel 必须保护每个资源。为了高效地完成这项任务，Exokernel 允许库操作系统使用安全绑定绑定到资源 没太理解什么是保护应用程序 使用三种基本技术来实现安全绑定：硬件机制、软件缓存和下载应用程序代码。 安全绑定可以在 Exokernel 中缓存。例如，Exokernel 可以使用大型软件 TLB [7, 28] 缓存不适合硬件 TLB 的地址转换。软件 TLB 可以被视为经常使用的安全绑定的缓存。 安全绑定可以通过将代码下载到内核中来实现。每次资源访问或事件时都会调用此代码以确定所有权和内核应执行的操作。将代码下载到内核中允许在发生内核事件时立即执行应用程序控制线程。下载代码的优点是可以避免潜在的昂贵交叉，并且此代码可以在不要求调度应用程序本身的情况下运行。类型安全语言 [9, 42]、解释和沙箱 [52] 可以用于安全地执行不受信任的应用程序代码 [21]。 Multiplexing Physical Memory 原型 Exokernel 中实现了物理内存的安全绑定， 当库操作系统分配一个物理内存页面时，Exokernel 通过记录所有者和库操作系统指定的读写能力来为该页面创建一个安全绑定。页面的所有者有权更改与其关联的能力并将其释放。特权机器操作（如 TLB 加载和 DMA）必须由 Exokernel 保护。根据 Exokernel 暴露内核簿记结构的原则，页表应在应用程序级别可见（只读）。 Multiplexing Network 网络解复用支持可以通过软件或硬件提供。硬件机制的一个例子是使用 ATM 信元中的虚拟电路将流安全地绑定到应用程序 [19]。软件支持可以通过包过滤器 [37] 提供消息解复用。包过滤器可以被视为安全绑定的实现，其中应用程序代码——过滤器——被下载到内核中。协议知识仅限于应用程序，而确定数据包所有权的保护检查以内核理解的语言表达。通过仔细的语言设计（以限制运行时）和运行时检查（以防止野指针和危险操作）确保故障隔离。 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#secure-bindings"},{"categories":["OS"],"collections":null,"content":"Downloading Code 除了实现安全绑定外，下载代码还可以用于提高性能。将代码下载到内核有两个主要的性能优势。第一个是显而易见的：消除内核交叉。 太多概念不懂了 ，周末有空看看 mit os ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#downloading-code"},{"categories":["OS"],"collections":null,"content":"Visible Resource Revocation Exokernel 对大多数资源使用可见撤销。即使在时间片结束时，处理器也会被显式撤销； 撤销是什么，上下文切换？ 在可见撤销中，操作系统或内核在回收资源之前会通知应用程序或库操作系统。应用程序有机会在资源被回收之前执行一些清理或保存操作。撤销（Revocation）是指操作系统或内核回收已经分配给应用程序或进程的资源的过程。这些资源可能包括物理内存、处理器时间片、文件描述符、网络连接等。撤销的目的是确保资源能够被重新分配给其他应用程序或进程，从而提高系统的整体资源利用率。 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#visible-resource-revocation"},{"categories":["OS"],"collections":null,"content":"Revocation and Physical Naming 我们将撤销过程视为 Exokernel 和库操作系统之间的对话。库操作系统应组织资源列表，以便可以快速释放资源。例如，库操作系统可以拥有一个简单的物理页面向量：当内核指示某些页面应被释放时，库操作系统选择其页面之一，将其写入磁盘，并释放它。 写入磁盘？ ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:5","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#revocation-and-physical-naming"},{"categories":["OS"],"collections":null,"content":"Abort Protocol 看不懂了 后面是 Aegis 的实现，和一些与 Ultrix 的性能比较 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:5:6","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#abort-protocol"},{"categories":["OS"],"collections":null,"content":"后记 不得不感叹自己读论文效率低下，浅显的看得慢，深入的看不懂，读起来这补补那补补，很容易就看不完 ","date":"2024-09-11","objectID":"/posts/exokernel-paper/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Exokernel","uri":"/posts/exokernel-paper/#后记"},{"categories":["Distributed"],"collections":null,"content":"Scalability! But at what COST? 比较短的 paper，之前也略有看过，主要讲主流分布式框架在多机器上存在 COST(不是成本) 问题，并不一定能比单机更好。是很有意思的主题。 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#scalability-but-at-what-cost"},{"categories":["Distributed"],"collections":null,"content":"Abstract new metric for big data platforms: COST Configuration that Outperforms a Single Thread, 超越单线程的配置 许多系统的 COST 出乎意料地大，通常需要数百个核心，或者在所有报告的配置中，其性能始终无法超越单线程。 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#abstract"},{"categories":["Distributed"],"collections":null,"content":"Introduction 可扩展性视为分布式数据处理平台最重要的特性，但很少有研究直接评估其系统在合理基准测试下的绝对性能。这些系统在多大程度上真正提升了性能，而不是仅仅并行化了它们自身引入的开销？ any system can scale arbitrarily well with a sufficient lack of care in its implementation. 消除了并行带来的开销，损害了可扩展性，但是提高了性能。（多核情况下 speed up 降低了，但是延迟却低了） ","date":"2024-09-08","objectID":"/posts/scalability-cost/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#introduction"},{"categories":["Distributed"],"collections":null,"content":"Methodology graph processing, many published systems have unbounded COST—i.e., no configuration outperforms the best single-threaded implementation—for all of the problems to which they have been applied In some cases the singlethreaded implementations are more than an order of mag- nitude faster than published results for systems using hundreds of cores. 许多老的系统很难超越单机的表现，这是为什么呢？作者是否消除了并行的开销，比如共识算法？再者说拿旧的系统现在来比，是不是有些不公平呢，优化什么的也不太一样。 不过比较了 pagerank 在不同系统的表现 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:3:1","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#methodology"},{"categories":["Distributed"],"collections":null,"content":"Basic Graph Computations 作者用的是单线程 C# 代码，有点神秘 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#basic-graph-computations"},{"categories":["Distributed"],"collections":null,"content":"PageRank 可以看到单机的性能反而优秀很多 论文是使用 SSD + RAM，有没有机械硬盘的比较呢 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#pagerank"},{"categories":["Distributed"],"collections":null,"content":"Connected Components 单线程还是快 他的单机性能到底如何呢？之前我也做了多 ec2 和单 ec2 的比较，核心较少的时候确实是有性能提升的，但随着核数增多，确实存在由于分片算法精度丢失导致的不均匀导致提升不是线性 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#connected-components"},{"categories":["Distributed"],"collections":null,"content":"Better Baselines ","date":"2024-09-08","objectID":"/posts/scalability-cost/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#better-baselines"},{"categories":["Distributed"],"collections":null,"content":"Improving graph layout 论文优化了其他的实现，比如图实现 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#improving-graph-layout"},{"categories":["Distributed"],"collections":null,"content":"Improving algorithms 优化算法，图联通，应该并不是适合并行的把 怎么还有并查集？ ","date":"2024-09-08","objectID":"/posts/scalability-cost/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#improving-algorithms"},{"categories":["Distributed"],"collections":null,"content":"Applying COST to prior work ","date":"2024-09-08","objectID":"/posts/scalability-cost/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#applying-cost-to-prior-work"},{"categories":["Distributed"],"collections":null,"content":"PageRank ","date":"2024-09-08","objectID":"/posts/scalability-cost/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#pagerank-1"},{"categories":["Distributed"],"collections":null,"content":"Graph connectivity ","date":"2024-09-08","objectID":"/posts/scalability-cost/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#graph-connectivity"},{"categories":["Distributed"],"collections":null,"content":"Lessons learned scalable systems design and implementation contribute to overheads and increased COST 其实到底做了什么导致 overhead 和 COST？是之前的研究基准测试不对吗，还是说这篇文章在图处理上有问题？ 那为什么大家还用 mapreduce, spark 呢？甚至现在越来越多分布式数据库呢 作者提到 MapReduce 存在许多磁盘写入，是持久化的，也就是说他的实验很可能都是内存的？ ","date":"2024-09-08","objectID":"/posts/scalability-cost/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#lessons-learned"},{"categories":["Distributed"],"collections":null,"content":"Future directions (for the area) 许多高性能的可扩展系统实例存在。Galois [17] 和 Ligra [23] 都是共享内存系统，当在单台机器上运行时，它们的表现显著优于其分布式同类系统。Naiad [16] 引入了一种新的通用数据流模型，甚至超越了专用系统。理解这些系统做得正确的地方以及如何改进它们，比在新的领域中重复现有想法并与先前工作中最差的部分进行比较更为重要。 ","date":"2024-09-08","objectID":"/posts/scalability-cost/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Scalability! But at what COST","uri":"/posts/scalability-cost/#future-directions-for-the-area"},{"categories":["OS"],"collections":null,"content":"The Multikernel: A New OS Architecture for Scalable Multicore Systems SOSP 09 的文章，提出了 MultiKernel 分布式系统，网络架构是如何做通信、消息传递的。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:1:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#the-multikernel-a-new-os-architecture-for-scalable-multicore-systems"},{"categories":["OS"],"collections":null,"content":"ABSTRACT 商用计算机越来越多的 processor cores 和多样化的架构，内存、互联 、指令集、IO 配置等等，以前的高性能计算机已经可以 scaled，但是现代 server workloads 是动态的，OS 是静态的？优化很难做？ 提出了 multikernel 架构，将机器看作网络/独立核心，假设底层没有 inter-core sharing，将传统 os 功能放到了分布式系统进程中，使用 message-passing 进行消息传递。 没看懂 motivation，难点到底是什么？解决了什么？ ","date":"2024-09-08","objectID":"/posts/multikernel-os/:2:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#abstract"},{"categories":["OS"],"collections":null,"content":"INTRODUCTION 硬件发展快于软件，scalability and correctness 带来了挑战。多核系统的 workloads 更加难以预测，更偏向于 os-intensive。 针对特定硬件调优通用 OS 不再能接受，硬件区别太大，当有新硬件时，优化会过时。 优化往往调参（内存一致性模型、缓存层次、成本），可移植性不高。 shared-memory kernel，使用锁来包含共享数据结构，本文将 OS 看作分布式系统的一个功能单元（用消息进行通信），遵循三个设计原则，所有内核间通信是显式的；OS 结构与硬件无关；状态是复制而不是共享。 如图是本文提出的 multikernel 架构 尽管是当前高效的 cache-coherent shared memory，使用消息传递 OS 也带来了好处，shared data structure 会受到串行、远程数据访问的影响，对远程数据进行管道 pipeline 和 batch messages encoding 可以让一个 core 加大吞吐和减少 interconnect utilization，也适用于各种异构架构。 本文的贡献： 设计了 multi-kernel 架构，显式消息传递，硬件无关，状态复制 基于架构设计了 Barrelfish OS？ 测量了 Barrelfish 满足可伸缩性和适应性。 将 OS 看作分布式，在我看来是非常夸张的想法，OS 本身就是和硬件紧密相连的，如果状态都是复制，都是通信，IO 带来的延迟谁能接受呢，那 RTOS 怎么做呢。 此外，我一直以为 CPU 的设计是现代硬件的基础，CPU 决定了架构，决定了 OS 长什么样。因为程序需要编译成指令集运行在 CPU 上，不同指令集可能效果不一样 消息传递又怎么保持一致性呢？OS 到底是基于硬件产生的？还是先有 OS 再有硬件？这根本不是先有鸡先有蛋的问题吧。 其实文章引出了一个很好的问题，共享内存 还是 消息传递。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:3:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#introduction"},{"categories":["OS"],"collections":null,"content":"MOTIVATIONS 现代计算机都有多核处理器，商业服务器已经有几百核的处理器，所以需要新的 OS 技术面对多核硬件，还是说商用 OS 只需要利用多核处理器的技术？ 本文认为 OS 在面对未来硬件的问题和用于高性能计算的 ccNUMA 和 SMP 不同（前者使用非统一内存访问和缓存一致性，访问远处的内存还是需要等待；后者是 Symmetric Multi-Processor 表示每个处理器都是对称的，没有主从关系，共享所有内存，但是存在竞争，所以扩展性较差） 非统一内存，内存分成多个节点，每个节点属于一个核心 缓存一致性，ccNUMA 所有处理器共享一个全局内存，需要缓存一致性保证所有处理器看到的内存数据是一致的。 MPP(Massive Parallel Processing) 是多个 SMP，是 share nothing 架构，扩展能力强，节点间信息交换是互联网通信 SMP 可以线性扩展，NUMA 理论可以无限扩展，但不是线性的，需要等待远处内存访问 看完 motivation 反而觉得文章合理了许多，文章注重点在于可扩展性 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#motivations"},{"categories":["OS"],"collections":null,"content":"Systems are increasingly diverse 和 hpc 不同，通用 OS 必须在多种硬件/系统设计中表现良好，所以无法对特定的 硬件进行优化 比如： Dice and Shavit show how a reader-writer lock can be built to exploit the shared, banked L2 cache on the Sun Niagara processor, using concurrent writes to the same cache line to track the presence of readers 特定处理器上，利用共享 L2 缓存实现高效读写锁，缓存行在 L2 不会频繁移动。但是这在传统多核处理器不高效，会移动缓存。 所以，OS 设计如果针对特定的同步机制，对不同的硬件可能无法适应。推出新硬件时，OS 可能却难以适应。 os 为了适应现代硬件，需要采用日渐复杂的优化方法（举例\u0026hellip;），linux readcopy update implementation 需要大量的迭代/ 本文针对可扩展性，OS 的可扩展性？但是这些硬件也不是一般商用，也不通用啊 Sun Niagara 是更加多核多线程，指令集也是特殊的 SPARC V9，也是商用的服务器才能用上吧，商用肯定会有厂家做专门的 OS？ 再说了，是开发新的硬件难 ，还是 OS 内核开发者做适配难呢？文章也举例一些 OS 的问题，优化很复杂等等，但是这例子 真的令人信服吗，win7, linux, windows server 2003 都是普遍性的，大量商用的，是有大量维护和使用的，再说 linux 开源也有不同发行版。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:1","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#systems-are-increasingly-diverse"},{"categories":["OS"],"collections":null,"content":"Cores are increasingly diverse 多样性。 Moreover, core heterogeneity means cores can no longer share a single OS kernel instance, either because the performance tradeoffs vary, or because the ISA is simply different 核心异构，不同核心不同架构，不同指令集，比如常见高通骁龙处理器，大核心小核心异构，还有图形 GPU 架构，也有 AMD 的 APU 架构也是 CPU GPU 异构。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:2","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#cores-are-increasingly-diverse"},{"categories":["OS"],"collections":null,"content":"The interconnect matters 尽管对于现代 cache-coherent multiprocessor，消息传递也替代了单个共享，因为可扩展性。CPU 之间的缓存一致性协议确保了 OS 可以安全地使用单个共享内存，但是像 路由 routing 和 congestion 拥塞这种网络问题是总所周知的。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:3","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#the-interconnect-matters"},{"categories":["OS"],"collections":null,"content":"Messages cost less than shared memory 共享内存之前被认为是性能更好，但消息传递现在反转了？ 实验中 4x4 核心的 AMD 随着核心数增大，shared memory 延迟也增大，线性增长，因为缓存丢失需要等待。 核心间延迟？ 但是对于消息传递，cache line 在服务端本地缓存，所以延迟不会线性增长。但是对于有时候延迟也高于共享内存 这个例子展示了在 cache-coherent shared memory 在少量核心上的可扩展问题，本文的条件就是共享内存模型存在不可/难以扩展的问题，并且硬件创新速度很快，为 OS 内核带来问题。 带来什么问题？可扩展问题？ OS kernel 不应该是性能和稳定优先，可移植性是很重要的指标吗？硬件更新速度真的遵从摩尔定律吗，而且硬件更新速度和可移植性应该关系并不是很大才对。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:4","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#messages-cost-less-than-shared-memory"},{"categories":["OS"],"collections":null,"content":"Cache coherence is not a panacea 核心数量增加，互连增加，硬件的 cache-coherence protocols 也会非常昂贵。所以未来的 OS 需要处理非一致性内存，或者能够绕过 cache-coherence protocol NIC 和 GPU 等可编程外围已经可以不需要和 CPU 保持缓存一致性，许多多核处理器也可以使用非一致性共享内存。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:5","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#cache-coherence-is-not-a-panacea"},{"categories":["OS"],"collections":null,"content":"Messages are getting easier 消息传递，是 shared nothing 的吗。共享数据存在正确性和性能缺陷，需要各种粒度的锁、数据结构来保证 cache line 竞争问题。 shared nothing 是一种分布式计算架构，尤其是分布式数据库中，也有数据仓库数据湖的趋势，每个节点有自己的资源，具有良好的可扩展性 ，性能优秀也灵活。但是实现难度大，资源利用率可能不足，数据格式也是个问题。 消息传递带来的另一个顾虑是，stack ripping (调用栈剥离问题)，和事件驱动带来的 control flow 混淆？但是，传统的 monolithic kernels 就是事件驱动的，尽管在多核处理器上。 最后，消息传递和事件驱动模型是编程范式。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:6","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#messages-are-getting-easier"},{"categories":["OS"],"collections":null,"content":"Discussion 未来计算机架构发展趋势：越来越多的核心数量、硬件多样化（内核之间、系统之间） we take the opposite approach: design and reason about the OS as a distributed, non-shared system, and then employ sharing to optimize the model where appropriate 应该是一个复合的，共享 + 消息架构，但这样和 NUMA 又有什么很大的差别呢 ？文章好像也没有深入讨论消息传递缓存一致性的问题， ","date":"2024-09-08","objectID":"/posts/multikernel-os/:4:7","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#discussion"},{"categories":["OS"],"collections":null,"content":"THE MULTIKERNEL MODEL Make all inter-core communication explicit Make OS structure hardware-neutral View state as replicated instead of shared ","date":"2024-09-08","objectID":"/posts/multikernel-os/:5:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#the-multikernel-model"},{"categories":["OS"],"collections":null,"content":"Make inter-core communication explicit 不共享内存，但是不排除程序在核心之间共享内存？OS 设计不依赖这个。 explicit communication 有助于系统互连。更加符合分布式系统，有利于 pipelining 和 batching。也能提供 isolation 和资源管理。 消息传递是异步的？发送后可以休眠、做别的事。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:5:1","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#make-inter-core-communication-explicit"},{"categories":["OS"],"collections":null,"content":"Make OS structure hardware-neutral 信息传递机制和硬件接口（CPU 和设备） 一些平台没有缓存一致性机制，比如嵌入式，RTOS，专用计算设备 GPU FPGA？ late binding + 消息传递，动态绑定，灵活，优化了 TLB shootdown 维护 TLB 一致性 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:5:2","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#make-os-structure-hardware-neutral"},{"categories":["OS"],"collections":null,"content":"View state as replicated 复制是可伸缩的重要技术 文章还是 share replicas of system states 不过是对于紧密耦合的核心或者线程，使用自旋锁保护同步。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:5:3","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#view-state-as-replicated"},{"categories":["OS"],"collections":null,"content":"Applying the model 缺点： 某些基于特定平台的性能优化可能被牺牲，比如 核心之间共享的 L2 缓存 复制需要保持一致性协议 Barrelfish 的目标： 性能和目前商用操作系统可以比较 在大量核心上可以扩展，尤其是在全局 OS data structure 下的 workload 可以使用不同的共享机制、定位到不同硬件，不需要重构 可以通过消息传递，通过 pipeline, batching 实现良好性能 OS 模块化，利用硬件拓扑或负载 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:5:4","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#applying-the-model"},{"categories":["OS"],"collections":null,"content":"IMPLEMENTATION ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#implementation"},{"categories":["OS"],"collections":null,"content":"Test platforms x86, 但是 ARM 却还在 wip？ 说好的可以扩展呢，而且实验看上去也不是异构处理器。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:1","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#test-platforms"},{"categories":["OS"],"collections":null,"content":"System structure OS instance on each core into a privileged-mode CPU driver and a distinguished user-mode monitor process, kernel space: cpu driver user space: monitor ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:2","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#system-structure"},{"categories":["OS"],"collections":null,"content":"CPU drivers CPU driver 是时间驱动、单线程、不可抢占的，它以 trap 的形式连续处理来自用户进程的事件或来自设备或其他核心的中断。 全是 X86-64 架构，想知道基于 ARM 开发 OS 的难度究竟如何。而且 LRPC 的延迟看上去蛮高的，一般 E5 应该在 us microseconds 级别，文章数据基本都是 nanoseconds。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:3","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#cpu-drivers"},{"categories":["OS"],"collections":null,"content":"Monitors 每个核心，复制的数据结构，比如内存分配表和地址映射表，都全局一致。是 monitor 运行一致性协议。 看上去 monitor 才是实现的核心，需要考虑复制、保持一致。其实这里能不能也实现一些零拷贝呢？ ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:4","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#monitors"},{"categories":["OS"],"collections":null,"content":"Process structure Communication in Barrelfish is not actually between processes but between dispatchers (and hence cores) ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:5","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#process-structure"},{"categories":["OS"],"collections":null,"content":"Inter-core communication 不同场景不同传输实现，但是本文核间通信只实现了缓存一致性？ inter-core 和 per-cores/dispatchers 消息传递还不一样，而且实际上并不是 processes 之间通信，是调度器 核间通信很重要，使用 URPC， urpc 好像基本只有国外教材才有了，不太清楚用户级别的 rpc 有什么区别 ，看上去利用了缓存，轮询，阻塞等等，那能不能改进成类似 epoll 等红黑树结构呢？ urpc 性能看上去挺不错的 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:6","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#inter-core-communication"},{"categories":["OS"],"collections":null,"content":"Memory management 虚拟内存管理 本来想仔细看看这部分，但看起来作者走了弯路，不知道最后是怎么实现的。文章提到的内存管理能力 capability 就是内存管理机制，通过能力引用和操作，标识内存对象，权限等等。同步怎么做？还需要 2PC 来同步吗？ ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:7","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#memory-management"},{"categories":["OS"],"collections":null,"content":"Shared address spaces 线程可以共享地址空间，这样不需要 IPC，但会面临数据不一致问题。 TLB 用于加速虚拟地址到物理地址。 Barrelfish 支持传统的进程模型，即多个调度器（dispatcher）在多个核心上共享单一的虚拟地址空间。 可以通过 共享所有调度器的硬件页表 TLB ，或者通过消息协议复制硬件页表来实现。 本文是怎么做的？ ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:8","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#shared-address-spaces"},{"categories":["OS"],"collections":null,"content":"Knowledge and policy engine 这一段不理解 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:9","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#knowledge-and-policy-engine"},{"categories":["OS"],"collections":null,"content":"Experiences 大部分是 RPC 调用而不是系统调用，需要更多的上下文切换 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:6:10","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#experiences"},{"categories":["OS"],"collections":null,"content":"EVALUATION ","date":"2024-09-08","objectID":"/posts/multikernel-os/:7:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#evaluation"},{"categories":["OS"],"collections":null,"content":"Case study: TLB shootdown 保持 TLB 一致性，TLB shootdown 是指在页面被取消映射时，通过使 TLB 条目失效来保持 TLB 一致性的过程。 本文使用消息传递，广播来实现 TLB shootdown，延迟会高一些。 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:7:1","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#case-study-tlb-shootdown"},{"categories":["OS"],"collections":null,"content":"Messaging performance 2 阶段提交、轮询、IP lookback 两阶段提交的问题在这里会不会更明显呢？尤其是当 monitor 阻塞的时候，很可能产生长时间的阻塞和单点故障，当然性能也难以接受 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:7:2","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#messaging-performance"},{"categories":["OS"],"collections":null,"content":"Compute-bound workloads 这些基准测试在任一操作系统上都没有特别好的扩展性，但至少证明了尽管 Barrelfish 具有分布式结构，它仍然可以支持大规模的共享地址空间并行代码，且性能损失很小。 性能差距并不是很大，至少超出了我的预期，我以为性能差距会很大。但很明显还是要看 latency 而不是只看 cycles、还需要看资源利用率、上下文切换、吞吐 吞吐量看着也不错， Web server and relational database 作为服务器系统，说是避免了内核-用户态切换，能够明显减少上下文切换，所以能每秒处理更多的请求？ 为什么？ ","date":"2024-09-08","objectID":"/posts/multikernel-os/:7:3","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#compute-bound-workloads"},{"categories":["OS"],"collections":null,"content":"SUMMARY microbenchmarks 结果看着不错 但是评估一个 os 是非常复杂的工作 文章没有真的在异构硬件上测试 ","date":"2024-09-08","objectID":"/posts/multikernel-os/:8:0","tags":["Paper Reading"],"title":"Paper Reading: The Multikernel: A New OS Architecture for Scalable Multicore Systems","uri":"/posts/multikernel-os/#summary"},{"categories":["Database"],"collections":null,"content":"Mini-LSM Week 1 Day3 Week1 Day3 的内容，实现 SST block 编解码和 iterator ","date":"2024-07-27","objectID":"/posts/minilsm-3/:1:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day3","uri":"/posts/minilsm-3/#mini-lsm-week-1-day3"},{"categories":["Database"],"collections":null,"content":"Task 1: Block Builder 前两章实现了 LSM 在内存中的结构，现在实现 on-disk 的结构，即 Block 块。 Blocks 一般 4-KB 大小（可能因存储介质不同），和操作系统以及 SSD 中的页 page 大小一致。块存储排序后的 key-value pairs。 SST 由多个 blocks 组成，当 memtables 的数量超过了 system limit，就会 flush memtables 成为 SST。这一章实现 encoding and decoding of a block. 需要修改的文件： src/block/builder.rs src/block.rs教程的 block 的编码格式为： ---------------------------------------------------------------------------------------------------- | Data Section | Offset Section | Extra | ---------------------------------------------------------------------------------------------------- | Entry #1 | Entry #2 | ... | Entry #N | Offset #1 | Offset #2 | ... | Offset #N | num_of_elements | ----------------------------------------------------------------------------------------------------每个 Entry 是 key-value 对： ----------------------------------------------------------------------- | Entry #1 | ... | ----------------------------------------------------------------------- | key_len (2B) | key (keylen) | value_len (2B) | value (varlen) | ... | -----------------------------------------------------------------------key_len 和 value_len 都是 2 个字节，所以最长长度为 216=655352^16 = 65535216=65535，一般使用 u16 表示 block 具有大小限制 target_size，除非第一个 key-value pair 超过了 block size，不然你需要保证编码后的 block size 小于或等于 target_size (提供的代码里，target_size 与 block_size 本质一样) 当 build 被调用时，BlockBuilder 会产生 data part 和 unencoded entry offsets。信息会被存在 Block 结构中，key-value entries 使用 raw 格式，offsets 用单独的 vector，这减少了解码数据时不必要的内存分配和处理开销，你只需要简单地拷贝 raw block data 到 data vector 并且每 2 个 bytes 进行 decode entry offsets，而不是创建 Vec\u0026lt;(Vec\u0026lt;u8\u0026gt;, Vec\u0026lt;u8\u0026gt;)\u0026gt; 这样的结构，在一个 block 和内存中去存所有的 key-value pairs。这样 compact memory layout 更高效。 在 Block::encode 和 Block::decode，你需要按照上述的结构 encode/decode block. ","date":"2024-07-27","objectID":"/posts/minilsm-3/:2:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day3","uri":"/posts/minilsm-3/#task-1-block-builder"},{"categories":["Database"],"collections":null,"content":"Task 1: Solution 查看 src/block/builder.rs 观察 Block 结构体，offsets Vec\u0026lt;u16\u0026gt; 偏移量， data vec\u0026lt;u8\u0026gt; kv 数据等等，具有 new(block_size: usize) -\u0026gt; Self, add(\u0026amp;mut self, key: KeySlice, value: \u0026amp;[u8]) -\u0026gt; bool, is_empty(\u0026amp;self) -\u0026gt; bool 和 build(self) -\u0026gt; Block 方法。 /// Builds a block. pub struct BlockBuilder { /// Offsets of each key-value entries. offsets: Vec\u0026lt;u16\u0026gt;, /// All serialized key-value pairs in the block. data: Vec\u0026lt;u8\u0026gt;, /// The expected block size. block_size: usize, /// The first key in the block first_key: KeyVec, } /// Creates a new block builder. pub fn new(block_size: usize) -\u0026gt; Self { Self { offsets: Vec::new(), data: Vec::new(), block_size, first_key: KeyVec::new(), } }先实现 new(block_size) 方法创建一个 BlockBuilder 结构体，需要注意这里使用了 bytes::BufMut 库，BufMut 是 bytes 库中的一个 trait，它定义了一些方法来操作可变的字节缓冲区。通过实现 BufMut，你可以为自定义类型添加一些方便的方法来操作字节数据。 BufMut trait 中定义了一个 put 方法，用于将数据写入缓冲区。这个方法可以用于多种类型，包括 u8、\u0026amp;[u8]、\u0026amp;str 等。此时 Vec 使用 put 也更合理，持批量写入缓冲区，减少内存复制。 然后实现 add 函数，向 Block 添加一个 key-value 值，首先需要知道数据的 offset 即存放的位置 self.data.len()，然后按照格式 key_len key value_len value 存入数据。但需要注意判断当前编码后的 Block 是否超过了 block_size（注意：除非第一个 key-value pair 超过了 block size）： /// Adds a key-value pair to the block. Returns false when the block is full. #[must_use] pub fn add(\u0026amp;mut self, key: KeySlice, value: \u0026amp;[u8]) -\u0026gt; bool { // data: [u8] | offsets: [u16] | num of elements: u16 if !self.is_empty() \u0026amp;\u0026amp; /* can store one large key */ self.data.len() + self.offsets.len() * 2 + 2 + /* current encode */ key.len() + 2 + value.len() + 2 + 2 /* key_len: 2b | key: keylen | value_len: 2b | value: val_len + offset */ \u0026gt; self.block_size { return false; } if self.offsets.is_empty() { self.first_key = key.to_key_vec(); } self.data.put_u16(key.len() as u16); self.data.put(key.into_inner()); self.data.put_u16(value.len() as u16); self.data.put(value); self.offsets.push(self.data.len() as u16); true } /// Check if there is no key-value pair in the block. pub fn is_empty(\u0026amp;self) -\u0026gt; bool { self.offsets.is_empty() } /// Finalize the block. pub fn build(self) -\u0026gt; Block { Block { offsets: self.offsets, data: self.data, } }所以检查 Block::is_empty 是否为空就很简单了，只需要检查 offset 数组是不是空的，同时 build 就直接返回当前的 Block 带上 offset 和 data 然后实现 Block::encode 编码数据，按照 data offset 这种编码格式 encode，即教程中的 拷贝 raw block data 到 data vector 并且每 2 个 bytes 进行 decode entry offsets，由于此时的 data 是 Vec\u0026lt;u8\u0026gt; 类型，需要转成 Bytes 返回，可以直接使用 .into() 方法。但是教程提到只需要一个 Vec\u0026lt;u8\u0026gt;，所以需要将 offsets 也塞到 data 里去： impl Block { /// Encode the internal data to the data layout illustrated in the tutorial /// Note: You may want to recheck if any of the expected field is missing from your output pub fn encode(\u0026amp;self) -\u0026gt; Bytes { let mut buf = self.data.clone(); for offset in \u0026amp;self.offsets { buf.put_u16(*offset); } // num of elements buf.put_u16(self.offsets.len() as u16); buf.into() } /// Decode from the data layout, transform the input `data` to a single `Block` pub fn decode(data: \u0026amp;[u8]) -\u0026gt; Self { let offsets_len = (\u0026amp;data[data.len() - 2..data.len()]).get_u16() as usize; let offsets = (\u0026amp;data[data.len() - 2 - offsets_len * 2..data.len() - 2]) .chunks(2) .map(|mut chunk| chunk.get_u16()) .collect(); let data = data[..data.len() - 2 - offsets_len * 2].to_vec(); Self { data, offsets } } }相应的，decode 也是从一个 data: \u0026amp;[u8] 取得所有的 data 和相应的 offset，从缓冲区最后一个 u16 可以获得 offset 数组的长度 代码里的所有 2 都是 u16 的长度，更规范应该使用常量来表示，比如 pub(crate) const SIZEOF_U16: usize = std::mem::size_of::\u0026lt;u16\u0026gt;(); ","date":"2024-07-27","objectID":"/posts/minilsm-3/:3:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day3","uri":"/posts/minilsm-3/#task-1-solution"},{"categories":["Database"],"collections":null,"content":"Task 2: Block Iterator 修改 src/block/iterator.rs，这一小节，因为有了 encoded block，需要实现 BlockIterator 接口，使得用户可以 lookup/scan blocks 里的 keys。 BlockIterator 可以被 Arc\u0026lt;Block\u0026gt; 实现，如果 create_and_seek_to_first 被调用，它会放在 block 的第一个 key。如果 create_and_seek_to_key 被调用，iterator 会被放在第一个 \u0026gt;= 大于等于相应 key 的位置，比如 1, 3, 5 在一个 Block 时 let mut iter = BlockIterator::create_and_seek_to_key(block, b\u0026#34;2\u0026#34;); // 创建 key 2 assert_eq!(iter.key(), b\u0026#34;3\u0026#34;); // 此时 iterator 位置在第一个大于等于 2 的位置即 3 的位置 上面的 seek 2 将使迭代器定位在下一个可用键 2，在本例中为 3。 iterator 应该从 block 拷贝 key 并且存到 iterator 本身（未来会有 key compression 压缩的内容），对于值 value，必须在 iterator 存储起始/结束 begin/end offset 偏移，并且不能拷贝。 当 next 被调用，iterator 会移动到下一个位置。如果抵达 block 结束位置，可以设置 key 为空然后从 is_valid 返回 false，这样调用者可以切换到另外的 block。 ","date":"2024-07-27","objectID":"/posts/minilsm-3/:4:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day3","uri":"/posts/minilsm-3/#task-2-block-iterator"},{"categories":["Database"],"collections":null,"content":"Task 2: Solution 本节需要实现的函数较多，先观察 BlockIterator::new 构造函数，传入一个 Arc\u0026lt;Block\u0026gt;，但观察测试，基本都没有使用这个构造函数而是使用 create_and_... 等函数。 所以 create_and_seek_to_first(block: Arc\u0026lt;Block\u0026gt;) 和 create_and_seek_to_key(block: Arc\u0026lt;Block\u0026gt;, key: KeySlice) 都接受一个原子计数引用，调用构造函数，调用相应函数，返回迭代器： pub fn create_and_seek_to_first(block: Arc\u0026lt;Block\u0026gt;) -\u0026gt; Self { let mut iter = Self::new(block); iter.seek_to_first(); iter } /// Creates a block iterator and seek to the first key that \u0026gt;= `key`. pub fn create_and_seek_to_key(block: Arc\u0026lt;Block\u0026gt;, key: KeySlice) -\u0026gt; Self { let mut iter = Self::new(block); iter.seek_to_key(key); iter }对于 key value 函数，直接返回当前的 entry: /// Returns the key of the current entry. pub fn key(\u0026amp;self) -\u0026gt; KeySlice { self.key.as_key_slice() } /// Returns the value of the current entry. pub fn value(\u0026amp;self) -\u0026gt; \u0026amp;[u8] { \u0026amp;self.block.data[self.value_range.0..self.value_range.1] } /// Returns true if the iterator is valid. /// Note: You may want to make use of `key` pub fn is_valid(\u0026amp;self) -\u0026gt; bool { !self.key.is_empty() }","date":"2024-07-27","objectID":"/posts/minilsm-3/:5:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day3","uri":"/posts/minilsm-3/#task-2-solution"},{"categories":["Database"],"collections":null,"content":"DuckDB: an Embeddable Analytical Database 读一下 DuckDB 的论文，对 OLAP 没什么了解，但论文很短也可以看看。 ","date":"2024-06-07","objectID":"/posts/duckdb-2019/:1:0","tags":["Paper Reading"],"title":"Paper Reading: DuckDB: an Embeddable Analytical Database","uri":"/posts/duckdb-2019/#duckdb-an-embeddable-analytical-database"},{"categories":["Database"],"collections":null,"content":"ABSTRACT SQLite 应用很广，unobtrusive in-process data management 是有必要的，但目前没有系统做 这样的 analytical workloads。 DuckDB 提出了 analytical SQL queries while embedded in another process. DuckDB 开源，使用 CPP 实现：DuckDB Github 等有空可以学学 SQLite，是怎么做到体积小但是又应用这么广泛的。 unobtrusive 表示什么？不引人注目？ embbed database 和 database server 的区别在于，前者不属于服务器，体积小，和应用程序运行在同一进程。后者服务器独立运行。 ","date":"2024-06-07","objectID":"/posts/duckdb-2019/:2:0","tags":["Paper Reading"],"title":"Paper Reading: DuckDB: an Embeddable Analytical Database","uri":"/posts/duckdb-2019/#abstract"},{"categories":["Database"],"collections":null,"content":"INTRODUCTION 数据处理现在有许多 large monolithic database servers running as stand-alone processes，是因为需要解决多客户端，高并发带来的数据完整性问题。 而嵌入式数据库完全不同，只需要嵌入到宿主进程。比如专注事务处理 OLTP 的 SQLite，使用 B-Tree 和行存作为存储引擎，所以在 OLAP 表现不好。 可嵌入的 OLAP 是有必要的：1. Interactive data analysis 2. edge computing 比如 R, Python 进行数据分析，缺少查询优化和事务处理。边缘计算用嵌入数据库可以做一些在线分析，论文举了个 power meters 带宽限制的例子。 论文分析了几个嵌入式 OLAP 数据库的需求： 高效率 OLAP 工作负载，但不完全牺牲 OLTP 性能：比如 dashboard 中的数据并发修改，OLAP 进行可视化，OLTP 进行数据更新 高效地从 tables 到 database 的传输：嵌入式数据库和应用在同一个进程和地址空间，可以高效地实现 data sharing 高度稳定性：如果嵌入数据库崩溃，比如 OOM 可能会把宿主进程都带走，所以查询如果用完了资源，必须是可以 aborted cleanly 的，而且系统需要可以 gracefully 适应资源竞争。 实用的可嵌入性和可移植性，数据库需要可以运行在不论主机运行在的任何环境。外部依赖是有问题的、信号处理也不行。 论文提出了 DuckDB，开源的，可嵌入的关系型数据库。具有完整的 SQL 接口。 ","date":"2024-06-07","objectID":"/posts/duckdb-2019/:3:0","tags":["Paper Reading"],"title":"Paper Reading: DuckDB: an Embeddable Analytical Database","uri":"/posts/duckdb-2019/#introduction"},{"categories":["Database"],"collections":null,"content":"DESIGN AND IMPLEMENTATION DuckDB 设计目的：嵌入式分析，进行了组件分离：parser, logical planner, optimizer, physical planner, execution engine. 以及 Orthogonal components: transaction, storage managers. 作为嵌入数据库，DuckDB 没有客户端协议接口或者服务器进程，而是使用 C/C++ API 访问。此外，DuckDB 提供了一个 SQLite 兼容层，可以让使用 SQLite 的程序通过 re-linking 或者 library overloading 连接。 DuckDB SQL parser 衍生自 Postgres\u0026rsquo; SQL parser，并且尽可能地分离了。可以为 DuckDB 提供全功能、稳定的解析器，可以处理最不稳定的输入形式：SQL 查询。解析器将 SQL 查询字符串作为输入，并且返回一个解析树（C 语言结构）。解析树会立即被转成 DuckDB 自己的解析树（C++ 类）以限制 Postgres 数据结构的范围。 为什么要 limit the reach of Pg\u0026rsquo;s data structure? logical planner 逻辑规划器具有两个部分：binder 和 plan generator。binder 解析表达式和其引用的 schema object 比如表、视图的列名和类型。logical planner generator 将解析树转换成 tree of logical query operators 比如 scan, filter, project 等等。在 planning 阶段后，就有了 fully type-resolved logical query plan. DuckDB 保留了存储数据的统计信息，这些数据会在 planning 阶段通过不同的表达式树传播 propagate. 这些统计信息会在 optimizer 中使用，并且也可以用来在升级类型时做预防整型溢出。 DuckDB optimizer 使用 dynamic programming 动态规划实现 join order optimization，使用贪心作为 fallback 处理复杂的 join graphs. It performs flattening of arbitrary subqueries as described in Neumann et al. [9]. Thomas Neumann and Alfons Kemper. 2015. Unnesting Arbitrary Queries. In Datenbanksysteme für Business, Technologie und Web (BTW), 16. Fachtagung des GI-Fachbereichs \u0026ldquo;Datenbanken und Informationssys- teme\u0026rdquo; (DBIS), 4.-6.3.2015 in Hamburg, Germany. Proceedings. 没看懂这里 此外，duckdb 还有一系列重写的规则，可以简化表达式树，比如 common subexpression elimination, constant folding. 基数估计 cardinality estimation 使用了采样和 HyperLog 实现。此过程的结果是查询的优化逻辑计划 optimized plan for the query. The physical planner 物理规划器将逻辑规划转成物理规划，选择合适的实现方法。比如，scan 可能决定使用现有的索引而不是根据 selectivity estimates 扫描 base tables, 或者根据 join 谓词选择 hash join 还是 merge join。 DuckDB 使用了 vectorized interpreted execution engine. 使用了 SQL 查询 JIT 即时编译，有利于可移植性。JIT 引擎依赖于大量的编译库（比如 LLVM），DuckDB 使用了固定最大值的向量（默认值 1024）。固定长度类型的值，比如整数可以表示为原生数组 native arrays. 可变长度值比如字符串，可以表示为原生数组指针 into a separate string heap. NULL 值使用单独的位向量表示，当 NULL 值出现在向量时才存在。 这允许 fast intersection (逻辑乘) of NULL vectors for binary vector operation and avoids redundant computation\u0026hellip;. Peter A. Boncz, Marcin Zukowski, and Niels Nes. 2005. MonetDB/X100: Hyper-Pipelining Query Execution. In CIDR 2005, Second Biennial Conference on Innovative Data Systems Research, Asilomar, CA, USA, January 4-7, 2005. 225–237. 现代查询引擎的两种优化方式 - Vectorization and Compilation，有机会可以看看 执行引擎使用 火山模型 执行查询，查询执行通过物理计划的根节点拉取第一个 \u0026ldquo;chunk\u0026rdquo; 的数据。 chunk 是结果集合、查询中间结果或 base table 的一个水平子集。节点会递归地从子节点拉取 chunks，最终达到 scan operator，通过读持续化的表来产生 chunks. 过程持续到根节点为空，此时查询就完成了。 DuckDB 通过 MVCC 提供 ACID，实现了 HyPer\u0026rsquo;s 可串行化 MVCC 的变种，为 OLAP/OLTP 系统量身定做。这个变种会原地立刻更新数据，并且会单独的 undo buffer 保存之前的状态，用来进行并发事务和 abort。DuckDB 选择了 MVCC 和简单的 schemes 比如 Optimistic Concurrency Control。尽管 DuckDB 用于 analytics，但并行修改 tables ","date":"2024-06-07","objectID":"/posts/duckdb-2019/:4:0","tags":["Paper Reading"],"title":"Paper Reading: DuckDB: an Embeddable Analytical Database","uri":"/posts/duckdb-2019/#design-and-implementation"},{"categories":["Notes"],"collections":null,"content":"Go 并发编程实战 https://time.geekbang.org/column/intro/100061801 挺好的实战教程 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:1:0","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#go-并发编程实战"},{"categories":["Notes"],"collections":null,"content":"资源并发访问问题 使用 10 个线程对变量 counter 进行增加，每个增加 10000，结果最后是 10 * 10000 吗？ ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:2:0","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#资源并发访问问题"},{"categories":["Notes"],"collections":null,"content":"互斥锁 临界区就是一个被共享的资源。为了避免竞争导致并发结果不对，可以使用互斥锁，限定临界区只能同时由一个线程持有，比如 Mutex 锁。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:2:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#互斥锁"},{"categories":["Notes"],"collections":null,"content":"Race Detector Go race detector 基于 Google 的 C/C++ sanitizers 技术实现，在编译期检测对共享变量的非同步访问。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:2:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#race-detector"},{"categories":["Notes"],"collections":null,"content":"Mutex 实现原理 最初的 Mutex 使用信号量控制 goroutine 的阻塞休眠和唤醒 + flag 字段标记是否持有锁（CAS 进行判断，0 未持有，1 锁被持有且无等待，n 锁被持有且有 n-1 个等待） Go 可以使用 defer 来释放锁 初期的 Mutex 存在几个问题：排队请求锁需要上下文切换，性能不好 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:0","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#mutex-实现原理"},{"categories":["Notes"],"collections":null,"content":"State 字段 2011 年调整 Mutex 的 state 字段为 int32，第一位表示锁是否被持有，第二位表示唤醒的 goroutine，第三部分是阻塞等待的数量。 获取锁 CAS 检测 state 是否为 0，是则 Swap 成 mutexLocked 状态 atomic.CompareAndSwapInt32(\u0026amp;m.state, 0, mutexLocked) \u0026mdash;\u0026gt; Fast Path 获取锁 检测旧状态并且加锁 old | mutexLocked，如果加锁失败则进入等待 new = old + 1 \u0026lt;\u0026lt; mutexWaiterShift 如果被唤醒则消除 mutexWoken 状态 new \u0026amp;^= mutexWoken CAS 旧状态进入新状态，如果未被加锁则继续循环，否则请求信号量唤醒。 用位运算来判断状态。 释放锁 atomic.AddInt32(\u0026amp;m.state, -mutexLocked) 去掉锁标志 检测旧状态，如果没有等待者，则退出 如果有等待者，设置唤醒标志 注：Unlock 方法可以被任意的 goroutine 调用释放锁，即使是没持有这个互斥锁的 goroutine，也可以进行这个操作。Mutex 本身并没有包含持有这把锁的 goroutine 的信息，所以，Unlock 也不会对此进行检查。Mutex 的这个设计一直保持至今。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#state-字段"},{"categories":["Notes"],"collections":null,"content":"State 字段 2011 年调整 Mutex 的 state 字段为 int32，第一位表示锁是否被持有，第二位表示唤醒的 goroutine，第三部分是阻塞等待的数量。 获取锁 CAS 检测 state 是否为 0，是则 Swap 成 mutexLocked 状态 atomic.CompareAndSwapInt32(\u0026amp;m.state, 0, mutexLocked) \u0026mdash;\u0026gt; Fast Path 获取锁 检测旧状态并且加锁 old | mutexLocked，如果加锁失败则进入等待 new = old + 1 \u0026lt;\u0026lt; mutexWaiterShift 如果被唤醒则消除 mutexWoken 状态 new \u0026amp;^= mutexWoken CAS 旧状态进入新状态，如果未被加锁则继续循环，否则请求信号量唤醒。 用位运算来判断状态。 释放锁 atomic.AddInt32(\u0026amp;m.state, -mutexLocked) 去掉锁标志 检测旧状态，如果没有等待者，则退出 如果有等待者，设置唤醒标志 注：Unlock 方法可以被任意的 goroutine 调用释放锁，即使是没持有这个互斥锁的 goroutine，也可以进行这个操作。Mutex 本身并没有包含持有这把锁的 goroutine 的信息，所以，Unlock 也不会对此进行检查。Mutex 的这个设计一直保持至今。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#获取锁"},{"categories":["Notes"],"collections":null,"content":"State 字段 2011 年调整 Mutex 的 state 字段为 int32，第一位表示锁是否被持有，第二位表示唤醒的 goroutine，第三部分是阻塞等待的数量。 获取锁 CAS 检测 state 是否为 0，是则 Swap 成 mutexLocked 状态 atomic.CompareAndSwapInt32(\u0026amp;m.state, 0, mutexLocked) \u0026mdash;\u0026gt; Fast Path 获取锁 检测旧状态并且加锁 old | mutexLocked，如果加锁失败则进入等待 new = old + 1 \u0026lt;\u0026lt; mutexWaiterShift 如果被唤醒则消除 mutexWoken 状态 new \u0026amp;^= mutexWoken CAS 旧状态进入新状态，如果未被加锁则继续循环，否则请求信号量唤醒。 用位运算来判断状态。 释放锁 atomic.AddInt32(\u0026amp;m.state, -mutexLocked) 去掉锁标志 检测旧状态，如果没有等待者，则退出 如果有等待者，设置唤醒标志 注：Unlock 方法可以被任意的 goroutine 调用释放锁，即使是没持有这个互斥锁的 goroutine，也可以进行这个操作。Mutex 本身并没有包含持有这把锁的 goroutine 的信息，所以，Unlock 也不会对此进行检查。Mutex 的这个设计一直保持至今。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#释放锁"},{"categories":["Notes"],"collections":null,"content":"自旋 在 2011 年的基础上，不论是新来的 goroutine 还是被唤醒的，都会尝试请求锁且可以自旋 runtime.canSpin(iter) 缓解了饥饿问题。 目前的 Mutex 源码非常复杂 mutex.go 分成了 LockFast 和 LockSlow，也加入了超时的一些判断，都在解决饥饿问题。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#自旋"},{"categories":["Notes"],"collections":null,"content":"饥饿模式、正常模式 被唤醒的 goroutine 和新来的 goroutine 需要竞争，会导致饥饿产生。如果 waiter 获取不到锁的时间超过了阈值（1ms），Mutex 进入饥饿模式。 饥饿模式：Mutex 拥有者会直接把锁交给队列最前面的等待者，新来的 goroutine 不会尝试获取锁，不自旋，加入等待队列尾部。 正常模式：如果 waiter 已经是队列最后一个，没有其他等待锁的 goroutine，或者 waiter 等待时间小于阈值（1ms）。 正常模式具有更好的性能。 const ( mutexLocked = 1 \u0026lt;\u0026lt; iota // iota 0, 1 \u0026lt;\u0026lt; 0 = 1 mutexWoken // iota 1, 1 \u0026lt;\u0026lt; 1 = 2 mutexStarving // iota 2, 1 \u0026lt;\u0026lt; 2 = 4 mutexWaiterShift = iota // iota 3 starvationThresholdNs = 1e6 )这种用法是 Constant declarations ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:3:3","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#饥饿模式正常模式"},{"categories":["Notes"],"collections":null,"content":"Mutex 易错场景 Lock/Unlock 不是成对出现，会产生死锁，或 Unlock 一个未加锁的 Mutex 而导致 panic。（太多 if-else/重构/误写/\u0026hellip;） 重入，Java 中有 ReentrantLock 可重入锁，而 Mutex 是不可重入锁。 Lock() 之后不可以再 Lock()。 死锁，避免死锁的四个条件：互斥、拥有和等待、不可剥夺、环路等待。Go 运行时有死锁检测功能 Copy 已使用的 Mutex，sync 库的同步原语在使用后是不能复制的（Mutex 是有状态的对象，state 记录锁状态），复制使用后的 Mutex 会直接加锁。避免值拷贝传入函数参数，go vet 可以检测这种情况，使用 copylock 分析器静态分析。 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:0","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#mutex-易错场景"},{"categories":["Notes"],"collections":null,"content":"Go 实现可重入锁 锁需要记住当前是哪个 goroutine 持有该锁 goroutine id 通过 runtime.Stack 获取栈帧信息和 goroutine id，比如 goroutine 1 [running] 通过字符串处理得到 goroutine id。或者直接调用 petermattis/goid 等第三方库。 type RecursiveMutex struct { sync.Mutex owner int64 // 当前持有锁的 goroutine id recursion int32 // 这个 goroutine 重入的次数 } func (m *RecursiveMutex) Lock() { gid := goid.Get() // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入 if atomic.LoadInt64(\u0026amp;m.owner) == gid { m.recursion++ return } m.Mutex.Lock() // 获得锁的goroutine第一次调用，记录下它的goroutine id,调用次数加1 atomic.StoreInt64(\u0026amp;m.owner, gid) m.recursion = 1 } func (m *RecursiveMutex) Unlock() { gid := goid.Get() // 非持有锁的goroutine尝试释放锁，错误的使用 if atomic.LoadInt64(\u0026amp;m.owner) != gid { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.owner, gid)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.owner, -1) m.Mutex.Unlock() }Token Go 开发者没有暴露 goroutine id，开发者可以自己提供 token type RecursiveMutex struct { sync.Mutex token int64 recursion int32 } func(m *RecursiveMutex) Lock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) == token { m.recursion++ return } m.Mutex.Lock() atomic.StoreInt64(\u0026amp;m.token, token) m.recursion = 1 } func(m *RecursiveMutex) Unlock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) != token { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.token, token)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.token, 0) m.Mutex.Unlock() }","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#go-实现可重入锁"},{"categories":["Notes"],"collections":null,"content":"Go 实现可重入锁 锁需要记住当前是哪个 goroutine 持有该锁 goroutine id 通过 runtime.Stack 获取栈帧信息和 goroutine id，比如 goroutine 1 [running] 通过字符串处理得到 goroutine id。或者直接调用 petermattis/goid 等第三方库。 type RecursiveMutex struct { sync.Mutex owner int64 // 当前持有锁的 goroutine id recursion int32 // 这个 goroutine 重入的次数 } func (m *RecursiveMutex) Lock() { gid := goid.Get() // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入 if atomic.LoadInt64(\u0026amp;m.owner) == gid { m.recursion++ return } m.Mutex.Lock() // 获得锁的goroutine第一次调用，记录下它的goroutine id,调用次数加1 atomic.StoreInt64(\u0026amp;m.owner, gid) m.recursion = 1 } func (m *RecursiveMutex) Unlock() { gid := goid.Get() // 非持有锁的goroutine尝试释放锁，错误的使用 if atomic.LoadInt64(\u0026amp;m.owner) != gid { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.owner, gid)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.owner, -1) m.Mutex.Unlock() }Token Go 开发者没有暴露 goroutine id，开发者可以自己提供 token type RecursiveMutex struct { sync.Mutex token int64 recursion int32 } func(m *RecursiveMutex) Lock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) == token { m.recursion++ return } m.Mutex.Lock() atomic.StoreInt64(\u0026amp;m.token, token) m.recursion = 1 } func(m *RecursiveMutex) Unlock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) != token { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.token, token)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.token, 0) m.Mutex.Unlock() }","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#goroutine-id"},{"categories":["Notes"],"collections":null,"content":"Go 实现可重入锁 锁需要记住当前是哪个 goroutine 持有该锁 goroutine id 通过 runtime.Stack 获取栈帧信息和 goroutine id，比如 goroutine 1 [running] 通过字符串处理得到 goroutine id。或者直接调用 petermattis/goid 等第三方库。 type RecursiveMutex struct { sync.Mutex owner int64 // 当前持有锁的 goroutine id recursion int32 // 这个 goroutine 重入的次数 } func (m *RecursiveMutex) Lock() { gid := goid.Get() // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入 if atomic.LoadInt64(\u0026amp;m.owner) == gid { m.recursion++ return } m.Mutex.Lock() // 获得锁的goroutine第一次调用，记录下它的goroutine id,调用次数加1 atomic.StoreInt64(\u0026amp;m.owner, gid) m.recursion = 1 } func (m *RecursiveMutex) Unlock() { gid := goid.Get() // 非持有锁的goroutine尝试释放锁，错误的使用 if atomic.LoadInt64(\u0026amp;m.owner) != gid { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.owner, gid)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.owner, -1) m.Mutex.Unlock() }Token Go 开发者没有暴露 goroutine id，开发者可以自己提供 token type RecursiveMutex struct { sync.Mutex token int64 recursion int32 } func(m *RecursiveMutex) Lock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) == token { m.recursion++ return } m.Mutex.Lock() atomic.StoreInt64(\u0026amp;m.token, token) m.recursion = 1 } func(m *RecursiveMutex) Unlock(token int64) { if atomic.LoadInt64(\u0026amp;m.token) != token { panic(fmt.Sprintf(\u0026#34;wrong the owner(%d): %d!\u0026#34;, m.token, token)) } // 调用次数减1 m.recursion-- if m.recursion != 0 { // 如果这个goroutine还没有完全释放，则直接返回 return } // 此goroutine最后一次调用，需要释放锁 atomic.StoreInt64(\u0026amp;m.token, 0) m.Mutex.Unlock() }","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:1","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#token"},{"categories":["Notes"],"collections":null,"content":"流行项目 issues Docker (Moby) Moby #36114 hotAddVHDsAtStart 方法对 serviceVM 进行 VHD 的添加，并且会对 svm 加锁，如果添加 VHS 失败会调用 hotRemoveVHDsAtStart 方法去掉 VHD，而这个方法也进行了 svm.Lock() 因为 Mutex 不可重入，会导致死锁，所以改成了 hotRemoveVHDsNoLock 不用锁的方法来移除 VHD。 Moby #34881 这个 PR 在 if 判断后 return 却没有释放锁 其他还有 36840、37583、35517、35482、33305、32826、30696、29554、29191、28912、26507 等 issues。 Kubernetes Kubernetes #45192 忘记 Unlock 导致死锁 Kubernetes #72361 使用 mutex 解决竞争问题 Kubernetes #71617 使用 mutex 解决竞争问题 Kubernetes #70605 使用 mutex 解决竞争问题 gRPC-go gRPC-go #795 Unlock() 写错了？所以还是 defer unlock 合理。 gRPC-go #1318 添加 Lock 防止 data races gRPC-go #2074 添加 Lock 防止 data races etcd Distributed reliable key-value store for the most critical data of a distributed system etcd #10419 etcd Compact 方法调用的时候，使用了 mu.Lock() 而 Store 方法也会调用 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#流行项目-issues"},{"categories":["Notes"],"collections":null,"content":"流行项目 issues Docker (Moby) Moby #36114 hotAddVHDsAtStart 方法对 serviceVM 进行 VHD 的添加，并且会对 svm 加锁，如果添加 VHS 失败会调用 hotRemoveVHDsAtStart 方法去掉 VHD，而这个方法也进行了 svm.Lock() 因为 Mutex 不可重入，会导致死锁，所以改成了 hotRemoveVHDsNoLock 不用锁的方法来移除 VHD。 Moby #34881 这个 PR 在 if 判断后 return 却没有释放锁 其他还有 36840、37583、35517、35482、33305、32826、30696、29554、29191、28912、26507 等 issues。 Kubernetes Kubernetes #45192 忘记 Unlock 导致死锁 Kubernetes #72361 使用 mutex 解决竞争问题 Kubernetes #71617 使用 mutex 解决竞争问题 Kubernetes #70605 使用 mutex 解决竞争问题 gRPC-go gRPC-go #795 Unlock() 写错了？所以还是 defer unlock 合理。 gRPC-go #1318 添加 Lock 防止 data races gRPC-go #2074 添加 Lock 防止 data races etcd Distributed reliable key-value store for the most critical data of a distributed system etcd #10419 etcd Compact 方法调用的时候，使用了 mu.Lock() 而 Store 方法也会调用 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#docker-moby"},{"categories":["Notes"],"collections":null,"content":"流行项目 issues Docker (Moby) Moby #36114 hotAddVHDsAtStart 方法对 serviceVM 进行 VHD 的添加，并且会对 svm 加锁，如果添加 VHS 失败会调用 hotRemoveVHDsAtStart 方法去掉 VHD，而这个方法也进行了 svm.Lock() 因为 Mutex 不可重入，会导致死锁，所以改成了 hotRemoveVHDsNoLock 不用锁的方法来移除 VHD。 Moby #34881 这个 PR 在 if 判断后 return 却没有释放锁 其他还有 36840、37583、35517、35482、33305、32826、30696、29554、29191、28912、26507 等 issues。 Kubernetes Kubernetes #45192 忘记 Unlock 导致死锁 Kubernetes #72361 使用 mutex 解决竞争问题 Kubernetes #71617 使用 mutex 解决竞争问题 Kubernetes #70605 使用 mutex 解决竞争问题 gRPC-go gRPC-go #795 Unlock() 写错了？所以还是 defer unlock 合理。 gRPC-go #1318 添加 Lock 防止 data races gRPC-go #2074 添加 Lock 防止 data races etcd Distributed reliable key-value store for the most critical data of a distributed system etcd #10419 etcd Compact 方法调用的时候，使用了 mu.Lock() 而 Store 方法也会调用 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#kubernetes"},{"categories":["Notes"],"collections":null,"content":"流行项目 issues Docker (Moby) Moby #36114 hotAddVHDsAtStart 方法对 serviceVM 进行 VHD 的添加，并且会对 svm 加锁，如果添加 VHS 失败会调用 hotRemoveVHDsAtStart 方法去掉 VHD，而这个方法也进行了 svm.Lock() 因为 Mutex 不可重入，会导致死锁，所以改成了 hotRemoveVHDsNoLock 不用锁的方法来移除 VHD。 Moby #34881 这个 PR 在 if 判断后 return 却没有释放锁 其他还有 36840、37583、35517、35482、33305、32826、30696、29554、29191、28912、26507 等 issues。 Kubernetes Kubernetes #45192 忘记 Unlock 导致死锁 Kubernetes #72361 使用 mutex 解决竞争问题 Kubernetes #71617 使用 mutex 解决竞争问题 Kubernetes #70605 使用 mutex 解决竞争问题 gRPC-go gRPC-go #795 Unlock() 写错了？所以还是 defer unlock 合理。 gRPC-go #1318 添加 Lock 防止 data races gRPC-go #2074 添加 Lock 防止 data races etcd Distributed reliable key-value store for the most critical data of a distributed system etcd #10419 etcd Compact 方法调用的时候，使用了 mu.Lock() 而 Store 方法也会调用 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#grpc-go"},{"categories":["Notes"],"collections":null,"content":"流行项目 issues Docker (Moby) Moby #36114 hotAddVHDsAtStart 方法对 serviceVM 进行 VHD 的添加，并且会对 svm 加锁，如果添加 VHS 失败会调用 hotRemoveVHDsAtStart 方法去掉 VHD，而这个方法也进行了 svm.Lock() 因为 Mutex 不可重入，会导致死锁，所以改成了 hotRemoveVHDsNoLock 不用锁的方法来移除 VHD。 Moby #34881 这个 PR 在 if 判断后 return 却没有释放锁 其他还有 36840、37583、35517、35482、33305、32826、30696、29554、29191、28912、26507 等 issues。 Kubernetes Kubernetes #45192 忘记 Unlock 导致死锁 Kubernetes #72361 使用 mutex 解决竞争问题 Kubernetes #71617 使用 mutex 解决竞争问题 Kubernetes #70605 使用 mutex 解决竞争问题 gRPC-go gRPC-go #795 Unlock() 写错了？所以还是 defer unlock 合理。 gRPC-go #1318 添加 Lock 防止 data races gRPC-go #2074 添加 Lock 防止 data races etcd Distributed reliable key-value store for the most critical data of a distributed system etcd #10419 etcd Compact 方法调用的时候，使用了 mu.Lock() 而 Store 方法也会调用 ","date":"2024-05-22","objectID":"/posts/go-concurrent-1/:4:2","tags":["Go","并发编程","笔记"],"title":"阅读：Go 并发编程实战 1-4","uri":"/posts/go-concurrent-1/#etcd"},{"categories":["Learned Data Systems"],"collections":null,"content":"CatSQL: Towards Real World Natural Language to SQL Applications NL2SQL, text to SQL 是很有趣的方向。有 DL 方法也有现在的 LLM 微调。 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:1:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#catsql-towards-real-world-natural-language-to-sql-applications"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT rule-based 或者 Deep Learning 方法，要么无法通用，或者存在语法/语义错误/无法执行。 本文提出的新框架，注重 Accuracy 和 runtime，提出了 novel CatSQL sketch, which constructs a template with slots that initially serve as placeholders, and tightly integrates with a deep learning model to fill in these slots with meaningful contents based on the database schema 对比 sequence-to-sequence 或 sketch-based 方法，不需要生成关键词，有更高准确和更快 对比 sketch-based 方法，更加通用，而且可以用已经填好的词来提高性能。 提出了 Semantics Correction technique, which is the first that leverages database domain knowledge in a deep learning based NL2SQL solution. Semantics Correction 是 post-processing 后处理，通过 rules 识别和修改语义错误，大大提高了 NL2SQL 的准确度。 实验：single-domain and cross-domain，准确性和吞吐量都提高很多，尤其是在 Spider 基准上，比之前最好的 stoa NL2SQL 要高四分，63x 的吞吐量 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:2:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION 早期 NL2SQL 是 rule-based, 先将 nl parse 成中间态(parsing tree) 然后开发 rules 映射成 SQL 抽象语法树，然后转成 SQL query。这样难以跨域，而且需要大量人力开发规则。 最近借助 Deep Learning 实现 cross-domain adaption，而且提供了大量数据集，比如 Wiki-SQL 和 Spider。部分 DL 方法可以达到 80-90% 的精度，但是对于复杂查询性能会显著下降，不到 50%。以前的方法将其认为是翻译问题，机器翻译问题(seq2seq)，没有利用数据库知识，很容易导致语义错误。 本文是第一个将 DL 结合数据库的，开发了新的 NL2SQL 问题，可以提高性能。 CatSQL, is a deep learning-based approach, which can mitigate the issues of rule-based solutions to generalize across application domains. most existing deep learning approaches are based on a sequence-to-sequence or sequence-to-tree model . These methods do not guarantee the generated SQL queries are executable or even syntactically legal. Instead, our approach is a sketch-based solution, which relies on our novel CatSQL sketch to generate the SQL query. CatSQL sketch is a template with keywords and slots. These slots initially serve as placeholders. We use a deep learning model to fill in the empty slots to get a final SQL query, which is almost always a legal SQL query 完形填空？总是合法的 SQL 语句，那语义能否保证正确呢，结果总是正确的吗。如果结果正确，性能怎么样呢？NL2SQL 会不会知道索引，或者说会不会结合 query optimization 呢？ 以前也有 fill in slots 的做法， sketch-based idea Our CatSQL SQL generation algorithm employs a novel CatSQL sketch, which is general enough, and can facilitate the idea of parameter sharing to boost the performance 强调了更加 general，而且参数之间可以共享 以前的 DL 没有结合数据库信息，CatSQL 做了语义矫正，To the best of our knowledge, we are the first to incorporate semantic information into a deep learning-based NL2SQL solution. We evaluate our CatSQL approach on the well-known Spider dataset, and the results demonstrate that CatSQL is 4 points better than the previous state-of-the-art methods A novel sketch-based model CatSQL is proposed to achieve the state-of-the-art performance on various NL2SQL benchmarks; Semantics Correction of CatSQL is the first work that adopts database domain knowledge in a NL2SQL solution; Extensive evaluations demonstrate that CatSQL significantly outperforms all existing solutions on cross-domain benchmarks such as Spider and WikiSQL; On single-domain benchmarks, CatSQL solution also significantly outperforms existing solutions; CatSQL prototype achieves outstanding runtime performance: its single query runtime latency can be 2×−20× faster than all baselines; while its throughput can be 2.5×−63×higher than the previous approaches 主要贡献，CatSQL 新方法基于 sketch-based，语义矫正，大量测试，单领域多领域，运行时、吞吐量性能更好 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:3:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"OVERVIEW ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:4:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"Motivating Example The NL2SQL problem is to translate the natural language question into the corresponding SQL query. We need to figure out three challenging questions from the natural language description: (1) what tables and columns that will be used in the query; (2) what is the correct query structure; and (3) how to fill in query details and the literal in the query. 表和列，query 结构，query details 字面量。 Solutions from the DB community requires designing rules for the mapping between natural language tokens into SQL elements, and translating a natural language parsing tree into a SQL’s abstract syntax tree (AST). However, these approaches’ performance decay significantly when they are applied to a new domain of database, that requires redesigning the mapping. rules 难以应对新的 domain，新 domain 是什么意思，新的表？还是新的数据？ Deep Learning 训练大模型可以解决 cross-domain 问题，并且性能很好，但是没有利用语义信息生成查询，而且难以处理复杂查询，而且运行时性能和模型参数、复杂度有关。训练需要很多计算。 本文结合了这两种方法，and we demonstrate that our approach performs better than all existing solutions. Our approach employs a deep learning architecture, and we leverage DB domain knowledge to develop novel Semantics Correction techniques to postprocess the query generated by a neural network to fix obvious semantics errors. In the following, we will first provide necessary background on how a deep neural network NL2SQL solution works, and then we will give an overview of our approach. ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:4:1","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#motivating-example"},{"categories":["Learned Data Systems"],"collections":null,"content":"Background Embedding: As an atomic operator, neural networks, such as LSTMs [18] and Transformers [41], can model a sequence of tokens into a sequence of 𝑁-dimensional numeric vectors called embedding 这里也提到了 BERT, GraPPa 是另一个表语义分析的预训练模型，应该是专用于 SQL 领域的、表结构的。搜了一下才知道，之前有一篇 RatSQL 就是利用自注意力机制来做的，能知道关系，也就是 Relation-Aware Text2SQL Classifier: Using this operator, we can build a classifier to classify the input sequence into one of 𝐶 categories as follows. Sequence-to-sequence translation: Training: 基础知识略 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:4:2","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#background"},{"categories":["Learned Data Systems"],"collections":null,"content":"Existing NL2SQL Approaches (1) rule-based approaches; (2) sequence-to-sequence approaches; These approaches typically face the problem of picking a wrong column or a wrong value. Therefore, most of the efforts in these works are devoted to tackling this issue (3) sketch-based approaches. We will briefly explain the basic ideas below 具体介绍略，但 rule-based 可以处理复杂结构和 query？而且 Sketch-based 方法并不新，以前也有，比如 SyntaxSQLNet 和 Sqlnet，只不过可能没有结合 RL 或者没有做得那么好。CatSQL 可能就是利用 GraPPa 加上 Sketch-Based 的结合 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:4:3","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#existing-nl2sql-approaches"},{"categories":["Learned Data Systems"],"collections":null,"content":"CATSQL APPROACH Column Action Templates ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:5:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#catsql-approach"},{"categories":["Learned Data Systems"],"collections":null,"content":"CatSQL template The definition of the CatSQL template is designed to facilitate the idea of parameter sharing, which is not adapted by previous sketch-based approaches. In particular, since each of the four CAT clauses can be viewed as a sequence of CATs, we can train one sequence-to-sequence model for all four different clauses. At runtime, once the CATs are predicted, we can simply construct the final SQL query by assembling different CAT clauses. We will explain how our neural network fills a CatSQL sketch in the next section parameter sharing 这个概念不太理解，如果说填空之间有联系，能不能用微调后的模型，做一个上下文预测？ ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:5:1","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#catsql-template"},{"categories":["Learned Data Systems"],"collections":null,"content":"CatSQL query generation the overall architecture of CatSQL is composed of four components, namely GraPPa embedding network, CAT decoder network, conjunction network, and FROM decoder network. We now explain each of these components 就是用 GraPPa 实现了 parameter sharing，看来本质还是上下文预测。想知道和 CatSQL 的区别在哪呢？ ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:5:2","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#catsql-query-generation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Semantics Correction Semantics Correction technique significantly improves the accuracy by leveraging database domain knowledge. While a deep learning model is effective in understanding the intention of a question, sometimes the generated SQL query expresses the same intention, but is semantically invalid considering the database schema. 如果和 CatSQL + GraPPa 区别不大，是不是语义矫正提高了更多的准确性 如果说 ChatGPT 生成的 SQL 质量较好，是因为 RLHF 吗？ We classify our Semantics Correction rules into three categories: (1) token-level violation, (2) FROM clause revision, and (3) join-path revision. 有三个类型可以修正，是 best-effort 而不是保证可以的，也不会把对的写成错的 token-level violation 看上去是是类型检查 FROM clause revision 有时候 query 会出现一些没出现过的 column 比如 FROM 里没出现过的，此时会直接加到 FROM 里？用最小生成树来判断这些 JOIN 情况。 join-path revision 有时候 join 会失败 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:5:3","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#semantics-correction"},{"categories":["Learned Data Systems"],"collections":null,"content":"SYSTEM IMPLEMENTATION 基于 MySQL v5.7.30 Offline processing. 离线预处理，模型训练。甚至用了 redis 存储 literals to embedding (word2vec 而不是 BERT 的原因是更快) Online serving. 在线推理，由于构建了后端，好像可以并行化（吞吐量的由来？） Model details: ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:6:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#system-implementation"},{"categories":["Learned Data Systems"],"collections":null,"content":"EXPERIMENTS accuracy, running speed, throughput ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#experiments"},{"categories":["Learned Data Systems"],"collections":null,"content":"Benchmarks and Baselines Dataset: cross-domain: WikiSQL, Spider 具有大量的 queries, tables 和 databases single-domain: GeoQuery, Scholar, IMDB\u0026hellip; Spider is considered as the hardest NL2SQL dataset currently. Spider supports much richer SQL syntax, and it classifies the dataset into four categories based on their hardness levels Evaluation metrics: accuracy (i.e., Accex ). 语义上正确的 SQL？还是结果一致？ logical form accuracy (i.e., Acclf ). 完全一致的 SQL？ executable rate: 本文提出的 Baseline approaches: 文章选了 RAT-SQL，但是是用 GraPPa 预训练的，而 NatSQL 和 SmBoP 选的是 GraPPa 与训练的 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:1","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#benchmarks-and-baselines"},{"categories":["Learned Data Systems"],"collections":null,"content":"Cross-Domain Evaluation Results 分别测试了没有 parameter sharing 的，没有 semantic correction 的，完整的， 在 Spider 上表现非常好，几乎各项都是第一，没有 parameter sharing 的数据都挺好但看上去 SC 的提升在 easy 提升很明显，但 Extra Hard 就提升没那么多。 而且可执行的概率也特别高，除非没有语义矫正，都是 100% 论文提到 not designed for logical form accuracy？ 运行速度也很快，吞吐量也很大。 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:2","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#cross-domain-evaluation-results"},{"categories":["Learned Data Systems"],"collections":null,"content":"Single-Domain Evaluation Results 比 rule based 也要好，但是在 IMDB 上表现比较差，但也比其他都好 使用的是 Spider 数据训练，在 IMDB 上表现差是因为很多 join rules 不太适用？ ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:3","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#single-domain-evaluation-results"},{"categories":["Learned Data Systems"],"collections":null,"content":"Throughput analysis 吞吐量高的原因，用了小的模型 GraPPa，所以也更快。而且在 decoding 时，一个 CAT 生成一组 slots，可以大幅并行。可以并行处理 11 个查询， ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:4","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#throughput-analysis"},{"categories":["Learned Data Systems"],"collections":null,"content":"Case Study 分析了几个用例，比较有趣的是没法生成 AVG() 这种，而是直接 select average，但是表带有 average 列？ 还有就是 Column Action Template 会 flatten conjunction 关系 导致无法生成合适的 SQL 来选取 OR 关系。 论文认为可以多训练 This problem could be possibly handled by either collecting more training data containing queries with complex conjunction relationships, or designing new architecture to predict logical operations explicitly. Both approaches are challenging to implement and require high-quality manual labels. We leave this issue as a further research to study 多训练是合理的，不知道微调 GraPPa 能不能预测一些 conjunction 关系 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:7:5","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#case-study"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORK 提到了 GraPPa 非常有用 ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:8:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION 略 名字非常好玩，从 RatSQL 到 CatSQL，但实际上论文没有太多比较 RatSQL 甚至 RatSQL 只用了 BERT 来作为 baseline 也非常创新地利用 RL 从翻译变成了完形填空，而且用 LLM 可以预测上下文的能力进行关联，非常巧妙 而且用 Semantic Correction 可以后处理这些可能错误的语义，只是还有一些缺陷，文中也说训练也可能可以解决。 看了下目前 CatSQL 78 分 Acc𝑒𝑥 并不算高的，阿里后面的 DAIL-SQL + GPT-4 和 DAIL-SQL + GPT-4 + Self-Consistency 刷到了 86 分。甚至有匿名的 MiniSeek 刷到了 91 分，可能 GPT 参数更大，还是要更强吧。 同时最近还有新的 benchmark BIRD-SQL 是阿里和港大一起推出的， Summarize The paper propose a novel framework called CatSQL, which tackles the challenge of translating natural language questions into SQL queries. While existing systems like rule-based and deep-learning-based cannot handle cross-domain datasets or donot use the database information, CatSQL creatively combines two approaches. CatSQL leverages the contextual prediction capabilities of language models such as GraPPa to enable parameter sharing based on a sketch-based method and can generate more accurate SQL statements with semantics correction. In addition, CatSQL not only exhibits better accuracy, it is also faster and has greater throughput. Contributions: CatSQL innovatively bridges two existing Text-to-SQL approaches (deep-learning and rule-based) and overcomes their problems, for example, rule-based emthods are almost exclusively used for single-domain datasets. CatSQL also turns the existing Deep Learning translation task into a filling task with faster runtime and higher throughput. CatSQL applies sketches to construct a SQL statement template with slots and placeholders, and then uses deep learning model like GraPPa to fill the slots with meaningful content based on the database schema. The method is called parameter sharing and can reduce the computational complexity and improve generalization capability and flexibility. The paper proposes a semantic correction technique to identify and correct semantic errors in the generated SQL queries with database domain knowledge. The method can detect erros on token level, FROM clause level and join-path level, improving the exact set match accuracy and obtaining a significant improvement over baseline models (RAT-SQL with BERT model). Limitations: CatSQL would be very dependent on the performance of deep learning models, although the paper compares Rat-SQL, which is using the BERT model. If the GraPPa model is also used, RAT-SQL as a baseline will also perform better. And from the current Spider Leader Board, the front runners are using larger models like GPT-4, and the score is much higher than CatSQL, at 86 (DAIL-SQL + GPT-4). The paper says that CatSQL doesn\u0026rsquo;t focus on accuracy, but no attempt is made to solve the problems encountered in the Case Study such as conjunction and column name confusion by more training, or by switching to a larger model. The method Semantic correction is post-processed and could be improved if it can be performed when reasoning or generating SQL statements. In addition, when new benchmarks come out, such as BIRD, which can better evaluate a model\u0026rsquo;s ability to work across data domains, and CatSQL might need to be retrained to achieve better performance. Improve: This paper is very innovative and methodologically sound. I might try to replace the language model with a larger one at first, possibly with more parameters capable of stronger context and parameter sharing. And I may do more model training to try to solve the problems in cast study, especially conjunction errors and semantic obfuscation. In addition, perhaps the ChatGPT fine-tuning approach, or Reinforcement Learning Human Feedback (RLHF) could also help CatSQL to achieve higher accuracy. Finally, I would try to run the modified model on more benchmarks for evaluation, especially on the BIRD benchmark. ","date":"2024-04-14","objectID":"/posts/catsql-nlsql/:9:0","tags":["Paper Reading"],"title":"Paper Reading: CatSQL: Towards Real World Natural Language to SQL Applications [VLDB 23]","uri":"/posts/catsql-nlsql/#conclusion"},{"categories":["Learned Data Systems"],"collections":null,"content":"The Case for a Learned Sorting Algorithm 除了 Query Optimization, Index, Tunning, ML 还可以用在 Database 其他方面，比如排序？ 论文作者中的 Tim Kraska 以及其团队，是 SageDB, Bao: Learned Query Optimization, Neo 的作者，此外还有 FITing-Tree 索引结构。 看了一下 Tim 除了在 MIT 当 AP，还在一家叫 Einblick 的公司创业（看了下好像是 text2SQL 类型的，应该已经被 Databricks 收购了），然后在 Amazon 研究 Data Science 和 AI 的结合？最近也有一些比如 2023 的 BRAD: simplifying cloud data processing with learned automated data meshes 和之前看的 SEED: Domain-specific data curation with large language models 也是 Tim 团队的，他们做了很多 AI 与数据库、数据、数据结构、数据处理结合的事情。 回到本篇，ML 排序和 Query Optimization 的思路差不多，比较重要的一点都是需要 ML 学习数据的分布函数：cumulative distribution function (CDF)，也是 SageDB 当中重要的一点。 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:1:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#the-case-for-a-learned-sorting-algorithm"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT Sorting is one of the most fundamental algorithms in Computer Science and a common operation in databases not just for sorting query results but also as part of joins (i.e., sort merge-join) or indexing. In this work, we introduce a new type of distribution sort that leverages a learned model of the empirical CDF of the data. Our algorithm uses a model to efficiently get an approximation of the scaled empirical CDF for each record key and map it to the corresponding position in the output array. We then apply a deterministic sorting algorithm that works well on nearly-sorted arrays (e.g., Insertion Sort) to establish a totally sorted order. leverages a learned model of the empirical CDF of the data 看上去是先映射/分区，然后用插入排序在几乎有序的数组上排序，速度应该是很快的。和 C++ STL sort 思想也差不多，近乎有序就直接插入排序（当快速排序递归子区间足够小的时候，插入排序） We compared this algorithm against common sorting approaches and measured its performance for up to 1 billion normally-distributed double-precision keys. The results show that our approach yields an average 3.38× performance improvement over C++ STL sort, which is an optimized Quicksort hybrid, 1.49× improvement over sequential Radix Sort, and 5.54× improvement over a C++ implementation of Timsort, which is the default sorting function for Java and Python. 实际上 C++ STL sort 又叫 Introsort，也就是 Hybrid Quicksort，没有稳定性。而 TimSort 是保持稳定性的归并和插入混合排序。 前两年出现了一个更快的 pdqsort，Rust 和 Go 也将其引入了标准库。主要思想也是拆分成几种情况，正常 quicksort，短的近似有序的用 insertion sort 最坏情况用 heapsort 保证最坏 nlogn 时间复杂度。但明显 pdqsort 也是不稳定的。 radix sort 就是基数排序，按照数位从低到高 / 从高到低排序。不仅整数也支持浮点数。 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:2:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION 对比了 radix sort （按 key 排序）和比较排序，提出用 Tim 团队在 SageDB 中的一个想法： ML 增强排序算法。 核心思想：we train a CDF model FFF over a small sample of keys AAA and then use the model to predict the position of each key in the sorted output. If we would be able to train the perfect model of the empirical CDF, we could use the predicted probability PPP (A≤xA ≤ xA≤x) for a key x, scaled to the number of keys NNN, to predict the final position for every key in the sorted output: pos=FA(x)⋅N=P(A≤x)⋅Npos = FA(x) · N =P (A ≤ x ) · Npos=FA(x)⋅N=P(A≤x)⋅N . Assuming the model already exists, this would allow us to sort the data with only one pass over the input, in O(N)O(N)O(N) time 还是一个强前提，必须训练一个完美的 CDF model，先不说训练时间，训练精度能否拟合都是个问题 Obviously, several challenges exists with this approach. Most importantly, it is unlikely that we can build a perfect empirical model. Furthermore, state-of-the-art approaches to model the CDF, in particular NN, would be overly expensive to train and execute. More surprising though, even with a perfect model the sorting time might be slower than a highly optimized Radix Sort algorithm. Radix Sort can be implemented to only use sequential writes, whereas a naïve ML-enhanced sorting algorithm as the one we outlined in [28] creates a lot of random writes to place the data directly into its sorted order 自己也提出了问题：perfect empirical model 完美的经验模型是不存在的。训练方法 NN 也比较昂贵？而且就算完美的 CDF 模型，也可能比 radix sort 慢。但不太懂什么是 highly optimized radix sort，是只有顺序写的吗？而 ML sort 可能会产生许多随机写入，导致有很多 IO 代价？不知道是不是 SageDB 遇到的问题。 本文提出了 Learned Sort，sequential ML-enhanced sorting algorithm 来解决这些问题。此外还有 a fast training and inference algorithm for CDF modeling. 本文应该是第一篇 cache-efficient ML-enhanced sorting algorithm which does not suffer from the random access problem. We propose a first ML-enhanced sorting algorithm, called Learned Sort, which leverages simple ML models to model the empirical CDF to significantly speed-up a new variant of Radix Sort We theoretically analyze our sorting algorithm We exhaustively evaluate Learned Sort over various synthetic and real-world datasets ","date":"2024-04-12","objectID":"/posts/learned-sorting/:3:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"LEARNING TO SORT NUMBERS pdf: 概率密度函数，连续随机变量。CDF: 累积分布函数，分布函数，概率密度函数的积分，能描述一个随机变量 x 的概率分布。 高斯分布的概率密度函数很常见，它的累积分布函数，取积分后就是 0 到 1 很像 sin 函数，是严格递增的 通过 CDF 能知道一个数据大概在什么区间的概率，乘上长度就可以知道位置，最后数组就是接近有序的 Given a function FA(x), which returns the exact empirical CDF value for each key x ∈ A, we can sort A by calculating the position of each key within the sorted order as pos ← FA(x )· |A|. This would allow us to sort a dataset with a single pass over the data as visualized in Figure 1 但不可能会有完美的 CDF 函数，而且也只能选取数据中的一些样本来训练模型，而且数据会有重复。 讨论了 out-of-place 和 in-place 两种？ 看上去还需要额外的辅助空间 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:4:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#learning-to-sort-numbers"},{"categories":["Learned Data Systems"],"collections":null,"content":"Sorting with imprecise models duplicate keys and imprecise models may lead to the mapping of multiple keys to the same output position in the sorted array. Moreover, some models (e.g., NN or even the Recursive Model Index (RMI) [29]) may not be able to guarantee monotonicity, creating small misplacements in the output. That is, for two keys a and b with a\u0026lt;ba \u0026lt; ba\u0026lt;b the CDF value of a might be greater than the one of b(F(a)\u0026gt;F(b))b (F (a) \u0026gt;F (b))b(F(a)\u0026gt;F(b)), thus, causing the output to not be entirely sorted. 如何解决重复键、不精确模型的问题？ 此外模型还可能不是单调的 所以如果位置发生碰撞 collisions，和哈希方法一样采用了 Linear probing, Chaining, Spill bucket 等方法来解决冲突。 Linear Probing 会导致错的键越来越多，尤其是非单调的时候。其他方法会增加空间开销，比如 spill bucket 用新的桶存，则需要单独归并。 论文都采用了 spill bucket 的方法，对于非单调的情况，用插入排序来解决问题。 the expected number of collisions depends on how well the model overfits to the observed data. 说了一堆，就是碰撞的概率还是很高，除非是完美的 CDF，但是有需要减少训练时间，而且只能抽取样本训练，没法学到完美的 CDF 使用了额外的辅助空间解决 collision。 就算完美 CDF 也比 Radix 慢，但是为什么呢？ 这一部分分析说认为是随机访问导致了 CPU cache 和 TLB 局部性失效，而 radix 是顺序写入的，能够利用缓存和局部性。所以本文还提出了利用缓存的 ML-enhanced 算法 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:4:1","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#sorting-with-imprecise-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Cache-optimized learned sorting 其实 ML sort 和 Radix 很像，都是 bitmap 类型的，论文也提到如果 the number of elements to sort is close to the key domain size (e.g., 23^2 for 32-bit keys) 就是几乎一致的。 所以主要是和 radix 比较？ radix 速度取决于 key domain size，而当元素数量远小于 key domain size 时，Learned Sort 速度也比 radix 快很多。 We organize the input array into logical buckets. That is, instead of predicting an exact position, the model only has to predict a bucket index for each element, which reduces the number of collisions as explained earlier. Step 1: For cache efficiency, we start with a few large buckets and recursively split them into smaller ones. By carefully choosing the fan-out ( f ) per iteration, we can ensure that at least one cache-line per bucket fits into the cache, hence transforming the memory access pattern into a more sequential one. This recursion repeats until the buckets become as small as a preset threshold t . Section 3.1 explains how f and t should be set based on the CPU cache size. Step 2: When the buckets reach capacity t , we use the CDF model to predict the exact position for each element within the bucket. Step 3: Afterwards we take the now sorted buckets and merge them into one sorted array. If we use a non-monotonic model, we also correct any sorting mistakes using Insertion Sort. Step 4: The buckets are of fixed capacity, which minimizes the cost of dynamic memory allocation. However, if a bucket becomes full, the additional keys are placed into a separate spill bucket array (see Figure 4 the “S”bucket symbol). As a last step, the spill bucket has to be sorted and merged. The overhead of this operation is low as long as the model is capable of evenly distributing the keys to buckets. cache-optimized learned sort 有几个优化：1. 预测桶的位置，避免碰撞 2. 为了提高缓存效率，采用几个大的桶，分成小的桶，选择一个 fan-out f 函数？ 3. 桶达到容量时，用 CDF 模型预测位置 4. 如果用非单调模型，用插入排序修正 5. 小桶容量固定，还有溢出 spill bucket 还是要保证 CDF 的拟合程度，以及分布的均匀性 算法细节略 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:4:2","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#cache-optimized-learned-sorting"},{"categories":["Learned Data Systems"],"collections":null,"content":"Implementation optimizations 实现上有几个优化 We process elements in batches. First we use the model to get the predicted indices for all the elements in the batch, and then place them into the predicted buckets. This batch-oriented approach maintains cache locality As in Algorithm 1, we can over-provision array B by a small factor (e.g., 1.1×) in order to increase the bucket sizes and consequently reduce the number of overflowing elements in the spill bucket S. This in turn reduces the sorting time for S. Since the bucket sizes in Stage 2 are small (i.e., b ≤ t ), we can cache the predicted position for every element in the current bucket in Line 28 and reuse them in Line 34. In order to preserve the cache’s and TLB’s temporal locality, we use a bucket-at-a-time approach, where we perform all the operations in Lines 11-40 for all the keys in a single bucket before moving on to the next one. The code for the algorithm can be found at http://dsg.csail.mit.edu/mlforsystems. 首先这是批处理的，先预测位置，然后入桶，能够保证缓存局部性。可以增加辅助数组的大小？减少桶溢出的概率。而且桶大小很小，可以重复利用。最主要的是 bucket-at-a-time approach，每次只处理一个桶里的所有键，提高缓存局部性。 算法 2 比较长，但大致思路就是利用小桶（桶和桶之间应该也是有序的）局部性，而且能够避免部分碰撞 只是这个桶的数量要怎么选呢？论文好像没有仔细讲。而且关于稳定性，只是稍微讲了一句只要溢出桶的排序是稳定的，则 Learned Sort 是稳定的，我觉得可以多讨论一下和验证，尤其是当溢出桶数据比较多的时候，选择什么算法？是插排还是归并？还是需要分析算法的。 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:4:3","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#implementation-optimizations"},{"categories":["Learned Data Systems"],"collections":null,"content":"Choice of the CDF model 如何选取 CDF model？虽然论文说不取决于特定的模型，但是还是需要考虑训练和推理快的。 Thus, models such as KDE[43, 47], neural networks or even perfect order-preserving hash functions are usually too expensive to train or execute for our purposes. One might think that histograms would be an interesting alternative, and indeed histogram-based sorting algorithms have been proposed in the past. Unfortunately, histograms have the problem that they are either too coarse-grained, making any prediction very inaccurate, or too fine-grained, which increase the time to navigate the histogram itself (see also Section 6.8). Certainly many model architectures could be used, however, for this paper we use the recursive model index (RMI) architecture as proposed in [29] (shown in Figure 5). RMIs contain simple linear models which are organized into a layered structure, acting like a mixture of experts[29]. 考虑了一些传统的 NN 或者带顺序的哈希，甚至直方图，但可能不太准或者粒度太细。 还是使用了 SageDB 中提出的 recursive model index RMI 模型， 推理速度很快，一次加法、乘法和查找 训练：采样几个输入，然后排序之后(std::sort), 生成了一棵树，自顶向下训练一个线性模型。除了三维也可以二维， 单个模型的训练，为了保证模型是单调的，算法 2 就不需要插入排序修正，需要训练时进行边界检查，但又会使得算法 2 需要额外的分支命令（没有 cache？） 用了单调性更好的 linear spline fitting？训练快，插入排序也减少了 35% 的 swap ","date":"2024-04-12","objectID":"/posts/learned-sorting/:4:4","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#choice-of-the-cdf-model"},{"categories":["Learned Data Systems"],"collections":null,"content":"ALGORITHM ANALYSIS 讨论时间复杂度和性能，以及不同参数下的表现 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:5:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#algorithm-analysis"},{"categories":["Learned Data Systems"],"collections":null,"content":"Analysis of the sorting parameters 3.1.1 Choosing the fan-out ( f ) f 大，可以使得模型更准？那不就成了 radix 吗 为了利用缓存，f 也必须要限制。测试了 100m doubles 和不同的 f，表现了其性能和 L1 L2 cache size(f = 1-5K) 相关。 3.1.2 Choosing the threshold ( t ). The threshold t determines the minimum bucket capacity as well as when to switch to a Counting Sort subroutine (Line 11 in Algorithm 2). We do this for two reasons: (1) to reduce the number of overflows (i.e., the number of elements in the spill bucket) and (2) to take better advantage of the model for the in-bucket sorting. Here we show how the threshold t affects the size of the spill bucket, which directly influences the performance t 是 bucket size，会影响溢出的概率 论文也提到，基于样本训练，不可能会有完美的模型，不可能可以把每个元素映射到唯一的位置，总是会发生不同元素映射到同一个位置。这里推导了 E[s] = N / e 表示溢出桶的大小，N 表示桶的数量，论文通过测试不同的大小，当 t = 100 时，overflow 概率 3.9% \u0026lt; 5%, Learned Sort 性能最好。但说的是经验上来看？没有给出合理的证明和更详细的测试。 3.1.3 The effect of model quality on the spill bucket size. 不同模型会导致碰撞、溢出。论文假设样本是从分布中独立生成的？这部分没看懂，对于小样本，怎么学习分布才能导致溢出更少？反正还是要尽可能地拟合 CDF 3.1.4 Choosing the sample size. 太多经验操作了，直接就 1% 采样率了，而且他这个采样率只测试了采样率和训练/排序的时间，没有测其他参数？ ","date":"2024-04-12","objectID":"/posts/learned-sorting/:5:1","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#analysis-of-the-sorting-parameters"},{"categories":["Learned Data Systems"],"collections":null,"content":"Complexity This process takes O(N⋅L)O(N ·L)O(N⋅L) time, where L is the number of layers in the model. Since we split the buckets progressively using a fan-out factor f until a threshold size t , the number of iterations and the actual complexity depend on the choice of f and t . However, in practice we use a large fan-out factor, therefore the number of iterations can be considered constant t 和 f 之间是否存在关系？时间复杂度 O(N L) L 是模型层数？f 比较大，迭代次数是常数级别的？ 最重要的问题还是取决于模型质量，不论是什么阶段，如果模型太差，基本就是退化到插入排序，O(n^2)，桶的溢出也受影响，溢出桶也受影响。 算法使用了辅助空间，空间复杂度 O(N) 但后续实现了 in-place 的 O(1) 空间复杂度 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:5:2","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#complexity"},{"categories":["Learned Data Systems"],"collections":null,"content":"EXTENSIONS 在字符串上 in-place 排序 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:6:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#extensions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Making Learned Sort in-place 还是分组，但是是用 buffer ","date":"2024-04-12","objectID":"/posts/learned-sorting/:6:1","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#making-learned-sort-in-place"},{"categories":["Learned Data Systems"],"collections":null,"content":"Learning to sort strings 字符串和浮点数的区别 但是怎么训练 CDF？限制字符有可能导致非单调。 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:6:2","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#learning-to-sort-strings"},{"categories":["Learned Data Systems"],"collections":null,"content":"Duplicates 重复值产生很大影响，会导致很多碰撞。 比如 [0,1,0,1,0,1] 这种数据，应该会导致很多问题 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:6:3","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#duplicates"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORK 排序算法通常被分类为 基于比较 或 基于分布 的 Comparison sorts: Distribution sorts: SIMD optimization Hashing functions ML-enhanced algorithms 神奇的是，这一部分提到了 Swift5 换了新的 PDQSort，论文却没有进行对比？ 论文还提到了 radix 非常适合 SIMD 和多核场景，不知道 Learned Sort 能否也扩展到这种情况 CDF model 和 order-preserving 哈希函数很像，但 CDF 还是需要分类，而 hash 训练慢而且一般没有 order-preserving ","date":"2024-04-12","objectID":"/posts/learned-sorting/:7:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"EVALUATION • Evaluate the performance of Learned Sort compared to other highly-tuned sorting algorithms over real and synthetic datasets • Explain the time-breakdown of the different stages of Learned Sort • Evaluate the main weakness of Learned Sort: duplicates • Show the relative performance of the in-place versus the out-of-place Learned Sort • Evaluate the Learned Sort performance over strings. ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#evaluation"},{"categories":["Learned Data Systems"],"collections":null,"content":"6.1 Setup and datasets 这里还是比较了 PDQ 的，但是认为 PDQS 比 baseline 差很多？这不太合理吧。。PDQS 能消除很多分支预测，时间复杂度应该也是 O(nk) 和 O(nlogn) 级别的 而且训练时间也算入了排序时间，除非另有说明 synthetic datasets: 不同分布的，混合分布的， 真实数据：地图数据、物联网数据、FaceBook Rw 数据、TPC-H（其实也不是真实的） 对比的数据是 Sorting Rate，吞吐量，Learned Sort 都比较高，而 IS4o 和 Radix 比较接近，其他都没有太高 我认为他这个测试对传统的排序算法不是很好，传统的排序吞吐量都比较低。而且不太理解这个指标的概念是什么，不直观 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:1","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#61-setup-and-datasets"},{"categories":["Learned Data Systems"],"collections":null,"content":"Overall Performance ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:2","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#overall-performance"},{"categories":["Learned Data Systems"],"collections":null,"content":"Sorting rate 没能看到 runtime, memory usage, comparisons/swaps 等更多指标 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:3","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#sorting-rate"},{"categories":["Learned Data Systems"],"collections":null,"content":"Sorting Strings 这里就排除了训练时间，而且去掉了 Radix，比 baseline 慢很多 Learn sort 在这里的吞吐量并不是很高，几乎是浮点数的十分之一，但还是要比 IS4O 高，比传统排序也高 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:4","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#sorting-strings"},{"categories":["Learned Data Systems"],"collections":null,"content":"The impact of duplicates 这里很有意思，重复很多的时候，Learned Sort 吞吐量变化其实并不大？只有非常多重复的时候才会降低很多。 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:5","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#the-impact-of-duplicates"},{"categories":["Learned Data Systems"],"collections":null,"content":"In-place sorting 吞吐量下降了 10% 左右 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:6","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#in-place-sorting"},{"categories":["Learned Data Systems"],"collections":null,"content":"Performance decomposition 大部分时间在桶分类 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:7","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#performance-decomposition"},{"categories":["Learned Data Systems"],"collections":null,"content":"Using histograms as CDF models RMI 模型更适合，更快，用了连续函数 ","date":"2024-04-12","objectID":"/posts/learned-sorting/:8:8","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#using-histograms-as-cdf-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION AND FUTURE WORK 加速分类，对字符串处理和排序做了展望，以及并行、多核排序、SIMD 等等。还有更多复杂类型 是一篇很好的论文，但 evaluation 只有一个指标，不知道能不能优化一下 std::sort 之类的 cache，不知道表现会怎么样。 The paper proposes a sorting algorithm called Learned Sort, which is developed from learned database SageDB by the same team. Learned Sort leverages machine learning (ML) to predict the sorted position of elements, significantly improving sorting efficiency for large datasets. The main idea is to train a CDF model over a small sample of the data and then use the model to predict the position of each key. In addition, the paper also proposes a highly optimized Learned Sort algorithm to maximize cache utilization with bucketization, and compares its performance between in-place and out-of-place implementation. Learned Sort outperforms existing sorting algorithms, particularly when dealing with large datasets that overflow the CPU\u0026rsquo;s cache. The paper also discusses where Learned Sort can be improved, such as more complex data types and parallel multicore sorting. Contributions: The paper propose the first ML-enhanced sorting algorithm, Learned Sort, which leverages RMI to model the empirical cumulative distribution function (CDF) of the data. The prediction allows Learned Sort to directly place elements close to their final position, reducing the number of comparisons and swaps needed compared to traditional comparison-based sorting methods. Besides the basic ML-enhanced sorting algorithm, the paper also explores an optimized Learned Sort algorithm that can take advantage of caching, and provides an exhaustive comparison with Radix Sort with large datasets. Optimized Learned Sort makes use of buckets and spill buckets for sorting, and the paper also presents an improved version that does not require auxiliary space that can sort in-place. The paper provides a detailed theoretical analysis of Learned Sort, and also gives complete evaluations based on the analysis, comparing not only traditional sorting algorithms such as std::sort and Timsort, but also the optimized Radix Sort and IS4o. The experimental result proves that Learned Sort has better throughput with large datasets (real-world, synthetic, mixed distributions, and large number of duplicates). Limitations: Learned Sort heavily relies on the CDF models to predict the positions of keys. Not only it relies on the accuracy of the model, the paper also points out that even if there is a perfect prediction model, collisions will inevitably occur due to the sampling method used in training. There are too few metrics for evaluation, the paper only compares sorting rate, which may not show the performance of Learned Sort in other aspects, such as runtime, memory usage, exchange rate or comparison ratio, etc. Compared with floating numbers, the throughput of Learned Sort drops sharply, which means that it is necessary to compare other metrics. The paper\u0026rsquo;s comparison of Recursive Model Index (RMI) and other CDF models is less detailed, especially how to select the fan-out function f and bucket size t under different models, and the impact on overflow and spill buckets. In addition, the paper does not discuss the internal relationship between f and t. And when analyzing the algorithm, the paper doesnot give detailed proof that the algorithm is stable or unstable. Improve: First I would try to explore some techniques like transfer learning to leverage a pre-trained model on a broader data set like Zero-shot model, which could improve Learned Sort\u0026rsquo;s performance on new data types without retraining. And according to different CDF models, I will try to select different parameters such as fan-out function f and bucket size, discuss the relationship between them. Then I would try to optimize std::sort and timesort and even pdq sort to make them take advantages of cache and sequential writes, making a more detailed evaluation based on various metrics, such as runtime and memory usage. This paper is very innovative and well written, ","date":"2024-04-12","objectID":"/posts/learned-sorting/:9:0","tags":["Paper Reading"],"title":"Paper Reading: The Case for a Learned Sorting Algorithm [SIGMOD 2020]","uri":"/posts/learned-sorting/#conclusion-and-future-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems CMU 的对于 DBMS 自动调优的论文，采用了 ML 机器学习方法，是 Ottertune 的论文。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:1:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#an-inquiry-into-machine-learning-based-automatic-configuration-tuning-services-on-real-world-database-management-systems"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT Modern database management systems (DBMS) expose dozens of configurable knobs that control their runtime behavior 与专家 DBA 相比，使用机器学习（ML）进行自动调整方法，的最新工作已显示出更好的性能。然而，这些基于 ML 的方法是在具有有限调优机会的合成工作负载上进行评估的，因此尚不清楚它们是否在生产环境中提供相同的好处 To better understand ML-based tuning, we conducted a thorough evaluation of ML-based DBMS knob tuning methods on an enterprise database application. We use the OtterTune tuning service to compare three state-of-the-art ML algorithms on an Oracle installation with a real workload trace. Our results with OtterTune show that these algorithms generate knob configurations that improve performance by 45% over enterprise-grade configurations. We also identify deployment and measurement issues that were overlooked by previous research in automated DBMS tuning services 在企业数据库上实现了 OtterTune，提高了 45% 的性能，并且发现了部署和检测问题。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:2:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION automate the tuning of database management systems (DBMSs) “self-adaptive” DBMSs that used recommendation tools with physical database design (e.g., indexes, partitioning). In the early 2000s, “self-tuning” systems expanded the scope of the problem to include automatic knob configuration. These knobs are tunable options that control nearly all aspects of the DBMS’s runtime behavior. DBMSs have hundreds of knobs, and a database administrator (DBA) cannot reason about how to tune all of them for each application. Unlike physical database design tools [11], knob configuration tools cannot use the built-in cost models of the DBMS’s query optimizers. There are two ways that these tools automatically tune knobs. The first is to use heuristics (i.e., static rules) that the tool developers manually create [1, 5, 14, 27, 42]. Previous evaluations with open-source DBMSs (Postgres, MySQL) showed that these tools improve their throughput by 2–5×over their default configuration for OLTP workloads [38]. These are welcomed improvements, but there are additional optimizations that the tools failed to achieve. This is partly because they only target the 10–15 knobs that are thought to have the most impact. It is also because the rules do not capture the nuances of each workload that are difficult to codify 为什么无法用内置的 cost model 调整 knobs？启发式规则没法捕捉到 workload 之间细微的差别？ tune a DBMS s knobs is to use machine learning (ML) methods that devise strategies to configure knobs without using hardcoded rules 在 OLTP 任务中 ML 方法比静态工具吞吐量提高 15 35%，原因是 ML 比静态规则考虑了更多 knobs，而且还会考虑 knobs 之间的依赖关系，是非线性的，人类难以模拟 ML 方法比人类 DBA 更好，性能好。(1) opensource DBMS with limited tuning potential (e.g., Postgres, MySQL, MongoDB) and (2) synthetic benchmarks with uniform workload patterns 而且用了本地存储（SSD），显示 DBMS 都用的非本地共享存储，比如 on-premise SANs and cloud-based block stores (S3)? 后者有更高的读写延迟， incur more variance in their performance than local storage。 目前并不清楚这些差异怎么影响 ML 调优方法？为什么呢，如果云存储导致读写延迟高，那 ML 调优 建模的时候应该也能考虑到这些情况？ 之前的研究也没有提到自动化调优，they do not specify how they select the bounds of the knobs they are tuning. This means that the quality of the configurations may still depend on a human initializing it with the right parameters 还是需要 DBA 初始化 this paper presents a field study of automatic knob configuration tuning on a commercial DBMS with a real-world workload in a production environment. We provide an evaluation of state-of-the-art ML-based methods for tuning an enterprise Oracle DBMS (v12) instance running on virtualized computing infrastructure with non-local storage. We extended the OtterTune [3] tuning service to support three ML tuning algorithms: (1) Gaussian Process Regression (GPR) from OtterTune [38], (2) Deep Neural Networks (DNN) [4, 39], and (3) Deep Deterministic Policy Gradient (DDPG) from CDBTune [40]. We also present optimizations for OtterTune and its ML algorithms that were needed to support this study. 本文应该是集大成的，自动调优、商业数据库、生产环境、stoa benchmark、云存储。OtterTune 使用了 GDR 、DNN 和 DDPG 方法。 the quality of the configurations from the ML algorithms (GPR, DDPG, DNN) are about the same on higher knob counts, but vary in their convergence times. knob 多的时候，效果差不多，但收敛速度不一样 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:3:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"BACKGROUND how an automated tuning service works using OtterTune as an example limitations of previous evaluations why a more robust assessment is needed to understand their capabilities ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:4:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#background"},{"categories":["Learned Data Systems"],"collections":null,"content":"OtterTune Overview 介绍 OtterTune，It maintains a repository of data collected from previous tuning sessions and uses it to build models of how the DBMS responds to different knob configurations. It uses these models to guide experimentation and recommend new settings. Each recommendation provides the service with more data in a feedback loop for refining and improving the accuracy of its models feedback loop 有点像 RL OtterTune is made up of a controller and a tuning manager. The controller acts as an intermediary between the target DBMS and the tuning manager. It collects runtime data from the target DBMS and installs configurations recommended by the tuning manager. The tuning manager updates its repository and internal ML models with the information provided by the controller and then recommends a new configuration for the user to try At the start of a tuning session, the user specifies which metric should be the target objective for the service to optimize. 需要用户指定优化目标 first tuning iteration, 1.executes the target workload on the DBMS. After the workload finishes, the controller 2.collects the runtime metrics and configuration knobs from the DBMS and 3.uploads them to the tuning manager. OtterTune’s tuning manager 4.receives the latest result from the controller and stores it in its repository. Next, the tuning manager 5.uses its ML models to generate the next knob configuration and returns it to the controller. The controller 6.applies the knob configuration to the DBMS and starts the next tuning iteration. This loop continues until the user is satisfied with the improvements over the original configuration 控制器 1○ 执行 DBMS 上的目标工作负载。工作负载完成后，控制器 2○ 收集 DBMS 的运行时指标和配置旋钮将其上传到 Tuning Manager。Ottertune 的调整管理器 4○ 收到控制器的最新结果，并将其存储在其存储库中。接下来，Tuning Manager 5○ 使用其 ML 型号生成下一个旋钮配置并将其返回到控制器。控制器 6○ 将旋钮配置应用于 DBMS，并开始下一个调整迭代。该循环继续进行，直到用户对原始配置的改进感到满意 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:4:1","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#ottertune-overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"Motivation (1) workload, (2) DBMS, and (3) operating environment. Workload Complexity: Gaining access to production workloads to evaluate new research ideas is non-trivial due to privacy constraints and other restrictions. Prior studies evaluate their techniques using synthetic benchmarks; the most complex benchmark used to evaluate ML-based tuning techniques to date is the TPC-C OLTP benchmark from the early 1990s. But previous studies have found that some characteristics of TPC-C are not representative of real-world database applications [23, 25]. Many of the unrealistic aspects of TPC-C are due to its simplistic database schema and query complexity. Another notable difference is the existence of temporary and large objects in production databases. Some DBMSs provide knobs for tuning these objects (e.g., Postgres, Oracle), which have not been considered in prior work 之前的研究都是 synthetic benchmarks, TPC-C OLTP benchmark 无法代表显示，很多不符合实际。而且许多生产数据库会有超大临时对象，以前的工作没有考虑这种 System Complexity: The simplistic nature of workloads like TPC-C means that there are fewer tuning opportunities in some DBMSs, especially for the two most common DBMSs evaluated in previous studies (i.e., MySQL, Postgres). For these two DBMSs, one can achieve a substantial portion of the performance gain from configurations generated by ML-based tuning algorithms by setting two knobs according to the DBMS’s documentation. These two knobs control the amount of RAM for the buffer pool cache and the size of the redo log file on disk TPC-C workload 简单，某些数据库调整的机会比较少，就能得到很大的性能调整（buffer pool 和 redo log file size） Postgres Knobs – SHARED_BUFFERS, MAX_WAL_SIZE MySQL Knobs – INNODB_BUFFER_POOL_SIZE, INNODB_LOG_FILE_SIZE 文章用 OtterTune 在 DDPG 算法下调节十个 knob，改善了 TPC-C 在 DBMS 的性能， More importantly, however, the configurations generated by ML algorithms achieve only 5–25% higher throughput than the two-knob configuration across the different versions of MySQL and Postgres. That is, one can achieve 75–95% of the performance obtained by ML-generated configurations by tuning only two knobs for the TPC-C benchmark 这个实验表现了只用调整两个 knob 就能在 TPC-C workload 上得到很好的吞吐量表现，比标准配置高很多 Operating Environment: Disk speed is often the most important factor in a DBMS’s performance. Although the previous studies used virtualized environments to evaluate their methods, to our knowledge, they deploy the DBMS on ephemeral storage that is physically attached to the host machine. But many real-world DBMS deployments use durable, non-local storage for data and logs, such as on-premise SANs and cloud-based block/object stores. The problem with these non-local storage devices is that their performance can vary substantially in a multi-tenant cloud environment 强调了非本地存储的情况，对比了本地存储 SSD 和云存储 VM 存储，后者延迟不好预测，前者则稳定。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:4:2","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#motivation"},{"categories":["Learned Data Systems"],"collections":null,"content":"AUTOMATED TUNING FIELD STUDY 文章的突破点在于，更加现实的自动调优 DBMS，给出了商业数据库的例子，甚至对比专业的 DBA 都有很好的表现。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:5:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#automated-tuning-field-study"},{"categories":["Learned Data Systems"],"collections":null,"content":"Target Database Application TicketTracker 数据 Database: We created a snapshot of the TicketTracker database from its production server using the Oracle Recovery Manager tool. The total uncompressed size of the database on disk is ∼1.1 TB, of which 27% is table data, 19% is table indexes, and 54% is large objects (LOBs). This LOB data is notable because Oracle exposes knobs that control how it manages LOBs, and previous work has not explored this aspect of DBMS tuning. large objects LOB 数据是以前没有探讨过的， Workload: We collected the TicketTracker workload trace using Oracle’s Real Application Testing (RAT) tool. RAT captures the queries that the application executes on the production DBMS instance starting at the snapshot. It then supports replaying those queries multiple times on a test database with the exact timing, concurrency, and transaction characteristics of the original workload [19]. Our trace is from a two-hour period during regular business hours and contains over 3.6m query invocations. 大部分查询都是 SELECT 短查询，2% 查询是多表查询，98% 都是单表。大部分查询都走了索引。 There are important differences in the TicketTracker application compared to the TPC-C benchmark used in previous ML tuning evaluations. Foremost is that the TicketTracker database has hundreds of tables and the TPC-C database only has nine. TPC-C also has a much higher write ratio for queries (46%) than the TicketTracker workload (10%). This finding is consistent with previous work that has compared TPC-C with real-world workloads [23, 25]. Prior to our study, it was unknown whether these differences affect the efficacy of ML-based tuning algorithms. 分析了 TPC-C 和该 workload 的区别，虽然这是现实例子，但它具有代表性吗？ ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:5:1","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#target-database-application"},{"categories":["Learned Data Systems"],"collections":null,"content":"Deployment DBA 实现根据文档也调整了一些 knobs， As such, for the TicketTracker workload, the DBA further customized some of the knobs in the pre-tuned configuration, including one that improves the performance of LOBs We set up multiple of OtterTune’s tuning managersand controllers in the same data center as the Oracle DBMSs. We ran each component in a Docker container with eight vCPUs and 16 GB RAM. Each DBMS instance has a dedicated OtterTune tuning manager assigned to it. This separation prevents one session from using training data collected in another session, which will affect the convergence rate and efficacy of the algorithms Oracle Knob: DB_32K_CACHE_SIZE ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:5:2","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#deployment"},{"categories":["Learned Data Systems"],"collections":null,"content":"Tuning At the beginning of each iteration in a tuning session, the controller first restarts its target DBMS instance. Restarting ensures that the knob changes that OtterTune made to the DBMS in the last iteration take effect. Some knobs in Oracle do not require restarting the DBMS, but changing them is not instantaneous and requires additional monitoring to determine when their updated values have been fully applied. To avoid issues with incomplete or inconsistent configurations, we restart the DBMS each time 需要重启，以更改 knob，这是不是一个限制？也有很多 knob 不需要重启 DBMS，但修改不是立刻见效的，还需要额外检测，所以还是需要重启。 Another issue is that Oracle could refuse to start if one of its knobs has an invalid setting. 启动失败的时候，没有错误日志，调优迭代就会停止然后报告给 tuning manager，找到下一个配置再迭代。但重启之后的恢复 workload trace 也很快？五分钟，利用了 snapshot 仅重置修改过的 pages 尽管 workload 有 1T reset 之后会跑一个 microbenchmark 评测性能，但不是必须的。This step is not necessary for tuning, and none of the algorithms use this data in their models. Instead, we use these metrics to explain the DBMSs’ performance in noisy cloud environments (see Section 6). 没太理解这里，benchmark 的结果是什么，为什么对于 tuning 不是必须的？ OtterTune’s controller executes TicketTracker’s workload trace using Oracle RAT. We limit the replay time for two reasons. First, the segment’s timespan is based on the wall clock of when the trace was collected on the production DBMS. This means that when the trace executes on a DBMS with a sub-optimal configuration (which is often the case at the beginning of a tuning session), the 10-minute segment could take several hours to complete. We halt replays that run longer than 45 minutes. The second reason is specific to Oracle: RAT is unstable on large traces for our DBMS version. Oracle’s engineers did provide SG with a fix, but only several months after we started our study, and therefore it was too late to restart our experiments replay time 是什么，为什么限制 replay time，一个是 10min segtime 可能需要几个小时完成，一个是 Oracle RAT 不稳定，尽管提供了解决方案但太晚了（可能也是另一个 limitation） ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:5:3","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#tuning"},{"categories":["Learned Data Systems"],"collections":null,"content":"TUNING ALGORITHMS (1) Gaussian Process Regression (GPR), (2) Deep Neural Network (DNN), and (3) Deep Deterministic Policy Gradient (DDPG). Although there are other algorithms that use query data to guide the search process [28], they are not usable at SG because of privacy concerns since the queries contain user-identifiable data. Methods to anonymize this data are outside the scope of this paper 为什么其他方法会设计隐私？那为什么 DDPG 不会？ ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:6:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#tuning-algorithms"},{"categories":["Learned Data Systems"],"collections":null,"content":"4.1 GPR — OtterTune (2017) Data Pre-Processing: Each invocation takes up to an hour, depending on the number of samples and DBMS metrics 具体算法略 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:6:1","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#41-gpr--ottertune-2017"},{"categories":["Learned Data Systems"],"collections":null,"content":"4.2 DNN — OtterTune (2019) Previous research has argued that Gaussian process models do not perform well on larger data sets and high-dimensional feature vectors ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:6:2","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#42-dnn--ottertune-2019"},{"categories":["Learned Data Systems"],"collections":null,"content":"4.3 DDPG — CDBTune (2019) RL 方法，OtterTune 实现了 DDPG++ We identified a few optimizations to CDBTune’s DDPG algorithm that reduce the amount of training data needed to learn the representation of the Q-value. We call this enhanced version DDPG++. ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:6:3","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#43-ddpg--cdbtune-2019"},{"categories":["Learned Data Systems"],"collections":null,"content":"EVALUATION 使用 Random sampling 方法评估 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#evaluation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Performance Variability Because each tuning session in our experiments takes multiple days to complete, we deployed the Oracle DBMS on multiple VMs to run the sessions in parallel. Our VMs run on the same physical machines during this time, but the other tenants on these machines or in the same rack may change. As discussed in Section 2.2, running a DBMS in virtualized environments with shared storage can lead to unexplained changes in the system’s performance across instances with the same hardware allocations and even on the same instance 每次 tuning session 都需要好几天？这是为什么？甚至花了六个月来检测，每周测一次。甚至同一个 VM 上 DBMS 也有性能波动？ We believe that these inconsistent results are due to latency spikes in the shared-disk storage. Figure 9 shows the DBMS’s performance for one VM during a tuning session, along with its CPU busy time and I/O latency. These results show a correlation between spikes in the I/O latency (three highlighted regions) and degradation in the DBMS’s performance. In this example, the algorithm had converged at this point of the tuning session, so the 出现了一些不一致的结果，论文认为是共享存储的一些延迟尖峰引起的，因为他们测了 VM 的性能，CPU 占用率和 IO 延迟，认为算法已经收敛，所以应该是外部因素有关，而不是 DBMS 内部问题。 为什么不是某次的 knob 调整出问题了？这里没有提到 knob 是否更改，虽然 IO 的问题应该更大一些。Performance Variability 这部分没有很好地解释这个变化到底是为什么，也不太直白。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:1","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#performance-variability"},{"categories":["Learned Data Systems"],"collections":null,"content":"Tuning Knobs Selected by DBA 让 DBA 选 40 个 knobs 来调优， Figure 11 shows that the configurations recommended by DNN and DDPG++ that tune 10 knobs improve the DB Time by 45% and 43% over the default settings, respectively. Although LHS, GPR, and DDPG achieve over 35% better DB Time, they do not perform as well as DNN and DDPG++ because they select a sub-optimal version of the optimizer features to enable DNN 和 DDPG++ 改了 10 个 knobs 提高了 45% 左右的性能，改 20 个 knobs 时提高 33-40% 性能但存在至少一个 knob 错误，论文认为是复杂度太高，算法的随机性以及 VM 的噪声引起的。但没有解释哪个原因才是根本的。 We also see that DNN has the largest gap between its minimum and maximum optimized configurations. the configurations from DNN and GPR achieve 40% better DB Time than the default configuration. DDPG and DDPG++ only achieve 18% and 32% improvement, respectively. The reason is that neither of them can fully optimize the 40 knobs within 150 iterations. DDPG++ outperforms DDPG because of the optimizations that help it converge more quickly (see Section 4.3). With more iterations, DDPG would likely achieve similar performance to the other ML-based algorithms. But due to computing costs and labor time, it was not practical to run a session for more than 150 iterations in our evaluation. The LHS configuration performs the worst of all, achieving only 10% improvement over the 150 迭代都没法优化 40 个 knobs，为什么？DDPG++ 收敛更快一些， In summary, we find that the configurations generated by all of the algorithms that tune 10, 20, and 40 knobs can improve the DBMS’s performance over the default configuration. GPR always converges quickly, even when optimizing 40 knobs. But GPR is prone to getting stuck in local minima, and once it converges, it stops exploring and thus does not continue to improve after that point. The performance of GPR, therefore, depends on whether it explores the best-observed ranges of the impactful knobs from Table 2. We also observe that its performance is influenced by the initial samples executed at the start of the tuning session. This is consistent with findings from previous studies [26]. In contrast, DNN, DDPG, and DDPG++ require more data to converge and carry out more exploration. The configurations that tune 10 knobs perform the best overall. This is because the lower complexity of the configuration space enables DNN and DDPG++ to find good settings for the impactful knobs in Table 2. GPR 可以快速收敛，但容易陷入 local minima，所以选取初始 knob 就很重要，而其他的需要更多数据。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:2","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#tuning-knobs-selected-by-dba"},{"categories":["Learned Data Systems"],"collections":null,"content":"Tuning Knobs Ranked by OtterTune 不需要 DBA 参与，使用 OtterTune Lasso 算法选择旋钮，然后用 LHS。 实际上 OtterTune 选择的 knobs 和 DBA 很近似，而且会选很重要的几个 knob 比如 DB_CACHE_SIZE LHS 配置无法识别？但是提升很大，10knobs 都有提升，但是 20knobs 只有 GPR 和 DNN 有提升，大部分要么没提升，要么就一点，甚至不超过默认配置。论文认为是当时共享存储的 noise 很多，导致了差异。但这差异也太大了。。。 论文也没怎么介绍 Lasso 算法，甚至在 10knobs 也不知道哪个算法是比较好的，有些小的改进还可能是 noise 引起的 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:3","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#tuning-knobs-ranked-by-ottertune"},{"categories":["Learned Data Systems"],"collections":null,"content":"Adaptability to Different Workload We next analyze the quality of ML-generated configurations when we train their models on one workload and then use them to tune another workload. The ability to reuse training data across workloads potentially reduces the number of iterations that the algorithms need for their models to converge. 最近看的一些调优相关的，都很注重 across workloads 用 TPC-C 训练 We examined the configurations for differences in the best-observed settings for TPC-C and TicketTracker that may explain why the algorithms were unable to achieve performance comparable to the results in Figures 11 and 13. We observed that none of the algorithms changed the sizes of the two buffer caches in Table 2 from their SG default settings. This is expected for the LOB buffer cache since none of TPC-C’s data is stored in the 32 KB tablespace. 前文提到的 LOB ，但是却没有修改 LOB buffer，因为 TPC-C 数据比较小，而他们实验是在 TicketTracker 这种很大的数据集上，实际上需要打开。 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:4","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#adaptability-to-different-workload"},{"categories":["Learned Data Systems"],"collections":null,"content":"Execution Time Breakdown DDPG++ has fewer canceled replays than DDPG due to its improved convergence rate (see Section 4.3). LHS has the highest workload execution time and percentage of canceled replays because it is a sampling technique and never converges ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:7:5","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#execution-time-breakdown"},{"categories":["Learned Data Systems"],"collections":null,"content":"LESSONS LEARNED (1) Handling Long-running Configurations: Given this, the controller needs to support an early abort mechanism that stops long-running workload replays. Setting the early abort threshold to lower values is beneficial because it reduces the total tuning time. This threshold, however, must be set high enough to account for variability in cloud environments. We found that the 45-minute cut-off worked well, but further investigation is needed on more robust methods. For early aborted configurations, the DBMS’s metrics, especially Oracle’s DB Time, are incorrectly smaller because the workload is cut off. Thus, to correct this data, the controller calculates a completion ratio as the number of finished transactions divided by the total transactions in the workload. It then uses this ratio to scale all counter metrics to approximate what they would have been if the DBMS executed the full workload trace 需要一个可以早起终止的机制 (2) Handling Failed Configurations 需要一个机制可以知道什么时候 knob 不正确 ， (3) DBMS Maintenance Tasks (4) Unexpected Cost Considerations: ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:8:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#lessons-learned"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORK 略 ","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:9:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION In this study, we conducted a thorough evaluation of machine learning-based DBMS knob tuning methods with a real workload on an Oracle installation in an enterprise environment. We implemented three state-of-the-art ML algorithms in the OtterTune tuning service in order to make a head-to-head comparison. Our results showed that these algorithms could generate knob configurations that improved performance by up to 45% over ones generated by a human expert, but the performance was influenced by the number of tuning knobs and the assistance of human experts in knob selection. We also solved several deployment and measurement issues that were overlooked by previous studies 这篇论文读着太吃力了，而且对 ML 调优的印象更加差了。做了一堆事情，好像什么都没解决，也不知道原因。 更像是 OtterTune 在现实数据库和数据集的一个尝试，但这个新的数据集区别又很大，实验结果也很莫名其妙，10-20 knobs 效果有，但是 40 knobs 效果不好又说是共享存储的问题 The paper investigates the effectiveness of using machine learning (ML) for automatic configuration and knobs tuning in real-world database management systems (an enterprise database called Oracle). Since previous ML-based tuning methods were tested on synthetic workloads and cannot represent real-world conditions, the paper conducted an evaluation of ML-based tuning methods using the OtterTune framework on an Oracle DBMS with a real-world workload trace SG and TicketTracker. The paper experiments with many ML algorithms (GPR, DNN, DDPG, DDPG++ and LHS) for automatic DBMS knob tuning, and achieves over 45% improvement with 10 and 20 tuning knobs chosen by OtterTune and compared the results with knobs chosen by DBA. And the paper also discusses the problems encountered in the experiment, such as I/O latency with shared storage, long tuning time and convergence speed. Contributions: While previous ML tuning methods mainly benchmarked on synthetic workloads and open-source DBMSs, this paper uses a real-world workload trace from a bank’s application(SG and TicketTracker) and commercial DBMS(Oracle), offering insights into the practical effectiveness of ML tuning. The paper conducts lots of experiments by using the OtterTune framework to evaluate many ML-based methods on the commercial DBMS, they tested Gaussian Process Regression (GPR) and Deep Neural Networks (DNNs), as well as Reinforcement Learning (RL) with Deep Deterministic Policy Gradient (DDPG) and DDPG++. Prior research often uses local storage like SSDs to do experiments, the paper points out that local storage can impact the performance and the effectiveness of the tuning algorithms being evaluated because shared storage is more real-world, and the I/O latency of shared storage will have an impact on tuning. Limitations: Though the paper conducts lots of experiments, there was a big difference in the experimental results when using OtterTune tuning 20 knobs. The evaluation was heavily affected by the latency of non-local storage. When the paper introduces various ML tuning methods, it does not carefully distinguish their differences. Especially when analyzing the experimental results, DDPG++ should converge faster than DDPG, but the experiments cannot present the advantages of DDPG++. The paper mentions that when using shared storage, I/O latency will lead to great variability in tuning results, but it does not provide a control group or a reproduction of the situation. Moreover, the TicketTracker database looks very different from TPC-C. The paper should try to analyze some other representative real-world workloads. The paper did a lot of experiments, but the paper also mentioned that Oracle fixed a very important issue in the later stage. I guess this will require re-experimenting, although this may be very time consuming. I may also want to try some methods to reduce the tuning time, because this will allow me to experiment with more rounds and reflect the differences between DDPG and DDPG++. In addition, I would like to conduct the experiments on other real-world workloads, which can demonstrate the ability of OtterTune and ML-based tuning met","date":"2024-04-07","objectID":"/posts/ml-based-autotunning/:10:0","tags":["Paper Reading"],"title":"Paper Reading: An Inquiry into Machine Learning-based Automatic Configuration Tuning Services on Real-World Database Management Systems","uri":"/posts/ml-based-autotunning/#conclusion"},{"categories":["Learned Data Systems"],"collections":null,"content":"MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems self-driving database management systems ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:1:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#mb2-decomposed-behavior-modeling-for-self-driving-database-management-systems"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT Database management systems (DBMSs) are notoriously difficult to deploy and administer.self-driving DBMS is to remove these impediments by managing itself automatically predict the DBMS’s runtime behavior and resource consumption. ModelBot2 e2e framework for constructing and maintaining prediction models using machine learning (ML) in self-driving DBMSs. decomposes a DBMS’s architecture into fine-grained operating units that make it easier to estimate the system’s behavior for configurations ModelBot2 then provides an offline execution environment to exercise the system to produce the training data used to train its models. We integrated ModelBot2 in an in-memory DBMS and measured its ability to predict its performance for OLTP and OLAP workloads running in dynamic environments. We also compare ModelBot2 against state-of-the-art ML models and show that our models are up to 25×more accurate in multiple scenarios 完全自治的数据库，用 ML 预测和修改配置。和同样是 Andy 组的 Ottertune 区别在哪呢？ ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:2:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION A self-driving DBMS can configure, tune, and optimize itself without human intervention, even as the application’s workload, dataset, and operating environment evolve. Such automation seeks to remove the complications and costs involved with DBMS deployments. The core component that underlies a self-driving DBMS’s decision-making is behavior models [51]. These models estimate and explain how the system’s performance changes due to a potential action (e.g., changing knobs, creating an index). This is similar to how self-driving vehicles use physical models to guide their autonomous planning [49]. Techniques for constructing database behavior models fall under two categories: (1) “white-box” analytical methods and (2) ML methods. Analytical models use a human-devised formula to describe a DBMS component’s behavior, such as the buffer pool or lock manager [42, 45, 74]. These models are customized per DBMS and version. They are difficult to migrate to a new DBMS and require redesign under system updates or reconfiguration. Recent works on using ML methods to construct models have shown that they are more adaptable and scalable than white-box approaches, but they have several limitations. These works mostly target isolated query execution [9, 17, 20, 34, 40]. The models that support concurrent queries focus on real-time settings where the interleaving of queries is known [16, 68, 72], but a self-driving DBMS needs to plan for future workloads without such accurate information [37]. Many ML-based models also rely on dataset or workload-dependent information [16, 40, 58]; thus, a DBMS cannot deploy these models in new environments without expensive retraining. white-box analytical 是什么？为什么指的是 Buffer Pool 和 Lock Manager？白盒是开发人员可以看到所有的内部组件和源码，所以能够知道 buffer pool 和 lock manager 中的内容，甚至预测吗？ 使用 ML 方法来构建模型，更适合扩展，但存在局限，之前的工作主要集中在隔离的查询，而支持并行查询的模型主要集中在实时设置并且交织查询已知。完全自治的 DBMS 无法知道准确的信息，而且一些 ML 预测模型依赖于数据集/workload 信息。甚至还需要训练。 本文提出了 ModelBot2 (MB2), generates behavior models that estimate the performance of a self-driving DBMS’s components and their interference during concurrent execution. 使得 DBMS planning components 可以推理对 action 的影响，期望收益。比如，MB2 可以回答创建索引需要多长时间，索引创建会怎么影响系统性能，新索引会如何加速 workload\u0026rsquo;s query。 MB2 主要思想是分解 DBMS 的内部结构，分成了几个小的、互相独立的操作单元 operating units (OUs) (e.g., building a hash table, flushing log records). MB2 then uses ML methods to train an OU-model for each OU that predicts its runtime and resource consumption for the current DBMS state。 小的 OU 单元模型需要更少的训练时间，并且对每个 DBMS 组件都有性能 insight。推理时，MB2 结合所有 OU-models 预测 DBMS 对未来的 workload 的性能和系统状态（工作量也是预测的）。 multi-core environments with concurrent threads, MB2 also estimates the interference between OUs by defining the OUmodels’ outputs as a set of measurable performance metrics that summarizes each OU’s behavior 拆成更小单元，训练每个小的，联合预测。评测结果支持 OLTP OLAP 工作负载，with a minimal loss of accuracy ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:3:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"BACKGROUND AND MOTIVATION 类比 self-driving DBMS 和无人驾驶，同样有 1. forecasting 2. behavior model 3. planning system The forecasting system is how the DBMS observes and predicts the application’s future workload The DBMS then uses these forecasts with its behavior models to predict its runtime behavior relative to the target objective function (e.g., latency, throughput). The DBMS’s planning system selects actions that improve this objective function 预测 + action + 目标函数 Behavior models 是基础 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:4:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#background-and-motivation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Behavior Modeling Given an action, a self-driving DBMS’s behavior models estimate (1) how long the action takes, (2) how much resource the action consumes, (3) how applying the action impacts the system performance, and (4) how the action impacts the system once it is deployed. 对比 Analytical models 和 Behavir Model，前者最近可以用 ML 分析，用 query plan information (cardinality estimates) 估计性能参数 (latency) 尽管 ML 方法可扩展，但还需要调整或重新训练，而且不支持事务 workload，也没考虑 DBMS 维护操作（GC）。一些方法支持并发查询，但对完全自治的 DBMS 还不够，因为未来工作量未知。 举了个例子，如果删除二级索引，DBMS 会重新加回来，启动之前会用 planning component 和 behavior model 预测什么索引比较好，还会选择创建索引的线程数。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:4:1","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#behavior-modeling"},{"categories":["Learned Data Systems"],"collections":null,"content":"Challenges ML 方法建造一个自治 DBMS 会遇到的问题： High Dimensionality 高纬度，一个大模型捕捉 DBMS 所有方面，workload, configuration, actions. 自治 DBMS 还需要考虑 runtime state (e.g., database contents, knob configurations), interactions with other components (e.g., garbage collection), and other autonomous actions (e.g., building indexes), which further increases dimensionality Concurrent Operations 并行操作，动态环境 query 之间的干涉 interference，资源竞争。 Training, Generalizability, and Explainability，资源收集比较困难，构建索引耗时很久。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:4:2","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#challenges"},{"categories":["Learned Data Systems"],"collections":null,"content":"OVERVIEW MB2, embedde behavior modeling framework for self-driving DBMS. MB2 generates model offline in a dataset and workload independent manner MB2\u0026rsquo;s models are debuggable, explainable, and adaptable. decompose the DBMS into independent operating units (OUs). An OU represents a step that the DBMS performs to complete a specific task. These tasks include query execution steps (e.g., building join hash tables (JHTs)) and internal maintenance steps (e.g., garbage collection). DBMS developers are responsible for creating OUs and deciding their boundaries based on the system’s implementation. MB2 pairs each OU with an OU-runner that exercises the OU’s corresponding DBMS component by sweeping the component’s input parameter space. MB2 then provides a lightweight data collection layer to transform these OU-runners’ inputs to OU features and track the OU’s behavior metrics (e.g., runtime, CPU utilization). For each OU, MB2 automatically searches, trains, and validates an OU-model using the data collected from the related OU-runner simulate concurrent enviornments, MB2 uses concurrent runners to execute e2e workloads. 将数据库操作拆成 OU 和对应的 OU runner，比如创建索引、垃圾回收、Flush Log，开发者来提供 OU 集合。OU runner 用于训练 OU 模型，需要知道有多少 tuple, column 等等，以及 output feature: CPU, memory utilization, IO 等等。 MB2 also estimates the effect of building the index on the regular workload by converting its queries into OUs and predicting their performance. The DBMS’s planning system then decides whether to build this index and provides explanations for its decision based on these detailed predictions Assumptions and Limitations: framework uses a forecasting system to generate estimations for future workload arrival rates in fixed intervals (e.g., a minute/hour). The workload forecasting system cannot predict ad-hoc queries it has never seen before. Thus, we assume the DBMS executes queries with a cached query plan except for the initial invocation the target system is an in-memory DBMS. MB2 does not support disk-oriented DBMSs with buffer pools. This assumption simplifies MB2’s behavior models since it does not have to consider what pages could be in memory for each query. Estimating cache contents is difficult enough for a single query. It is more challenging when evaluating a sequence of forecasted queries. MB2 supports OLTP and OLAP, and mixed workloads. We assume that the DBMS uses MVCC and MB2 supports capturing lock contention. MB2 does not, however, model transaction aborts due to data conflicts because it is challenging to get precise forecasts of overlapping queries MB2’s OU-models’ input features contain the cardinality estimation from the DBMS optimizer, which is known to be error-prone Our evaluation shows that MB2’s prediction is insensitive against cardinality estimation errors within a reasonable range (30%). There are recent works that use ML to improve an optimizer’s cardinality estimations , which MB2 may leverage Lastly, while MB2 supports hardware context in its models (see Sec. 4.2), we defer the investigation on what features to include for different hardware (e.g., CPU, disk) and environments (e.g., bare-metal, container) as future work. 没法预测 ad-hoc 用户自己的数据集，预测缓存非常困难。也只支持内存数据库。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:5:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"OU-MODELS create a DBMS’s OU-models with MB2. The goal of these models is to estimate the time and resources that the DBMS will consume to execute a query or action. A self-driving DBMS can make proper planning decisions by combining these estimates from multiple queries in a workload forecast interval. In addition to accuracy, these models need to have three properties that are important for self-driving operations: (1) they provide explanations of the DBMS’s behavior, (2) they support any dataset and workload, and (3) they adapt to DBMS software updates 除了精确度，能够提供解释、支持任何数据集、支持动态更新 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:6:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#ou-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Principles Developers use MB2 to decompose the DBMS into OUs to build explainable, adaptable, and workload independent behavior models. 开发人员来做 decompose, 因为 NoisePage 是 CMU 自己的关系型数据库，可以拆除很多操作单元，比如 Hash Table Build, Probe 等等 Independent: The runtime behavior of an OU must be independent of other OUs. Thus, changes to one OU do not directly affect another unrelated OU. For example, if the DBMS changes the knob that controls the join hash table size then this does not change the resource consumption of the DBMS’s WAL component or the resource consumption of sequential scans for the same queries. 比较好奇怎么拆成独立的 OU，如果改 join hash table size，会不会影响 join table probe OU 呢 Low-dimensional: An OU is a basic operation in the DBMS with a small number of input features. Comprehensive: Lastly, the framework must have OUs that encompass all DBMS operations which consume resources. Thus, for any workload, the OUs cover the entire DBMS, including background maintenance tasks (e.g., garbage collection) and self-driving actions that the DBMS may deploy on its own (e.g., index creation). OU 到底是对 DBMS 设置，还是针对数据集设置的？ ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:6:1","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#principles"},{"categories":["Learned Data Systems"],"collections":null,"content":"Input Features After deciding which OUs to implement, DBMS developers then specify the OU-models’ input features based on their degree of freedom the amount of work for a single OU invocation or multiple OU invocations in a batch (the number of tuples to process) the parallel invocation status of an OU (e.g., number of threads to create an index), and the DBMS configuration knobs (e.g., the execution mode). degree of freedom 统计学概念？ Although humans select the features for each OU-model, the features’ values are generated automatically by the DBMS based on the workload and actions. Some features are generic and will be the same across many DBMSs, whereas others are specific to a DBMS’s implementation. We categorize OUs into three types based on their behavior pattern, which impacts what information the input features have Singular OUs: The first type of OUs have input features that represent the amount of work and resource consumption for a single invocation. These include NoisePage’s execution category OUs in Table 1. Almost all its execution OUs have the same seven input features. The first six features are related to the relational operator that the OU belongs to: (1) number of input tuples, (2) number of columns of input tuples, (3) average input tuple size, (4) estimated key cardinality (e.g., sorting, joins), (5) payload size (e.g., hash table entry size for hash joins), and (6) number of loops (only for index nested loop joins). Lastly, the seventh feature is an execution mode flag that is specific to NoisePage; this indicates whether the DBMS executes a query with its interpreter or as JIT-compiled. NoisePage’s networking OU also belongs to this type since network communication is discrete amount of work. Batch OUs: The second type of OUs have input features that represent a batch of work across multiple OU invocations. It is challenging to derive features for these OUs since a single invocation may span multiple queries based on when those queries arrive and the invocation interval. (log flushes) To address this, we define the log flush OU’s input features to represent the total amount of records generated by the set of queries predicted to arrive in a workload forecasting interval: (1) the total number of bytes, (2) the total number of log buffers, and (3) the log flush interval. These features are independent of what plans the DBMS chooses for each query Contending OUs: The last type of OUs are for operations that may incur contention in parallel invocations. 独立的 OU，批处理 OU，竞争 OU。第一个可以迁移，因为 input 类似，比如行数、列数、行大小、基数等等。第二个可能跨 query 比如日志落盘。第三个是并行可能引起锁竞争的 OU，比如多线程构建索引，需要知道行数、key 数量、key 大小、key 基数估计、等等 A self-driving DBMS must also predict how changes to its configuration knobs impact its OUs. We categorize these tunable knobs into behavior knobs and resource knobs. ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:6:2","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#input-features"},{"categories":["Learned Data Systems"],"collections":null,"content":"Output Labels Every OU-model produces the same output labels, (1) elapsed time, (2) CPU time, (3) CPU cycles, (4) CPU instructions, (5) CPU cache references, (6) CPU cache misses, (7) disk block reads, (8) disk block writes (for logging), and (9) memory consumption. These metrics explain what work an OU does independent of which OU it is. Using the same labels enables MB2 to combine them together to predict the interference among concurrent OUs. They also help the self-driving DBMS estimate the impact of knobs for resource allocation. For example, the OU-models can predict each query’s memory consumption by predicting the memory consumption for all the OUs related to the query. A self-driving DBMS evaluates what queries can execute under the knob that limits the working memory for each query, and then sets that knob according to its memory budget. Similarly, CPU usage predictions help a self-driving DBMS evaluate whether it has assigned enough CPU resources for queries or its internal components OU-model 输出 label，所以有利于 debug Output Label Normalization: We now discuss how MB2 normalizes OU-models’ output labels by tuple counts to improve their accuracy and efficiency. DBMSs can execute queries that range from accessing a single tuple (i.e., OLTP workloads) to scanning billions of tuples (i.e., OLAP workloads). The former is fast to replicate with OU-runners, but the latter is expensive and time-consuming. To overcome this issue, MB2 employs a normalization method inspired by a previous approach for scaling query execution modeling [34]. We observe that many OUs have a known complexity based on the number of tuples processed, which we denote as 𝑛. For example, if we fix all the input features except for 𝑛, the time and resources required to build a hash table for a join are O(𝑛). Likewise, the time and resources required to build buffers for sorting are O(𝑛 log 𝑛). We have found in our evaluations with NoisePage that with typically less than a million tuples, the output labels converge to the OU’s asymptotic complexity multiplied by a constant factor 如何处理 label？使用 normalize, divides the outputs by the related OU’s complexity based on the number of tuples, 但是构建哈希表时，由于 NoisePage 使用不同的哈希表处理 join 和 agg：预先分配内存给 join，哈希表会对插入的 unique key 额外增长。所以 MB2 需要用除法来归一化。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:6:3","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#output-labels"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTERFERENCE MODEL The OU-models capture the internal contention on data structures or latches within an OU (Sec. 4.2). But there can also be interference among OUs due to resource competition, such as CPU, I/O, and memory.2 MB2 builds a common interference model to capture such interference since it may impact any OU. 论文也观察到，OU 之间也存在资源共享，比如连续运行的 OU 会有缓存局部性。但论文说这是极端情况。 Building such an interference model has two challenges: (1) there are an exponential number of concurrent OU combinations, and (2) self-driving DBMSs need to plan actions ahead of time [50], but predicting queries’ exact future arrival times and concurrent interleavings is arguably impossible 需要提前预测，但不能精确预测并行发生的时间，并且 OU 组合是指数级的 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:7:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#interference-model"},{"categories":["Learned Data Systems"],"collections":null,"content":"Input Features The interference model’s inputs are the OU-model’s output labels for the OU to predict and summary statistics of the OUs forecasted to run in the same interval (e.g., one minute). ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:7:1","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#input-features-1"},{"categories":["Learned Data Systems"],"collections":null,"content":"Output Labels The interference model generates the same set of outputs as the OU-models. ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:7:2","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#output-labels-1"},{"categories":["Learned Data Systems"],"collections":null,"content":"DATA GENERATION AND TRAINING MB2 is an end-to-end solution that enables self-driving DBMSs to generate training data from OUs and then build accurate models that predict their behavior. MB2 如何有利于收集数据和训练模型，并且再次强调需要在离线情况下，非生产环境，运行 MB2。推迟了如何在在线系统收集数据，而不会导致性能观察退化的问题。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:8:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#data-generation-and-training"},{"categories":["Learned Data Systems"],"collections":null,"content":"Data Collection Infrastructure OU Translator: This component extracts OUs from query and action plans and then generates their corresponding input features. MB2 uses the same translator infrastructure for both offline training data collection and runtime inference. Resource Tracker: Next, MB2’s tracker records the elapsed time and resource consumption metrics (i.e., output labels) during OUs execution. The framework also uses this method for the interference model data since it uses the same output labels to adjust the OU-models’ outputs. Metrics Collector: The challenges with collecting training data are that (1) multiple threads produce metrics and thus require coordination, and (2) resource tracker can incur a noticeable cost. It is important for MB2 to support low-overhead metrics collection to reduce the cost of accumulating training data and interference with the behavior of OUs, especially in concurrent environments MB2 uses a decentralized metrics collector to address the first issue. When the DBMS executes in training mode, a worker thread records the features and metrics for each OU that it encounters in its thread-local memory. MB2 then uses a dedicated aggregator to periodically gather this data from the threads and store it in the DBMS’s training data repository. To address the second challenge, MB2 supports resource tracking only for a subset of queries or DBMS components. For example, when MB2 collects training data for the OUs in the execution engine, the DBMS can turn off the tracker and metrics collector for other components 数据收集的架构 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:8:1","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#data-collection-infrastructure"},{"categories":["Learned Data Systems"],"collections":null,"content":"OU-Runners An OU-runner is a specialized microbenchmark in MB2 that exercises a single OU. The goal of each OU-runner is to reproduce situations that an OU could encounter when the DBMS executes a real application’s workload. The OU-runners sweep the input feature space (i.e., number of rows, columns, and column types) for each OU with fixed-length and exponential step sizes, which is similar to the grid search optimization There are two ways to implement OU-runners: (1) low-level execution code that uses the DBMS’s internal API and (2) high level SQL statements. We chose the latter for NoisePage because it requires less upfront engineering effort to implement them, and has little to no maintenance costs if the DBMS’s internal API changes 用 SQL statement 实现 OU runner 是怎么做到的 MB2 supports modeling OLTP and OLAP workloads. To the best of our knowledge, we are the first to support both workload and data-independent modeling for OLTP query execution. Prior work either focused on modeling OLAP workloads [34, 40, 68] or assumes a fixed set of queries/stored procedures in the workload [42, 45]. Modeling the query execution in OLTP workloads is challenging for in-memory DBMSs: since OLTP queries access a small number of tuples in a short amount of time, spikes in hardware performance (e.g., CPU scaling), background noise (e.g., OS kernel tasks), and the precision of the resource trackers (e.g., hardware counters) can inflict large variance on query performance. Furthermore, DBMSs typically execute repeated OLTP queries as prepared statements 第一个支持 OLAP 和 OLTP 数据集，并且是 workload and data-independent 的，支持 OLTP。但 NoisePage 本身就是事务数据库，MB2 是怎么做到这种特性的。 MB2 executes the OU-runners for the OUs in the execution engine with sufficient repetitions (10×) and applies robust statistics to derive a reliable measurement of the OU’s behavior. MB2 executes each query for five warm-up iterations before taking measurements for the query’s OUs, with all executions of a given query using the same query template. MB2 starts a new transaction for each execution to avoid the data residing in CPU caches. For queries that modify the DBMS state, MB2 reverts the query’s changes using transaction rollbacks. We find the labels insensitive to the trimmed mean percentage and the number of warm-up iterations. 多次重复和 warm up，所以这是没法在 磁盘 DBMS 使用 MB2 的原因吗，没有办法预测 buffer pool 中的内容，那 NoisePage 有缓存吗？ MB2 starts a new transaction for each execution to avoid the data residing in CPU caches. For queries that modify the DBMS state, MB2 reverts the query’s changes using transaction rollbacks. We find the labels insensitive to the trimmed mean percentage and the number of warm-up iterations. 每次都是用事务，避免了 CPU data cache，并且用 rollback 改回了 DBMS 的状态。label 和 trimmed mean percentage and warmup iterations 不敏感。那为什么要 warm up？ ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:8:2","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#ou-runners"},{"categories":["Learned Data Systems"],"collections":null,"content":"Concurrent Runners Since OU-runners invoke their SQL queries one at a time, MB2 also provides concurrent runners that execute end-to-end benchmarks (e.g., TPC-C, TPC-H). These concurrent runners provide MB2 with the necessary data to train its interference model ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:8:3","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#concurrent-runners"},{"categories":["Learned Data Systems"],"collections":null,"content":"Model Training Lastly, we discuss how MB2 trains its behavior models using the runner-generated data. Since OUs have different input features and behaviors, they may require using different ML algorithms that are better at handling their unique properties and assumptions about their behavior. For example, Huber regression (a variant of linear regression) is simple enough to model the filter OUs with arithmetic operations. In contrast, sorting and hash join OUs require more complex models, such as random forests, to support their behaviors under different key number, key size, and cardinality combinations 使用不同的 ML 方法，那怎么选？ MB2 trains multiple models per OU and then automatically selects the one with the best accuracy for each OU. MB2 currently supports seven ML algorithms for its models: (1) linear regression, (2) Huber regression, (3) support vector machine, (4) kernel regression, (5) random forest, (6) gradient boosting machine, and (7) deep neural network. For each OU, MB2 first trains an ML model using each algorithm under the common 80/20% train/test data split and then uses cross-validation to determine the best ML algorithm to use. MB2 then trains a final OU-model with all the available training data using the best ML algorithm determined previously. Thus, MB2 utilizes all the data that the runners collect to build the models. MB2 uses this same procedure to train its interference models. 自动选了 ML 方法（精度最高的） ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:8:4","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#model-training"},{"categories":["Learned Data Systems"],"collections":null,"content":"HANDLING SOFTWARE UPDATES 讨论了 DBMS 可以更新（bug 修复，性能提升等等）的情况下，自动驾驶的 DBMS 应该怎么处理。因为 OUs 之间是独立的，MB2 只需要重新跑受影响的 OU-runners 。 This restricted retraining is possible because the OUs are independent of each other. The OU-runners issue SQL queries to the DBMS to exercise the OUs, which means that developers do not need to update them unless there are changes to the DBMS’s SQL syntax. Furthermore, MB2 does not need to retrain its interference models in most cases because resource competition is not specific to any OU. In NoisePage, we currently use a heuristic for MB2 to retrain the interference models when a DBMS update affects at least five OUs 不需要重新训练干扰模型，只需要重新跑 OU runner。除非 DBMS 引入了新的 OU behavior 比如新的组件，MB2 就需要重新运行 OU concurrent runners 生成干扰模型。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:9:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#handling-software-updates"},{"categories":["Learned Data Systems"],"collections":null,"content":"EXPERIMENTAL EVALUATION NoisePage DBMS: OLTP-Bench testbed as an end-to-end workload generator for the SmallBank, TATP , TPC-C, and TPC-H benchmarks. OLAP 和 OLTP 都测了，使用不同的指标 We use two evaluation metrics. For OLAP workloads, we use the average relative error ( |𝐴𝑐𝑡𝑢𝑎𝑙−𝑃𝑟𝑒𝑑𝑖𝑐𝑡| 𝐴𝑐𝑡𝑢𝑎𝑙 ) used in similar prediction tasks in previous work . Since OLTP queries have short run-times with high variance, their relative errors are too noisy to have a meaningful interpretation. Thus, we use the average absolute error ( |𝐴𝑐𝑡𝑢𝑎𝑙 −𝑃𝑟𝑒𝑑𝑖𝑐𝑡|) per OLTP query template ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#experimental-evaluation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Data Collection and Training MB2’s data collection and model training. 看上去训练数据不是很大，训练时间也不是很久。 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:1","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#data-collection-and-training"},{"categories":["Learned Data Systems"],"collections":null,"content":"OU-Model Accuracy the OU-model test relative error averaged across all output labels. More than 80% of the OU-models have an average prediction error less than 20%, which demonstrates the effectiveness of the OU-models. OU 模型的预测误差小于 20%，证明了有效性。但没看懂这个结果，用不同的 ML 方法得到的误差都不同，有的很低有的很高，比如随机森林总是很低的误差，论文也提到 OU 模型维度很低，容易出现过拟合。 we show the predictive accuracy of the OU-models for each output label, averaging across all OUs. Most labels have an average error of less than 20%, where the highest error is on the cache miss label. Accurately estimating the cache misses for OUs is challenging because the metrics depend on the real-time contents of the CPU cache. Despite the higher cache miss error, MB2’s interference model still captures the interference among concurrent OUs (see Sec. 8.4) because the interference model extracts information from all the output labels. The results in Fig. 6 also show the OU-model errors without output label normalization optimization from Sec. 4.3. From this, we see that normalization has minimal impact on OU-model accuracy while enabling generalizability 看上去结果很容易受到 CPU 缓存影响。但前文又说影响不大？能够预测 query runtime 和 workload ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:2","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#ou-model-accuracy"},{"categories":["Learned Data Systems"],"collections":null,"content":"OU-Model Generalization For a state-of-the-art baseline, we compare against the QPPNet ML model for query performance prediction QPPNet uses a tree-structured neural network to capture a query plan’s structural information. It outperforms other recent models on predicting query runtime especially when generalizing the trained model to different workloads (e.g., changing dataset sizes) Since NoisePage is an in-memory DBMS with a fused-operator JIT query engine, we remove any disk-oriented features from QPPNet’s inputs and adapt its operator-level tree structure to support pipelines. But such adaptation requires QPPNet’s training data to contain all the operator combinations in the test data pipelines to do inference with the proper tree structure. Thus, we can only train QPPNet on more complex workloads (e.g., TPC-C) and test on the same or simpler workloads (e.g., SmallBank) 还是强调了 NoisePage 是内存数据库，而且是 JIT 的 We evaluate MB2 and QPPNet on the (1) TPC-H OLAP workload and (2) TPC-C, TATP, and SmallBank OLTP workloads. To evaluate generalizability, we first train a QPPNet model with query metrics from a 1 GB TPC-H dataset and evaluate it on two other dataset sizes (i.e., 0.1 GB, 10 GB). 在 TPCH 上训练和评估。QPPNET 的准确性非常好，但在其他数据集上错误率很高。 而 MB2 比 QPPNET 高 25x 的精度，并且所有 workload size 上预测的精确度都很稳定。 We attribute this difference to how (1) MB2 decomposes the DBMS into fine-grained OUs and the corresponding OU-runner enumerates various input features that cover a range of workloads, and (2) MB2’s output label normalization technique further bolsters OU-models’ generalizability. 最重要的两个因素，分解成小的 OU，将结果 label normalization，所以能够泛化。 Even though the 10 GB TPC-H workload has tables up to 60m tuples, which is 60× larger than the largest table considered by MB2’s OU-runners, MB2 is still able to generalize with minimal loss of accuracy. MB2 without the output normalization has much worse accuracy on large datasets. 再次强调 normalization 的重要性 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:3","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#ou-model-generalization"},{"categories":["Learned Data Systems"],"collections":null,"content":"Interference Model Accuracy We next measure the ability of MB2’s interference model to capture the impact of resource competition on concurrent OUs. We run the concurrent runner with the 1 GB TPC-H benchmark since it contains a diverse set of OUs. The concurrent runner enumerates three parameters: (1) subsets of TPC-H queries, (2) number of concurrent threads, and (3) query arrival rate. Since the interference model is not specific to any OU or DBMS configuration, the concurrent runner does not need to exercise all OU-model inputs or knobs. For example, with the knob that controls the number of threads, we only generate training data for odd-numbered settings (i.e., 1, 3, 5, . . . 19) and then test on even-numbered settings. The concurrent runner executes each query setup for 20s. To reduce the evaluation time, we assume the average query arrival rate per query template per 10s is given to the model. In practice, this interval can be larger. Neural network performs the best for this model given its capacity to consume the summary statistics of OU-model output labels To evaluate the model’s generalizability, the concurrent runner executes queries only in the DBMS’s interpretive mode (execution knob discussed in Sec. 4.2), but we test the model under JIT compilation mode. We also evaluate the model with thread numbers and workload sizes that are different from those used by MB2’s concurrent runners. To isolate the interference model’s estimation, we execute the queries in both single-thread and concurrent environments and compare the true adjustment factors (the concurrent interference impact) against the predicted adjustment factors 什么是 interpretive mode？ the actual and interference model-estimated average query run times under concurrent environments. The interference model has less than 20% error in all cases. It generalizes to these environments because (1) it leverages summary statistics of the OU-model output labels that are agnostic to specific OUs and (2) the elapsed time-based input normalization and ratio-based output labels help the model generalize across various scenarios. We also observe that generalizing the interference model to small data sizes result in the highest error (shown under TPC-H 0.1 GB in Fig. 8b) since the queries are shorter with potentially higher variance in the interference, especially since the model only has the average query arrival information in an interval as an input feature 还是 normalization ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:4","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#interference-model-accuracy"},{"categories":["Learned Data Systems"],"collections":null,"content":"Model Adaptation and Robustness 噪声影响，noisy estimation ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:5","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#model-adaptation-and-robustness"},{"categories":["Learned Data Systems"],"collections":null,"content":"Hardware Context Since MB2 generates models offline, their predictions may be inaccurate when the hardware used for training data generation and production differs. 在不同的 CPU 频率上训练，带来的误差也不同，而且看上去受影响很大？但为什么是跟主频有关？是因为训练的时候，错误估计了查询时间吗？所以可以扩展多一些参数吗？ ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:6","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#hardware-context"},{"categories":["Learned Data Systems"],"collections":null,"content":"End-to-End Self-Driving Execution demonstrate MB2’s behavior models’ application for a self-driving DBMS’s planning components and show how it enables interpretation of its decision-making process. We assume that the DBMS has (1) workload forecasting information of average query arrival rate per query template in each 10s forecasting interval. and (2) an “oracle” planner that makes decisions using predictions from behavior models. We assume a perfect workload forecast to isolate MB2’s prediction error 创建索引时，runtime，cpu 占用率都预测得很好，是 self-driving database 的基础 This example demonstrates that MB2’s behavior models accurately estimate the cost, impact, and benefit of actions for self-driving DBMSs ahead of time given the workload forecasting, which is a foundational step towards building a self-driving DBMS demonstrate MB2’s estimations under an alternative action plan of the self-driving DBMS in Fig. 11c. The DBMS plans the same actions under the same setup as in Fig. 11a except to build the index earlier (at 58s) with four threads to reduce the impact on the running workload. MB2 accurately estimates the smaller query runtime increment along with the workload being impacted for longer. Such a trade-off between action time and workload impact is essential information for a self-driving DBMS to plan for target objectives (e.g., SLAs). We also observe that MB2 underestimates the index build time under this scenario by 27% due to a combination of OU and interference-model errors. 但也存在预测错误的情况 ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:10:7","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#end-to-end-self-driving-execution"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORK We broadly classify the previous work in DBMS modeling into the following categories: ML models analytical models. Related to this are other methods for handling concurrent workloads. We also discuss other efforts on building automated DBMSs and reinforcement learning-based approaches Machine Learning Models: Most ML-based behavior models use query plan information as input features for estimating system performance. ML 方法需要开发人员调整 feature, 收集数据，重新训练以适应新环境。并且很多 ML 模型都是在合成的小 benchmark 上进行的，所以在其他 workload 上有很大的预测错误。并且不支持事务 workloads，并且忽略了 DBMS 内部操作。而 MB2 就拆分了小的 OU 操作，并且用 SQL 所以只用少量重新训练，而且适应更新。 Analytical Models: Researchers have also built analytical models for DBMS components. Concurrent Queries: Due to the difficulty of modeling concurrent operations as described in Sec. 2.2, the aforementioned techniques largely focus on performance prediction for one query at a time. For concurrent OLAP workloads, researchers have built sampling- based statistical models that are fixed for a known set of queries. Autonomous DBMSs: There is a long history of research on automating DBMS deployments 这里也提到了同样是 CMU 的 Ottertune，但 Ottertune 是 Gaussian Process Regression to model how a DBMS will perform with different knob settings. Reinforcement Learning (RL) for DBMSs: Recent work ex- plores using RL [55, 59] to enhance individual DBMS components [39, 48, 75], which are independent of MB2’s goal to build behavior models for self-driving DBMSs that automate all the administrative tasks. We use a modularized design (Sec. 2) for self-driving DBMSs, which has a different methodology than RL-based approaches. We think this approach provides better data efficiency, debuggability, and explainability, which are essential for the system’s practical application. All major organizations working on self-driving cars also use a modularized approach instead of end-to-end RL 没理解为什么用模块化方法而不是端到端 RL ","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:11:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION Behavior modeling is the foundation for building a self-driving DBMS. We propose a modeling framework ModelBot2 (MB2) that decomposes the DBMS into operating units (OUs) and builds models for each OU with runners that exercise OUs’ behavior. Our evalua- tion shows that MB2’s behavior models provide the essential cost, impact, and benefit estimations for actions of self-driving DBMSs and generalize to various workloads and environments. 主要讲了怎么建模 behavior model，怎么拆分 Operation Unit 然后用 ML 方法做预测，还有很重要的 normalization。结合起来就是 self-driving DBMS 的基础。 中间很多地方没怎么看懂，好像就是在特殊的数据库上拆出各种单元任务，用各种不同的 ML 方法预测，然后 normalize 结果。但 normalize 部分不太清楚，怎么选 ML 方法看上去也玄学。OU 选的也挺少的，而且无法处理 disk-oriented DBMS，假设还是挺强的。 让数据库自动驾驶还是非常吸引人的，尤其是优化。但数据库还是太复杂了，一个框架无法处理所有的情况，而且数据非常难收集，不同数据不同方法也差异很大，论文很有意思的一点就是用汽车的自动驾驶类比数据库的自动驾驶，是很相似的，都需要感知，知道有什么工作负载，也需要预测未来的工作负载，使其能够知道怎么优化。第二步就是优化，比如建索引、调参数，第三步是需要知道在什么时候优化，比如负载低的时候建立索引，还要知道操作带来的影响。 再一个比较重要的地方是，MB2 考虑了多线程并发的情况，这很重要，尤其是现在的数据库大部分都是支持并发的。 MB2 实际上是结合了机器学习建模和分析方法建模，但大部分情况下 gradient boosting 模型表现最好，但是是根据怎么指标来确定的呢？ 数据对自治数据库的实现也是非常重要的 Summarize: The paper proposes a modeling framework called ModelBot2(MB2) for building a self-driving DBMS. MB2 decomposes DBMS into many fine-grained and independent operating units(OUs) such as building a hash table and interference model for concurrent operations such as log record flush. MB2 trains these models with different ML methods like gradient boosting, and use different input features to estimate system performance metrics. The experiment result shows that MB2 is able to predict DBMS performance more accurately than the state-of-the-art ML models called QPPNet, especially under dynamic different workload scenarios. MB2 performs behavioral modeling on DBMS and predicts the impact and benefit of automated operations of various datasets(OLAP and OLTP), which is foundational for establishing an self-driving database. Contributions: MB2 breaks down the database management system\u0026rsquo;s functionality into smaller, independent units called operating units (OUs), each OU can have a model trained to predict its runtime and resource consumption. Using decomposed OUs to build behavioral models not only reduces the high-dimensionality of the problem, but each OU requires less training data and gives more fine-grained predictions. MB2 also proposes a interference model, which captures contention on locks and data structures through individual OU models. Additionally, a separate interference model is designed to predict contention on hardware resources such as memory, CPU, and I/O. This model outputs summary statistics to generalize to various concurrent OUs. MB2 integrates the behavior model and interference model with NoisePage, which is a relational database management system from CMU. MB2 can predict the system’s performance for OLTP, OLAP and even mixed workloads in dynamic environments, while most ML-based models do not support transcational workloads. MB2 achieves this by applying output normalization techniques, and it outperforms QPPNet particularly in terms of generalizability and performance across different workloads. Limitations: MB2 is limited to train on an offline enviornment. Though MB2 is adaptive to dynamic workload and even transcational workload, it still needs retrain when there are some DBMS updates. I\u0026rsquo;m not an expert in database tuning, but maybe some OUs in MB2 still need to be retrained when knobs and configurations are changed. MB2 is not be directly designed for predicting disk-oriented database performance, and it uses in-memory database. Since MB2 might not be able to predict the cache content in CPU cache runs or buffer pool. And MB2 needs warm up and enough repetitions to be able to predict accurately. This may cause the CPU cache and buffer pool to cache a lot of data, resulting in inaccurate predictions. MB2 is designed to be a self-driving DBMS, however, the initial setup of MB2 needs developers to decompose the DBMS’s architecture into operating unit","date":"2024-04-05","objectID":"/posts/mb2-self-driving-db/:12:0","tags":["Paper Reading"],"title":"Paper Reading: MB2: Decomposed Behavior Modeling for Self-Driving Database Management Systems","uri":"/posts/mb2-self-driving-db/#conclusion"},{"categories":["Database"],"collections":null,"content":"Mini-LSM Week 1 Day2 Week1 Day2 的内容，实现 Merge Iterator https://skyzh.github.io/mini-lsm/week1-02-merge-iterator.html ","date":"2024-03-23","objectID":"/posts/minilsm-2/:1:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#mini-lsm-week-1-day2"},{"categories":["Database"],"collections":null,"content":"Merge Iterator 本次需要实现： Memtable Iterator Merge Iterator LSM read path scan for memtables ","date":"2024-03-23","objectID":"/posts/minilsm-2/:2:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#merge-iterator"},{"categories":["Database"],"collections":null,"content":"Task1: Memtable Iterator 修改 src/mem_table.rs，实现 scan 接口，在一组 key-value pairs 上创建 iterator API 来迭代。在上一节已经实现了 get 和创建 immutable memtable 的逻辑，此时 LSM state 有多个 memtables。所以 scan 需要在一个 memtable 上创建 iterator，然后在所有 memtables 上创建一个 merge iterator 所有 LSM iterators 在 lsm_iterator.rs 中的 StorageIterator 特征，有四个函数 key, value, next 和 is_valid。当 iterator 创建后，cursor 会停在某些元素，key/value 会返回 memtable/block/SST 中的第一个满足 start condition 的 key，比如 start key。这两个接口会返回 \u0026amp;[u8] 引用类型防止 copy。本节的 iterator interface 和 Rust iterator 不太一样。 next 将 cursor 移动到下一个元素，is_valid 检查 iterator 是否终止或者发生错误。可以假设 next 会在 is_valid 返回成功时才会被调用。FusedIterator 是一个 wrapper 包含了所有的 iterator，会防止不正确的 next 调用，比如 not valid。 回到 memtable iterator, 这个结构体没有任何生命周期。如果你创建了一个 Vec\u0026lt;u64\u0026gt; 并且调用 vec.iter()，这个 iterator 类型会是 VecIterator\u0026lt;'a\u0026gt; 表示 vec 的生命周期。同样地，SkipMap 也是一样的，iter 接口返回一个带生命周期的 iterator。但是，在 Mini LSM 中，我们不希望这样的生命周期出现在迭代器中，使得整个系统变得过分复杂和难以编译。 如果迭代器没有生命周期参数 lifetime generics parameter，我们应该保证不管什么时候用 iterator，底层的 skiplist 都不应该被释放。这样的唯一做法是将 Arc\u0026lt;SkipMap\u0026gt; 对象放到 iterator 本身： pub struct MemetableIteraotr { map: Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt;, iter: SkipMapRangeIter\u0026lt;\u0026#39;???\u0026gt;, } 题外话，为什么有些语言的 struct 用逗号分隔，有些用分号，有些用换行呢？写多了会不会混淆 此时有新的问题：我们想表示 iterator 的生命周期和 map 是一样的，应该怎么做？ 这是 Mini LSM 中最 tricky 的 Rust 技巧：self-referential 结构： pub struct MemtableIterator { // \u0026lt;- with lifetime \u0026#39;this map: Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt;, iter: SkipMapRangeIter\u0026lt;\u0026#39;this\u0026gt;, }这样就解决了问题，也可以用第三方库 ouroboros 来解决这个问题，它提供了一个简单的方法定义 self-referential 结构，也可以用 unsafe Rust 来解决（ouroboros 就是用 unsafe Rust 实现的） 教程已经定义了 self-referential MemtableIterator，只需要实现 MemtableIterator 和 Memtable::scan 接口： ","date":"2024-03-23","objectID":"/posts/minilsm-2/:3:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task1-memtable-iterator"},{"categories":["Database"],"collections":null,"content":"Task1: Solution 观察 Memtable::scan 函数，pub fn scan(\u0026amp;self, _lower: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, _upper: Bound\u0026lt;\u0026amp;[u8]\u0026gt;) -\u0026gt; MemTableIterator，其中 lower 和 upper 属于 Bound 类型，表示一个范围的 keys（左闭右开），比如 (1..12).start_bound(), Included(\u0026amp;1), (1..12).end_bound(), Excluded(\u0026amp;12)。BTreeMap 通过 map.range((Excluded(3), Included(8))) 来获取 (3, 8] 范围内的 key-value。所以对于 memtable 的 scan，应该获取 [lower, upper] 的起始迭代器： /// Get an iterator over a range of keys. pub fn scan(\u0026amp;self, lower: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, upper: Bound\u0026lt;\u0026amp;[u8]\u0026gt;) -\u0026gt; MemTableIterator { let mut iter = MemTableIterator::new( self.map.clone(), // Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt; |map| map.range((map_bound(lower), map_bound(upper))), // iter FnOnce (Bytes::new(), Bytes::new()), // Stores the key-value pair. ); iter.next().unwrap(); iter }调用 MemTableIterator::new 新建一个 MemTableIterator，其结构体为： type SkipMapRangeIter\u0026lt;\u0026#39;a\u0026gt; = crossbeam_skiplist::map::Range\u0026lt;\u0026#39;a, Bytes, (Bound\u0026lt;Bytes\u0026gt;, Bound\u0026lt;Bytes\u0026gt;), Bytes, Bytes\u0026gt;; #[self_referencing] pub struct MemTableIterator { /// Stores a reference to the skipmap. map: Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt;, /// Stores a skipmap iterator that refers to the lifetime of `MemTableIterator` itself. #[borrows(map)] #[not_covariant] iter: SkipMapRangeIter\u0026lt;\u0026#39;this\u0026gt;, /// Stores the current key-value pair. item: (Bytes, Bytes), }这一个结构体令 Rust 初学的我非常懵，尤其是 #[borrows(map)] 找了很久没有找到在哪，而且 iter 我以为传入一个结构体，但实际上应该传入一个函数闭包，这个函数闭包的返回类型是 Range。所以第一个参数是一个 Arc 变量 self.map.clone() 返回当前的 map。第二个参数是 |map| map.range((map_bound(lower), map_bound(upper)))，本身其实是一个 iter，可以调用 next 等等，然后是 item 用于存当前的 key-value 对。创建完成后需要调用一次 iter.next().unwrap() 走到第一个 Range，然后返回。 然后为 MemTableIterator 实现 StorageIterator 特征，具有 value, key, is_valid 和 next 接口： impl StorageIterator for MemTableIterator { type KeyType\u0026lt;\u0026#39;a\u0026gt; = KeySlice\u0026lt;\u0026#39;a\u0026gt;; fn value(\u0026amp;self) -\u0026gt; \u0026amp;[u8] { \u0026amp;self.borrow_item().1.chunk() } fn key(\u0026amp;self) -\u0026gt; KeySlice { KeySlice::from_slice(\u0026amp;self.borrow_item().0.chunk()) } fn is_valid(\u0026amp;self) -\u0026gt; bool { !\u0026amp;self.borrow_item().0.is_empty() } fn next(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { let entry = self.with_iter_mut(|iter| iter.next()); let item = entry.map(|en| (en.key().clone(), en.value().clone())); self.with_mut(|iter| *iter.item = item.unwrap_or_default()); Ok(()) } }这里的 \u0026amp;self.borrow_item() 表示借用当前的 item，所以是引用，并且用 .chunk() 将 Bytes 转成 \u0026amp;[u8] 类型，value, key 和 is_valid 接口实现都很类似。 而 next 实现起来比较麻烦，大致思路是先通过 iter 得到下一个 Entry，然后将其值转成 (Bytes, Bytes) 类型然后赋给当前 self.item，同时修改本体所以需要用 self.with_mut() 此时调用 cargo x scheck 可以通过前两个测试 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:4:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task1-solution"},{"categories":["Database"],"collections":null,"content":"Task2: Merge Iterator 本节需要修改 src/iterators/merge_iterator.rs。 现在你已经有多个 memtables，你需要创建多个 memtable iterators，并且需要 merge 所有 memtables 返回的结果，并且每个 key 对应最新的结果。 MergeIterator 维护一个 binary heap，你需要处理错误（比如当 iterator not valid 时），并且需要保证 key-value 对是最新的，比如： iter1: b-\u0026gt;del, c-\u0026gt;4, d-\u0026gt;5 iter2: a-\u0026gt;1, b-\u0026gt;2, c-\u0026gt;3 iter3: e-\u0026gt;4merge iterator 返回的结果应该是： a-\u0026gt;1, b-\u0026gt;del, c-\u0026gt;4, d-\u0026gt;5, e-\u0026gt;4MergeIterator 的构造器 constructor 接受一个 vector of iterators 参数，假设 index 小的是新的。 但是错误处理有个陷阱，比如： let Some(mut inner_iter) = self.iters.peek_mut() { inner_iter.next()?; }如果 next 返回了错误（disk failure, network failure, checksum error, etc），但是当跳出了 if 条件并且返回错误给调用者时，PeekMut 的结果会移动 heap 里的元素，导致访问到 invalid iterator。所以，需要额外的错误处理，而不是使用在 PeekMut 范围里用 ?。 我们想避免尽可能 dynamic dispatch，所以我们不使用 Box\u0026lt;dynStorageIterator\u0026gt;。反而使用静态的分发，使用泛型，并且 StorageIterator 使用了 generic associated type (GAT)，所以它可以支持 KeySlice 和 \u0026amp;[u8] 同时作为 key 的类型。我们会在将来的作业改变 KeySlice 使其包含 timestamp。 Rust 中的动态分发，比如 Trait Objects，类似虚继承？ Generic Associated Type GAT 指的是通用关联类型，可以使得关联类型依赖于 trait 方法 比如 trait Processor{ type Output\u0026lt;'a\u0026gt;; fn process\u0026lt;'a\u0026gt;(\u0026amp;self, data: \u0026amp;a str) -\u0026gt; Self::Output\u0026lt;'a\u0026gt;; } 在 impl 时再指定 type 是什么。 为了开始本节，我们会使用 Key\u0026lt;T\u0026gt; 表示 LSM key 的类型，并且区分它的值类型。你应该使用 Key\u0026lt;T\u0026gt; 提供的接口而不是直接访问其内部值。我们会为这个 key 添加时间戳，并用这个 key abstraction 过渡会比较顺利。所以目前 KeySlice 和 \u0026amp;[u8] 是一样的，KeyVec 和 Vec\u0026lt;u8\u0026gt; 一样，KeyBates 和 Bytes 也是一样的。 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:5:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task2-merge-iterator"},{"categories":["Database"],"collections":null,"content":"Task2: Solution 这一节主要实现 src/iterators/merge_iterators 中的 MergeIterator::key,MergeIterator::value, MergeIterator::is_valid 和 MergeIterator::next，并且实现 MergeIterator::create 首先是 MergeIterator::create，接受一个 iters 数组，需要将 iters: Vec\u0026lt;Box\u0026lt;I\u0026gt;\u0026gt; 转换成 BinaryHeap\u0026lt;HeapWrapper\u0026lt;I\u0026gt;, Global\u0026gt;，即优先队列 / 堆 / 完全二叉树，教程已经实现了 HeapWrapper 的比较特征，所以只需要将 iters 遍历一遍，并且入堆。 比如传入 [[(\u0026quot;a\u0026quot;, 1), (\u0026quot;b\u0026quot;, 2), (\u0026quot;c\u0026quot;, 3)], [(\u0026quot;a\u0026quot;, 1.2), (\u0026quot;d\u0026quot;, 4)]] 这样的数组，先实现 MergeIterator::create： impl\u0026lt;I: StorageIterator\u0026gt; MergeIterator\u0026lt;I\u0026gt; { pub fn create(iters: Vec\u0026lt;Box\u0026lt;I\u0026gt;\u0026gt;) -\u0026gt; Self { let mut heap = BinaryHeap::new(); if iters.is_empty() { return Self { iters: heap, current: None, }; } for (i, iter) in iters.into_iter().enumerate() { if iter.is_valid() { heap.push(HeapWrapper(i, iter)); } } let current = heap.pop(); Self { iters: heap, current, } } }先检查 iters.is_empty() 传入的数组是否为空，然后遍历 iters 数组，使用 into_iter() 消耗原数组，转移所有权，然后 enumerate() 遍历，需要检查 iter.key() 是否是空的。 fn key(\u0026amp;self) -\u0026gt; KeySlice { self.current.as_ref().unwrap().1.key() } fn value(\u0026amp;self) -\u0026gt; \u0026amp;[u8] { self.current.as_ref().unwrap().1.value() } fn is_valid(\u0026amp;self) -\u0026gt; bool { self.current .as_ref() .map(|x| x.1.is_valid()) .unwrap_or(false) }key, value, is_valid 接口不再重述，但需要注意用 as_ref() 取得引用。next 的实现比较绕，因为对于 MergeIterator，有时候前面的 key 会小于堆顶的 key，所以需要进行交换： fn next(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { let current = self.current.as_mut().unwrap(); while let Some(mut iter) = self.iters.peek_mut() { if iter.1.key() == current.1.key() { let res = iter.1.next(); if let Err(e) = res { PeekMut::pop(iter); return Err(e); } else { if !iter.1.is_valid() { PeekMut::pop(iter); } } } else { break; } } current.1.next()?; if !current.1.is_valid() { if let Some(iter) = self.iters.pop() { *current = iter; } } else { // if the current key is smaller, swap it with the top of the heap // e.g. current \u0026#34;e\u0026#34; 101 \u0026lt; heap top iter key \u0026#34;d\u0026#34; 100 // PartialOrd for HeapWrapper will reverse the ordering // so that the top of the heap is the smallest key if let Some(mut iter) = self.iters.peek_mut() { if !(*iter \u0026lt; *current) { std::mem::swap(\u0026amp;mut *iter, current); } } } Ok(()) }首先是用 while 循环检查当前堆顶的 iter.1.key() 如果和当前的 current.1.key() 相同，则应该以 current 为准（最新版本），并且调用 iter.1.next() 走到下一个 key。 然后使用 current.1.next() 走到下一个 key，但此时就要处理新版本中的 key 实际小于 iters.peek_mut() 时的情况，但由于为了实现堆，HeapWrapper 重载了 PartialOrd 并反转了结果，所以堆顶的 key 是小的，那么比较堆顶 iter 和 current 就需要反过来。 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:6:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task2-solution"},{"categories":["Database"],"collections":null,"content":"Task3: LSM Iterator + Fused Iterator 本节需要修改 src/lsm_iterator.rs 我们使用 LsmIterator 结构表示 LSM 内部的 iterators，在整个 LSM 教程中，会有多个 iterators 被加进系统，所以你需要多次修改这个结构。现在由于只有多个 memtables 所以定义为 type LsmIteratorInner = MergeIterator\u0026lt;MemTableIterator\u0026gt;;你可以提前实现 LsmIterator 结构，调用 inner iterator 并且跳过 deleted keys. 但本节不测试 LsmIterator，但会在下一节任务 Task4 有个 integration 整合。 我们想提供额外的安全性，防止用户用错 iterators。在 iterator not valid 时用户不应该调用 key value 或 next 接口。同时，当 next 返回错误时，用户不应该再使用这个 iterator。FusedIterator 是一个 iterator wrapper 用于 normalize 规范化所有 iterators 的行为。 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:7:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task3-lsm-iterator--fused-iterator"},{"categories":["Database"],"collections":null,"content":"Task3: Solution 观察 FusedIterator，它包含了一个 iter 和一个 bool 字段表示是否是错误的： pub struct FusedIterator\u0026lt;I: StorageIterator\u0026gt; { iter: I, has_errored: bool, }本节需要实现 FusedIterator::is_valid, FusedIterator::key, FusedIterator::value, FusedIterator::next，同时保证如果 !self.is_valid() 就应该 panic: impl\u0026lt;I: StorageIterator\u0026gt; StorageIterator for FusedIterator\u0026lt;I\u0026gt; { type KeyType\u0026lt;\u0026#39;a\u0026gt; = I::KeyType\u0026lt;\u0026#39;a\u0026gt; where Self: \u0026#39;a; fn is_valid(\u0026amp;self) -\u0026gt; bool { // first check if the iterator has errored // iter.is_valid() may iter to next !self.has_errored \u0026amp;\u0026amp; self.iter.is_valid() } fn key(\u0026amp;self) -\u0026gt; Self::KeyType\u0026lt;\u0026#39;_\u0026gt; { if !self.is_valid() { panic!(\u0026#34;called key on invalid iterator\u0026#34;); } self.iter.key() } fn value(\u0026amp;self) -\u0026gt; \u0026amp;[u8] { if !self.is_valid() { panic!(\u0026#34;called value on invalid iterator\u0026#34;); } self.iter.value() } fn next(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { if self.has_errored { return Err(anyhow::anyhow!(\u0026#34;called next on invalid iterator\u0026#34;)); } if self.iter.is_valid() { let res = self.iter.next(); if let Err(e) = res { self.has_errored = true; return Err(e); } } Ok(()) } }逻辑比较简单，但 is_valid() 最好是先检查 self.has_errored，不然先调用 iter.is_valid() 可能会走 next 导致 index 不对 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:8:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task3-solution"},{"categories":["Database"],"collections":null,"content":"Task4: Read Path - Scan 本节任务需要修改 src/lsm_storage.rs 当所有 iterators 实现后，你就可以实现 LSM 引擎 scan 接口了。你可以简单地创建一个 LSM iterator 和 memtable iterator（记得在 merge iterator 最前面放最新的 memtable ），此时你的 存储引擎就可以做 scan 扫描请求了。 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:9:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task4-read-path---scan"},{"categories":["Database"],"collections":null,"content":"Task4: Solution 结合 Day1 和 Day2，从实现 memtable 和 memtable iterator，到多个 memtables 和多个 iterators，现在需要将这些 memtable iterators 结合起来，实现一个扫描方法，回顾单个 Memtable::scan，接受一个 lower 和 upper 参数，返回一个 MemtableIterator 迭代器，包含满足 (lower, upper) 的键值对： /// Get an iterator over a range of keys. pub fn scan(\u0026amp;self, lower: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, upper: Bound\u0026lt;\u0026amp;[u8]\u0026gt;) -\u0026gt; MemTableIterator { let mut iter = MemTableIterator::new( self.map.clone(), // Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt; |map| map.range((map_bound(lower), map_bound(upper))), // iter FnOnce (Bytes::new(), Bytes::new()), // Stores the key-value pair. ); iter.next().unwrap(); iter }同理，LsmStorageInner 也是接受一个 lower 和 upper 参数，返回一个 FusedIterator\u0026lt;LsmIterator\u0026gt; 融合的迭代器。 具体地，需要先拿出所有 memtables iterators，创建一个数组存储这些 MemtableIterator，先存入最新的 memtable 然后推入后面的 imm_memtables，并使用 MergeIterator::create() 创建 MergeIterator 之后创建 FusedIterator pub fn scan( \u0026amp;self, lower: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, upper: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, ) -\u0026gt; Result\u0026lt;FusedIterator\u0026lt;LsmIterator\u0026gt;\u0026gt; { let guard = self.state.read(); let mut memtable_iters = Vec::new(); memtable_iters.push(Box::new(guard.memtable.scan(lower, upper))); for memtable in guard.imm_memtables.iter() { memtable_iters.push(Box::new(memtable.scan(lower, upper))); } let memtable_iter = MergeIterator::create(memtable_iters); drop(guard); Ok(FusedIterator::new(LsmIterator::new(memtable_iter)?)) }但我的实现方法锁粒度大，checkpoint 使用了 Arc::clone(\u0026amp;guard) 克隆了一个原子变量，这样临界区小，可以立刻释放锁还保证了每个线程都能读到一样的 snapshot： let snapshot = { let guard = self.state.read(); Arc::clone(\u0026amp;guard) }; // drop global lock here 但此时还无法通过测试，观察测试可以发现，LsmStorageInner 在 delete 某个 key 后，我的实现方法无法将其忽略掉，所以要重新考虑得到一个 LsmIterator 迭代器后，如果 next 为空或者 deleted key，应该怎么做： impl LsmIterator { pub(crate) fn new(iter: LsmIteratorInner) -\u0026gt; Result\u0026lt;Self\u0026gt; { let mut iter = Self { inner: iter }; iter.move_to_non_delete()?; Ok(iter) } } impl LsmIterator { fn move_to_non_delete(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { while self.is_valid() \u0026amp;\u0026amp; self.inner.value().is_empty() { self.inner.next()?; } Ok(()) } } impl StorageIterator for LsmIterator { // ... fn next(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { self.inner.next()?; self.move_to_non_delete()?; Ok(()) } }这里 self.inner.next() 逻辑不变，但新增一个函数判断经过 next 后当前 self.is_empty，如果是则继续迭代，同时还需要修改 new() 因为新建 LsmIterator 时候也可能传入带有删除键的 LsmIteratorInner （测试就是这样做的） ","date":"2024-03-23","objectID":"/posts/minilsm-2/:10:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#task4-solution"},{"categories":["Database"],"collections":null,"content":"Conclusion 这次写下来比较吃力，一方面对 Rust 语法不太熟悉，尤其是类型不太懂。另一方面是对 MVCC 和多线程并发如何在 Rust 中实现还没有好的理解，需要看一下 Rust 多线程编程的一些例子。 What is the time/space complexity of using your merge iterator? Merge Iterator 时间复杂度看上去是 O(log N * M)，N 个 memtable，每个大小 M，空间复杂度则应该是 O(N * M) Why do we need a self-referential structure for memtable iterator? 自引用, 指的是一个结构体中, 有一个字段需要引用自己的另一个 field, 在 mem_table.rs 中有 #[self_referencing] pub struct MemTableIterator { map: Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt;, /// Stores a skipmap iterator that refers to the lifetime of `MemTableIterator` itself. #[borrows(map)] #[not_covariant] iter: SkipMapRangeIter\u0026lt;\u0026#39;this\u0026gt;, // 需要引用 map 的值 }就是教程中说的，为了标记 MemtableIterator 的生命周期和 map 一致, 结合 \u0026lt;'this\u0026gt; 与 self_referencing 使得他们的生命周期一致. 否则会出现 使用值 和 值的引用 同时出现, 最终所有权转移 和 借用一起发生 If a key is removed (there is a delete tombstone), do you need to return it to the user? Where did you handle this logic? 这里参考了 checkpoint 的实现, 跳过了 deleted key, 没有返回给用户 If we want to get rid of self-referential structure and have a lifetime on the memtable iterator (i.e., MemtableIterator\u0026lt;\u0026lsquo;a\u0026gt;, where \u0026lsquo;a = memtable or LsmStorageInner lifetime), is it still possible to implement the scan functionality? What happens if (1) we create an iterator on the skiplist memtable (2) someone inserts new keys into the memtable (3) will the iterator see the new key? What happens if your key comparator cannot give the binary heap implementation a stable order? Why do we need to ensure the merge iterator returns data in the iterator construction order? Is it possible to implement a Rust-style iterator (i.e., next(\u0026amp;self) -\u0026gt; (Key, Value)) for LSM iterators? What are the pros/cons? The scan interface is like fn scan(\u0026amp;self, lower: Bound\u0026lt;\u0026amp;[u8]\u0026gt;, upper: Bound\u0026lt;\u0026amp;[u8]\u0026gt;). How to make this API compatible with Rust-style range (i.e., key_a..key_b)? If you implement this, try to pass a full range .. to the interface and see what will happen. The starter code provides the merge iterator interface to store Box instead of I. What might be the reason behind that? 剩下的还是等写完全部有个具体概念回来再来补充 ","date":"2024-03-23","objectID":"/posts/minilsm-2/:11:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day2","uri":"/posts/minilsm-2/#conclusion"},{"categories":["Database"],"collections":null,"content":"Mini-LSM Week 1 Day1 记录下 LSM 的学习过程，感谢迟先生的教程 https://skyzh.github.io/mini-lsm/ ","date":"2024-03-22","objectID":"/posts/minilsm-1/:1:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#mini-lsm-week-1-day1"},{"categories":["Database"],"collections":null,"content":"前言 使用 Rust 实现 LSM-Tree 存储结构 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:2:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#前言"},{"categories":["Database"],"collections":null,"content":"什么是 LSM，为什么 LSM LSM, Log-structured merge trees, 是一种维护 key-value 对的数据结构。这种数据结构广泛用于分布式数据库（TiDB， CockroachDB）及其存储引擎。RocksDB 基于 LevelDB 实现了 LSM-Tree 存储引擎。LSM 提供许多 key-value 访问函数，并且被用于许多生产系统。 LSM Tree 属于 append-friendly 添加友好的数据结构。将 LSM 与 B-Tree 或 RB-Tree 等 key-value 数据结构比较。对于后者，所有数据都在原地操作，比如你想要更新 key 对应的 value，存储引擎会用新值直接覆盖原有的值（内存或硬盘）。而在 LSM-Tree 中，所有写操作 (比如 insert, update, delete) 会 lazily 懒地应用到存储中，存储引擎批量地将这些操作分成 SST (sorted string table) 文件并且写入磁盘。一旦被写入到磁盘，存储引擎不会直接修改它们。而是在 compaction 后台任务中，存储引擎会合并这些文件，应用这些更新或删除。 这个架构设计使得 LSM-Tree 易于使用： 数据在存储中是不可变的 immutable。并发控制更加直观。将后台任务（compaction）卸载? (offload) 到远程服务器是可行的。在云原生存储系统比如 S3 存储或提供数据也变得可行。 改变 compaction 算法允许存储引擎在读放大、写放大、空间放大之间取得平衡。LSM-Tree 是多功能的，通过调整 compaction 参数，可以优化 LSM 数据结构在不同工作负载中的表现。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:2:1","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#什么是-lsm为什么-lsm"},{"categories":["Database"],"collections":null,"content":"前提条件 Rust key-value store engine (PingCap Bitcask 项目) LevelDB 一些概念能帮助理解 可变和不可变 mem-table, SST, compaction, WAL 等等。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:2:2","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#前提条件"},{"categories":["Database"],"collections":null,"content":"期望 学习了解基于 LSM-Tree 的存储引擎是如何工作的，尤其是从各种 tradeoffs 权衡中寻找最优的、符合你的工作负载的需求或目标。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:2:3","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#期望"},{"categories":["Database"],"collections":null,"content":"总览 第一部分主要研究 LSM Tree 的存储结构和存储格式。 第二部分主要关于 compaction 和实现持久化。 第三部分实现 MVCC 多版本控制。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:3:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#总览"},{"categories":["Database"],"collections":null,"content":"LSM LSM 存储大致含有三部分： Write-Ahead Log 用于持久化数据（Recovery） SSTs 在磁盘用于维护 LSM-Tree 结构 Mem-Tables 在内存用于 batching 批处理写操作 存储引擎大致提供这些接口： Put(key, value): 在 LSM Tree 存储一个 key-value 键值对 Delete(key): 根据 key 删除一个 key-value 键值对 Get(key): 根据 key 获得 value Scan(range): 获得一个范围的 key-value 键值对 为了保证持续化： Sync(): 确保所有操作在 sync 之前都被持久化到硬盘 一些引擎会结合 Put 和 Delete 成为一个操作 WriteBatch，接受一批键值对。 MiniLSM 教程假设 LSM 使用 leveled compaction，是广泛使用的。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:3:1","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#lsm"},{"categories":["Database"],"collections":null,"content":"写路径 写操作包含四步： 在 WAL 写入键值对，在引擎 crash 时可以 recovery 在 memtable 写入键值对。当 1 和 2 完成后，通知用户写操作完成 (后台) 当 memtable 满时，freeze 它们成为 immutable mem-tables 然后 flush 到硬盘成为 SST files (后台) 引擎 compact 这些文件，按 levels 到一些低层的，维护一个好的 LSM 结构，减少读放大 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:3:2","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#写路径"},{"categories":["Database"],"collections":null,"content":"读路径 当需要读一个 key 时 probe 所有的 mem-tables，从最近的到最旧的 如果 key 没找到，就搜索整个 LSM tree 包括 SSTs，直到找到这个数据 有两种读操作：lookup 和 scan，lookup 找到一个 key，scan 在一个范围里找到所有的 keys。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:3:3","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#读路径"},{"categories":["Database"],"collections":null,"content":"MiniLSM 第一章需要构建必要的存储格式、读路径、写路径，以及一个基于 LSM 的 key-value store。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:0","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#minilsm"},{"categories":["Database"],"collections":null,"content":"Mem-Table in-memory 的读写路径 基于 skiplist 实现 memtables 实现 freezing memtable 的逻辑 实现 LSM 的 memtable 读路径 get ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:1","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#mem-table"},{"categories":["Database"],"collections":null,"content":"Task1: SkipList Memtable src/mem_table.rs 基于 crossbeam_skiplist 跳表，支持 lock-free 并发读写。跳表是一个允许并发读写的有序 key-value map。 crossbeam-skiplist 提供了与 Rust std BTreeMap 类似的接口：insert, get 和 iter。区别在于修改的接口比如 insert 需要一个 immutable reference 到 skiplist 而不是一个 mutable。实现不应该使用任何 mutex 锁。 MemTable 结构没有 delete 接口，在 mini-lsm 项目中，deletion 可以用 key 和 empty value 表示。 Task1 需要实现 MemTable::get 和 MemTable::put 使得可以修改 memtable bytes crate 包用于存取数据，bytes::Byte 和 Arc\u0026lt;[u8]\u0026gt; 类似。当 clone Bytes 或使用 Bytes 的切片时，数据不会被 copy, it is cheap to clone. 所以只是 create a new reference to the storage. When there are no references to the area, it will be freed. 跳表 skiplist 好像在之前并发的课上学过，但当时不理解有什么用。 Redis 中的有序集合 zset，LevelDB、RocksDB、HBase 中 Memtable 都用到了 SkipList 查询的期望时间复杂度 O(log(n)) 和红黑树等平衡二叉树的区别？ 基于有序链表，引入分层的概念，使得搜索可以像二分一样从高层往底层查找。 每个位于第 i 层的节点有 p 的概率出现在第 i+1 层 查找、删除、增加的简单实现：https://leetcode.cn/problems/design-skiplist/ ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:2","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task1-skiplist-memtable"},{"categories":["Database"],"collections":null,"content":"Task1 Solution 在 src/mem_table.rs 中实现 impl Memtable 中的 pub fn get(\u0026amp;self, key: \u0026amp;[u8]) -\u0026gt; Option\u0026lt;Bytes\u0026gt; {} 函数 pub fn put(\u0026amp;self, key: \u0026amp;[u8], value: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;()\u0026gt; {} 函数 Memtable 中有四个成员 map: Arc\u0026lt;SkipMap\u0026lt;Bytes, Bytes\u0026gt;\u0026gt; 即跳表 wal: Option\u0026lt;Wal\u0026gt; 是 WAL 日志 id: usize approximate_size: Arc\u0026lt;AtomicUsize\u0026gt; 由于不需要关心跳表的具体实现方法，对于 Memtable 只需要调用 map 的 insert 和 get 就可以： 首先实现 Memtable::get ，从跳表中获取数据，但 key: \u0026amp;[u8] 是一个切片比如 b\u0026quot;key1\u0026quot;。 实现方法：使用 self.map.get(key) 获取相应的 value，由于 map.get() 的返回值是 Option\u0026lt;Entry\u0026lt;'_, K, V\u0026gt;\u0026gt;，需要将其转成 Bytes，所以使用 map.get(key).map(|v| v.value()) 但此时 v 的类型是 Option\u0026lt;\u0026amp;bytes::Bytes\u0026gt; 还需要转换，可以使用 clone() 将结果转成函数返回类型的 Option\u0026lt;Bytes\u0026gt; pub fn get(\u0026amp;self, key: \u0026amp;[u8]) -\u0026gt; Option\u0026lt;Bytes\u0026gt; { self.map.get(key).map(|v| v.value().clone()) // v: Entry\u0026lt;\u0026#39;_, Bytes, Bytes\u0026gt; } 再实现 Memtable::put，存入 key: \u0026amp;[u8] 和 value: \u0026amp;[u8] 并且返回 Result\u0026lt;()\u0026gt;。由于 task1 不需要关心跳表的实现，也不需要关心 WAL 日志，直接使用 self.map.insert()。但是该函数的签名是 insert(\u0026amp;self, key: K, value: V) -\u0026gt; Entry\u0026lt;'_, K, V\u0026gt;，此时 K 和 V 都是 Bytes 所以需要将 key 和 value 转换成 Bytes (使用 bytes 库提供的 Bytes::copy_from_slice 方法。) 然后将 insert() 的返回值转成 Result\u0026lt;()\u0026gt; （这里参照其他函数直接返回 OK(())） pub fn put(\u0026amp;self, key: \u0026amp;[u8], value: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;()\u0026gt; { self.map .insert(Bytes::copy_from_slice(key), Bytes::copy_from_slice(value)); Ok(()) } 实现 MemTable::create 后，可以通过 mini-lsm-starter tests::week1_day1::test_task1_memtable_overwrite 和 mini-lsm-starter tests::week1_day1::test_task1_memtable_get 两个测试 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:3","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task1-solution"},{"categories":["Database"],"collections":null,"content":"Task2: A Single Memtable in the Engine Task2 需要修改 src/lsm_storage.rs，将 Memtable 加入到 LSM State 中。 在 LsmStorageState::create 中根据不同的 compaction_options 创建了一个 LSM 结构 memtable, imm_memtable, l0_sstables, levels, sstables。 默认情况下 memtable: Arc::new(MemTable::create(0)) 创建了一个 id 为 0 的 mutable memtable。任何时间，引擎只会有一个 mutable memtable，通常有个大小限制（比如 256MB），并且当存满后会被 frozen 变成 immutable memtable 在 lsm_storage.rs 文件中，有两个结构表示了存储引擎：MiniLSM 和 LsmStorageInner，前者是一个 thin wrapper: pub struct MiniLsm { pub(crate) inner: Arc\u0026lt;LsmStorageInner\u0026gt;, flush_notifier: crossbeam_channel::Sender\u0026lt;()\u0026gt;, flush_thread: Mutex\u0026lt;Option\u0026lt;std::thread::JoinHandle\u0026lt;()\u0026gt;\u0026gt;\u0026gt;, compaction_notifier: crossbeam_channel::Sender\u0026lt;()\u0026gt;, compaction_thread: Mutex\u0026lt;Option\u0026lt;std::thread::JoinHandle\u0026lt;()\u0026gt;\u0026gt;\u0026gt;, }需要实现的方法大部分在 LsmStorageInner 直到 Week2 Compaction，这个结构包含了当前的 LSM storage engine 结构。Week 1 只用到 memtable 结构，并且是 mutable memtable。 Task2 需要实现 LsmStorageInner::get, LsmStorageInner::put 和 LsmStorageInner::delete，这些接口会分发请求到 memtable 考虑 delete 函数，应该是简单的将 key 对应的 value 修改为空，称为 delete tombstone 标记删除而不是真的直接删除。实现 get 时应该注意这点。 不直接删除的原因是什么？是为了 MVCC 还是为了后面的 compaction 为了访问 memtable 需要使用锁，state lock，实现 put 时只需要 immutable reference，所以修改 memtable 时候只需要调用 state 读锁。这种方法允许多线程对 memtable 并发访问。 如果 put 用了不可变的引用，则不会修改原数据结构，所以 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:4","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task2-a-single-memtable-in-the-engine"},{"categories":["Database"],"collections":null,"content":"Task2 Solution 先实现 LsmStorageInner::put 和 LsmStorageInner::delete，前者调用读锁，后者存入空值，然后在 get 中处理 pub fn put(\u0026amp;self, key: \u0026amp;[u8], value: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;()\u0026gt; { self.state.read().memtable.put(key, value) } /// Remove a key from the storage by writing an empty value. pub fn delete(\u0026amp;self, key: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;()\u0026gt; { self.state.read().memtable.put(key, b\u0026#34;\u0026#34;) // handle empty value in get } get 实现需要额外判断取出来的 Bytes 是不是空值 pub fn get(\u0026amp;self, key: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;Option\u0026lt;Bytes\u0026gt;\u0026gt; { if let Some(v) = self.state.read().memtable.get(key) { if v.is_empty() { return Ok(None); } return Ok(Some(v)); } Ok(None) } ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:5","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task2-solution"},{"categories":["Database"],"collections":null,"content":"Task 3: Write Path - Freezing a Memtable 在 src/lsm_storage.rs 和 src/mem_table.rs 实现 freeze a memtable 功能。由于 memtable 无法一直增长，所以需要冷冻 freeze 起来，在当 memtable 超过大小限制时可以 flush，在 lsm_storage 中 target_sst_size 字段表示 SST table size 同时也是 memtable 的大致大小（比如 1 \u0026lt;\u0026lt; 20 1MB）。 本 task 需要在 put/delete 时，估算 memtable 的大小，比如 put 时简单地将 keys and values 的字节大小加起来（如果 put 两次，你也可以计算两次）。一旦 memtable 达到大小限制，调用 force_freeze_memtable 冻结 memtable 然后创建一个新的 memtable。 因为此时可能有多个线程从存储引擎中获取数据，force_freeze_memtable 可能会被多个线程并行调用，需要考虑避免 race condition 有多个地方可以更改 LSM state: freeze a mutable memtable, flush memtable to SST, and GC/compaction. 所有的修改，都可能是 IO 操作，为了实现 freeze_memtable 需要调用 state 中的写锁：let state = self.state.write(), state.immutable_memtable.push(/* something */) 和 state.memtable = create(); 但是，当需要为每个 memtable 创建 write-ahead log 时 state.memtable = MemTable::create_with_wal()? 由于需要几个毫秒比较耗时，其他线程就需要等待，造成 spike of latency 延迟尖峰。 可以将 IO 操作放在锁区域外，来解决这种问题（创建 Memtable 实际上不需要锁）： fn freeze_memtable(\u0026amp;self) { let memtable = MemTable::create_with_wal()?; // \u0026lt;- could take several milliseconds { let state = self.state.write(); state.immutable_memtable.push(/* something */); state.memtable = memtable; } }但这引起了另一种情况：memtable 快要达到容量限制，并且两个线程成功 put 了两个 keys 到 memtable，并且都发现了 memtable 达到了容量限制。两个线程都会检查 size 然后决定调用 freeze，于是两个空的 memtable 都被创建了。 为了解决这种情况，所有的 state 修改操作都需要通过 state lock 同步： fn put(\u0026amp;self, key: \u0026amp;[u8], value: \u0026amp;[u8]) { // put things into the memtable, checks capacity, and drop the read lock on LSM state if memtable_reaches_capacity_on_put { let state_lock = self.state_lock.lock(); if /* check again current memtable reaches capacity */ { self.freeze_memtable(\u0026amp;state_lock)?; } } }这种模式非常常见，比如 L0 flush 也是这样： fn force_flush_next_imm_memtable(\u0026amp;self) { let state_lock = self.state_lock.lock(); // get the oldest memtable and drop the read lock on LSM state // write the contents to the disk // get the write lock on LSM state and update the state }在这个 Task，需要修改 put 和 delete 满足 memtable 的 soft capacity limit，当到达限制时调用 force_freeze_memtable 冻结 memtable。测试文件并没有测试并发场景，所以需要自己考虑多种 race condition 竞争情况。并且，时刻记住检查锁的区域，保证 critical section 临界区是最小的。 可以简单地赋给下一个 memtable id 为 self.next_ssd_id(), 注意 imm_memtables 存储了最新到最旧的 memtables，imm_memtables.first() 是最后一个被冻结的。 修改 Memtable::put，计算 key 和 value 的长度，增加原子变量（使用 Relaxed 不需要线性一致） pub fn put(\u0026amp;self, key: \u0026amp;[u8], value: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;()\u0026gt; { let sz = key.len() + value.len(); self.map .insert(Bytes::copy_from_slice(key), Bytes::copy_from_slice(value)); self.approximate_size.fetch_add(sz, Ordering::Relaxed); Ok(()) } 实现 LsmStorageInner::force_freeze_memtable： pub fn force_freeze_memtable(\u0026amp;self, _state_lock_observer: \u0026amp;MutexGuard\u0026lt;\u0026#39;_, ()\u0026gt;) -\u0026gt; Result\u0026lt;()\u0026gt; { let memtable_id = self.next_sst_id(); let memtable = if self.options.enable_wal { Arc::new(MemTable::create_with_wal( memtable_id, self.path_of_wal(memtable_id), )?) } else { Arc::new(MemTable::create(memtable_id)) }; let mut state = self.state.write(); // acquire the lock let mut snapshot = state.as_ref().clone(); // first use as_ref() to convert Arc to \u0026amp;Arc and then clone() to make it mutable let old_memtable = std::mem::replace(\u0026amp;mut snapshot.memtable, memtable); snapshot.imm_memtables.insert(0, old_memtable.clone()); // insert to the front *state = Arc::new(snapshot); // update the state drop(state); // release the lock // old_memtable.sync_wal()?; Ok(()) }获取新的 memtable_id 后根据 option 创建新的 Memtable，随后获取写锁，获取 state 的 mut 状态得到旧的 memtable，使用 std::mem::replace 替换成新的，随后释放写锁并且更新 *state = Arc::new(snapshot)。 ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:6","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task-3-write-path---freezing-a-memtable"},{"categories":["Database"],"collections":null,"content":"Task 4: Read Path - Get 修改 src/lsm_storage.rs read path 中的 get 函数，来获取最新版本的 key，保证你 probe 扫描了最新到最旧的 memtable: pub fn get(\u0026amp;self, key: \u0026amp;[u8]) -\u0026gt; Result\u0026lt;Option\u0026lt;Bytes\u0026gt;\u0026gt; { let state = self.state.read(); if let Some(v) = state.memtable.get(key) { if v.is_empty() { return Ok(None); } return Ok(Some(v)); } for memtable in state.imm_memtables.iter() { if let Some(v) = memtable.get(key) { if v.is_empty() { return Ok(None); } return Ok(Some(v)); } } Ok(None) }","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:7","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#task-4-read-path---get"},{"categories":["Database"],"collections":null,"content":"Conclusion 第一次写 Rust 项目，有很多不熟悉，写了蛮久。 通过 cargo x copy-test --week 1 --day 1 和 cargo x scheck 可以通过第一天的所有测试 为什么 memtable 不提供 delete 接口？ 猜测是因为需要保持原子操作，比如说多版本操作 MVCC，或者 WAL 不好直接删，等到 compaction 时再处理 可以用其他数据结构实现 LSM 中的 memtable 吗？使用跳表的优劣势是什么？ 可以使用红黑树等自平衡树结构，来实现平均时间复杂度 O(logn) 查询。但红黑树非常难写，跳表实现则很直观，同时且支持范围查询，内存占用也少一些。 为什么需要 state 和 state_lock 的组合？可以直接使用 state.read() 和 state.write() 吗？ 在 LsmStorageInner 结构体中有 state: Arc\u0026lt;RwLock\u0026lt;Arc\u0026lt;LsmStorageState\u0026gt;\u0026gt;\u0026gt; 和 state_lock: Mutex\u0026lt;()\u0026gt; 组合 虽然在 week1 day1 可以不用 state_lcok （按理来说应该在 force_freeze_memtable 用 mutex 锁，防止竞争？） 这个问题不太懂，可能 Mutex 是为了防止多个线程同时 put 导致同时 freeze 的情况，此时 mutex 保护 size 为什么 store 和 probe memtables 的顺序很重要，如果一个 key 在多个 memtables，应该返回哪个版本的 value？ 最先访问应该是内存中的 memtable，然后从 imm_memtable 从第一个开始探测（第一个是最新的），所以返回的也是最新的 latest version 后面的一些题目留着之后写完有个全面认识再来看看，对 Rust, LSM 以及多线程编程还是不太熟悉，写起来有些吃力 Is the memory layout of the memtable efficient / does it have good data locality? (Think of how Byte is implemented and stored in the skiplist\u0026hellip;) What are the possible optimizations to make the memtable more efficient? So we are using parking_lot locks in this tutorial. Is its read-write lock a fair lock? What might happen to the readers trying to acquire the lock if there is one writer waiting for existing readers to stop? After freezing the memtable, is it possible that some threads still hold the old LSM state and wrote into these immutable memtables? How does your solution prevent it from happening? There are several places that you might first acquire a read lock on state, then drop it and acquire a write lock (these two operations might be in different functions but they happened sequentially due to one function calls the other). How does it differ from directly upgrading the read lock to a write lock? Is it necessary to upgrade instead of acquiring and dropping and what is the cost of doing the upgrade? More Memtable Formats. You may implement other memtable formats. For example, BTree memtable, vector memtable, and ART memtable. ","date":"2024-03-22","objectID":"/posts/minilsm-1/:4:8","tags":["Database","LSM"],"title":"Mini-LSM Week 1 Day1","uri":"/posts/minilsm-1/#conclusion"},{"categories":["Learned Data Systems"],"collections":null,"content":"Bao: Making Learned Query Optimization Practical MLDB + query optimization ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#bao-making-learned-query-optimization-practical"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT 最近 ML 做 query optimization 由于需要 substantive training overhead 所以其实很少 practical gains, inability to adapt to changes, poor tail performance. 论文提出了 Bao, Bandit Optimizer, 通过利用现有查询优化器的知识，对每个查询提供优化建议。 Bao combines mordern tree Convolutional Neural Networks (CNN) with Thompson sampling (RL). Bao 可以从错误中学习，适应不同的 query workloads, data, schema. 实验结果显示 BAO 可以快速学习改善端到端查询执行性能（包括 tail latency）的策略，用于几种包含 long-term 查询的工作负载。在云环境中，我们表明，与商业系统相比，BAO 可以提供降低的成本和更好的性能。 长尾请求：P99, 99% 请求在一定耗时内，长尾请求就是明显高于均值的那部分比较小的请求 ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION Query Optimization, cardinality estimation and cost modeling, difficult to crack 很多 Query Optimization 都是 ML 做的，但却是很少实用： Long training time, impractical amount of training data, ML-powered cardinality estimators based on supervised learning 需要收集精确的 cardinalities，非常昂贵的操作。所以 Bao wish to estimate cardinalities in the first place. RL 也需要好几天的训练。 Inability to adjust to data and workload changes. query workload, data, schema 是会变的，cardinality estimators based on supervised learning 就需要重新训练，不然就过时了。 Tail catastrophe. learning techniques 比 traditional optimizers 平均表现更好，但是长尾表现很差 （100x regression in query performance）。尤其是训练数据不足时，同时统计和现实还是具有差距. Black-box decisions. DL 方法是黑盒? 与传统优化器不同, 当前学到的优化方法不能给 DBA 提供方法 Integration cost. 大部分 learned optimizers 都是研究原型, 很少结合 DBMS. Bao 克服了上面的问题，可以集成到 PostgreSQL，https://github.com/learnedsystems/baoforpostgresql 核心思想：避免 learning an optimizer from scratch。 take an existing optimizer (PostgreSQL’s optimizer), and learn when to activate (or deactivate) some of its features on a query-by-query basis. In other words, Bao is a learned component that sits on top of an existing query optimizer in order to enhance query optimization, rather than replacing or discarding the traditional query optimizer altogether 在 PostgreSQL 优化器之上开始做优化，那如果之前的优化器不再更新，又或者更好的优化器出现了怎么办呢？ PostgreSQL optimizer might underestimate the cardinality for some joins and wrongly select a loop join when other join algorithms (e.g., merge join, hash join) would be more effective This occurs in query 16b of the Join Order Benchmark (JOB)， and disabling loop-joins for this query yields a 3𝑥 performance improvement (see Figure 1). Yet, it would be wrong to always disable loop joins. For example, for query 24b, disabling loop joins causes the performance to degrade by almost 50𝑥, an arguably catastrophic regression. Bao assumes a finite set of hint sets and treats each hint set as an arm in a contextual multi-armed bandit problem. multi-armed bandit problem 多臂赌博机问题，机器学习问题，每个老虎机赢的概率不一样，不知道概率，选择哪个能做到最大收益？ Bao learns a model that predicts which hints will lead to good performance for a particular query. When a query arrives, our system selects a hint set, executes the resulting query plan, and observes a reward. Over time, Bao refines its model to more accurately predict which hint set will most benefit an incoming query. For example, for a highly selective query, Bao can automatically steer an optimizer towards a left-deep loop join plan (by restricting the optimizer from using hash or merge joins), and to disable loop joins for less selective queries By formulating the problem as a contextual multi-armed bandit, Bao can take advantage of Thompson sampling, a well-studied sample efficient algorithm Because Bao uses an underlying query optimizer, Bao has cardinality estimates available, allowing Bao to adapt to new data and schema changes just as well as the underlying optimizer. 其他 learned query optimization 需要重新学习 传统优化器 已知的信息，Bao 可以直接开始学习怎么去调优底层优化器，并且相较于传统优化器可以减少 tail latency。 Short training time, 1 hour, by taking full advantage of existing query optimization knowledge, which was encoded by human experts into traditional optimizers available in DBMSes today Robustness to schema, data, and workload changes, maintain performance even in the presence of workload, data, and schema changes, leveraging a traditional query optimizer’s cost and cardinality estimates Better tail latency, Bao is capable of improving tail performance by orders of magnitude with as little as 30 minutes to a few hours of training Interpretability and easier debugging, be inspected using standard tools, Low integration cost, every SQL Extensibility, adding new query hints over time, w/o retraining. Additionally, Bao’s feature representation can be easily augmented with additional information which can be taken into account during optimization, although this does require retraining. cache state drawback: query optimization time increases, Bao must run the traditional quer","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"SYSTEM MODEL Bao combines a tree convolution model, a neural network operator that can recognize important patterns in query plan trees, with Thompson sampling (solving contextual multi-armed bandit problems) Generating 𝑛 query plans: When a user submits a query, Bao uses the underlying query optimizer to produce 𝑛 query plans, one for each set of hint. While some hints can be applied to a single relation or predicate, Bao focuses only on query hints that are a boolean flag (e.g., disable loop join, force index usage). The sets of hints available to Bao must be specified upfront, empty -\u0026gt; original optimizer Estimating the run-time for each query plan: Afterwards, each query plan is transformed into a vector tree (a tree where each node is a feature vector) -\u0026gt; value model(tree convolutional neural network) -\u0026gt; which predicts the quality (e.g., execution time) of each plan -\u0026gt; parallel Selecting a query plan for execution: best expected performance -\u0026gt; standard supervised fashion and pick the query plan with the best predicted performance. However, value model might be wrong, might not always pick the optimal plan and never try alternative strategies never learn when we are wrong. Thompson sampling explore a specific query offline and guarantee that only the best plan is selected. Once the query execution is complete, the combination of the selected query plan and the observed performance is added to Bao’s experience. Periodically, this experience is used to retrain the predictive model, creating a feedback loop Assumptions and Limitations: assumes hints result in semantically equivalent query plans, Bao always uses the hints for the entire query plan. Bao cannot restrict features for only a part of a query plan. RL converge -\u0026gt; small size of action Bao 的 vaule model 永远不会从错误中学习？而且看上去不支持 subquery 优化，不知道是不是时间复杂度太高的原因。 ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#system-model"},{"categories":["Learned Data Systems"],"collections":null,"content":"SELECTING QUERY HINTS Bao’s learning approach, optimization goal, formalize it as a contextual multi-armed bandit problem., apply Thompson sampling, a classical technique used to solve such problems Bao models each hint set in the family of hint sets Bao also assumes a user-defined performance metric 𝑃 Contextual multi-armed bandits (CMABs): Thompson sampling: RL 的一些东西，不看了 ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#selecting-query-hints"},{"categories":["Learned Data Systems"],"collections":null,"content":"Predictive model The core of Thompson sampling, Bao’s algorithm for selecting hint sets on a per-query basis, is a predictive model that estimates the performance of a particular query plan. Bao 是怎么转换 query plan trees: binarizing the query plan tree and encoding each query plan operator as a vector, optionally augmenting this representation with cache information Binarization: Many queries involve non-binary operations like aggregation or sorting. However, strictly binary query plan trees (i.e., all nodes have either zero or two children) are convenient because they greatly simplify tree convolution (explained in the next section). Vectorization: Each node in a query plan tree is transformed into a vector containing: (1) a one-hot encoding of the operator, (2) cardinality and cost information, and optionally (3) cache information Tree convolutional neural networks Integrating with Thompson sampling: ? ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#predictive-model"},{"categories":["Learned Data Systems"],"collections":null,"content":"Training loop 提出了新的 optimization, 在云平台上可以热加载 GPU？ ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#training-loop"},{"categories":["Learned Data Systems"],"collections":null,"content":"POSTGRESQL INTEGRATION install and use with PostgreSQL, hook system Per-query activation: sits on top of a traditional optimizer, When Bao is activated, Thompson sampling is used to select query hints. When Bao is deactivated, the PostgreSQL optimizer is used. Note that even when Bao is disabled, Bao can (optionally) still learn from query executions. off-policy reinforcement learning ?? Active vs. advisor mode: In active mode, Bao operates as described above, automatically selecting hint sets and learning from their performance. In advisor mode, Bao does not select hint sets (all queries are optimized by the PostgreSQL planner), but still observes the performance of executed queries and trains a predictive model. Triggered exploration: query regressions, because Bao actively explores new query plans, regressions may be more erratic ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#postgresql-integration"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORKS One of the earliest applications of learning to query optimization was Leo [72], which used successive runs of the similar queries to adjust histogram estimators. Neo [51] showed that deep reinforcement learning could be applied directly to query latency, and could learn optimization strategies that were competitive with commercial systems after 24 hours of training. However, none of these techniques are capable of handling changes in schema, data, or queryworkload, and none demonstrate improvement in tail performance. Works applying reinforcement learning to adaptive query processing have shown interesting results, but are not applicableto existing, non-adaptive systems like PostgreSQL. 我不认为 Bao 和 Neo 有太大的差别，同样都是 RL，为什么说 Neo 无法适应变化呢？ ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#related-works"},{"categories":["Learned Data Systems"],"collections":null,"content":"EXPERIMENTS real-world database workloads, quantifying not only query performance, but also on the dollar-cost of executing a workload ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#experiments"},{"categories":["Learned Data Systems"],"collections":null,"content":"Setup IMDb dataset, new real-world datasets and workload called Stack, Corp dataset 实验结果只是和 PG, ComSys 比了一下 latency，效果还不错，大概 20% 的改善， P99 也改善很多，但为什么这部分不和 Neo 对比呢？ Query optimization time: The maximum optimization time required by PostgreSQL was 140ms, for the commercial system 165ms, and for Bao 230ms. For some applications, a 70ms increase in optimization time could be acceptable. Moreover, our current prototype is simple (e.g., our inference code is in Python), and thus a lot of optimization potential exists 230ms 比起 140ms 虽然不多，但也不算少吧，而且用了大量并行 Prior learned optimizers: Neo [51] and DQ [40] are two other learning based approach to query optimization. Like Bao, Neo uses tree convolution, but unlike Bao, Neo does not select hint sets for specific queries, but instead fully builds query execution plans on its own. DQ uses deep Q learning [56] with a hand-crafted featurization and a fully-connected neural network (FCNN). We compare performance for the IMDb workload in Figure 14 (average of 20 repetitions on an N1-16 machine with a cutoff of 72 hours). For Figure 14a, we uniformly at random select a query to create a stable workload, and for Figure 14b we use our original dynamic workload. With a stable workload, Neo is able to overtake PostgreSQL after 24 hours, and Bao after 65 hours. This is because Neo has many more degrees of freedom than Bao: Neo can use any logically correct query plan for any query, whereas Bao is limited to a small number of options. These degrees of freedom come at a cost, as Neo takes significantly longer to converge. After 200 hours of training, Neo’s query performance was 15% higher than Bao’s. DQ, with a similar degrees of freedom as Neo, takes longer to outperform PostgreSQL, possibly due to FCNNs having a poor inductive bias [50] for query optimization [51]. With the dynamic workload (Figure 14b), Neo and DQ’s convergence is significantly hampered, as both techniques struggle to learn a policy robust to the changing workload. With a dynamic workload, neither DQ nor Neo is able to overtake Bao within 72 hours. 65h \u0026gt; 24h 为什么 Bao 会被 a small number of options 限制呢，文章也没有仔细解释 Bao 是怎么适应 dynamic workload 的 ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#setup"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION AND FUTURE WORK 实际上 Bao 团队和 Neo 团队都是一批人，应该是注重点不太一样： Neo 是第一个提出深度学习 query optimizer 的，用一个未补全的 query plan encoding 之后用树形网络做预测 cost，并用最小堆搜索最优 plan，忽略 cardinality 等等传统，为每个 predicate 用浮点数存 feature 设计 word vector 存语义，可能好于 histgram 预测。但缺点是训练时间长，只在平均上提升比较好，高质量的计划很可能在随机样本中缺失，样本量必须大，模型训练成本太高。 而 Bao 放弃做 Optimizer，为成熟数据库提供 query hints 如 boolean flag 表示是否使用 hash join, merge join, index scan 等等，枚举所有 hint set (access method + join method) 视作多臂老虎机，用 Thompson sampling 平衡选一个最优的。论文提到了 Bao 收敛快于 Neo，快很多，而且可以根据 workload 调整，在 JOB 数据集上表现非常好，但还是可能会去掉一些最优的，选择次优的。 去年也有一篇 Lero，采用成对方法训练分类器，也不是从头开始，很类似。尝试解决 Bao 的一些问题: 1. 在整个计划搜索过程中，通常对整个查询应用提示集。如果查询的不同部分（子查询）具有不同的最佳选择，则在查询级别调整标志可能会错过寻找高质量计划的机会 2. 可用的提示集是特定于系统的。优化器通常包含数百个标志来实现/禁用某些优化规则。在实践中枚举各种组合是不可行的。手动选择提示集的有效子集需要对系统进行深入的理解，并对工作负载进行综合分析。 Lero 甚至实现了一个 Bao+, 用更多计划进行模型训练，但好像没有仔细说。同时拓展了更多的数据集， IMDB，JOB，STATS，TPC-H，TPC-DS，等等 ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#conclusion-and-future-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"Paer Reading The paper proposes a more practical model called Bao, which sits on top of an existing query optimizer (PostgreSQL) and utilizes machine learning and reinforcement learning for database query optimization. Bao solves lots of the issues with previous learned optimizers, including long training times, inability to adapt, and being difficult to interpret while offers advantages like faster training, robustness to workload and data changes, and better interpretability. The core idea of Bao is to utilize tree convolutional neural networks to analyze query plans and leverage ML techniques called Multi-armed bandit and Thompson Sampling to choose the best query plan among alternatives. Bao addresses a major limitation of previous learned optimizers – long training times. By employing Thompson Sampling to solve bandit problem, Bao significantly reduces training time compared to traditional machine learning methods used for query optimization, and achieves similar performance to PostgreSQL in 2 hours training. Bao is more robust to changes in schema, data distribution, and query workload. By combining modern tree convolutional neural networks and Thompson sampling to provide query hints, Bao offers interpretability unlike other black-box machine learning models and aims to reduce tail latency and minimize the occurrence of slow-running queries. The paper also proposes a new method to optimize resource usage in cloud environments, specifically for tasks involving training a neural network with GPU and then using it for query optimization with CPU, which can reduce overall costs. Bao uses Thompson Sampling to enumerate all hint sets in the multi-armed bandit problem, but during the searching process, the hint set is applied to the entire query. If different parts of the query (subqueries) have different best choices, it will lead to the final choice is not optimal. Enumerating all collections is very time-consuming, the query optimization time of Bao is nearly 90ms higher than PostgreSQL. And for different systems, the size of the hint sets may be different, somtimes it might include hundreds of boolean flags. The architectures of Bao and Neo look very similar, though the authors of these papers are from the same group, the paper does not carefully compare Neo and Bao, especially the performance of training time and query latency. First, I would explain the difference between Neo and Bao in more depth, for example, why they all use modern tree convolutional neural networks. Besides, I will collect more data sets and testing their performance on more data benchmarks (such as TPC-H, TPC-DS, STATS). If possible, I would try some methods to limit the hint sets to reduce the current query optimization time, such as manual selection or from expert experiences. ","date":"2024-03-17","objectID":"/posts/bao-learned-query-opt/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Bao: Making Learned Query Optimization Practical [SIGMOD 21]","uri":"/posts/bao-learned-query-opt/#paer-reading"},{"categories":["Learned Data Systems"],"collections":null,"content":"Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022] ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#zero-shot-cost-models-for-out-of-the-box-learned-cost-prediction-vldb-2022"},{"categories":["Learned Data Systems"],"collections":null,"content":"Abstract 本文介绍了 zero-shot cost model，该模型可以使学习的成本估算能够 generalizes to unseen databases。与最 state-of-the-art 的工作负载驱动的方法相反，这些方法不必在每个新数据基础上执行大量 training queries ，zero-shot cost models thus allow to instantiate a learned cost model out-of-the-box without expensive training data collection。为了 zero-shot cost models，本文提出 a new learning paradigm based on pre-trained cost models。As core contributions to support the transfer of such a pre-trained cost model to unseen databases, we introduce a new model architecture and representation technique for encoding query workloads as input to those models. As we will show in our evaluation, zero-shot cost estimation can provide more accurate cost estimates than state-of-the-art models for a wide range of (real-world) databases without requiring any query executions on unseen databases. Furthermore, we show that zero-shot cost models can be used in a few-shot mode that further improves their quality by retraining them just with a small number of additional training queries on the unseen database. 以后尽可能少地直接机翻原论文了，太多名词机翻效果很差，反而降低阅读速度。基本上读完就写一点感想。 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"Introduction Motivation: Accurate physical cost estimation (i.e., estimating query latencies) is crucial for query optimization in DBMSs. Classically, cost estimation is performed using models that make several simplifying assumptions. As a result, such models often over or underestimate runtimes, leading to suboptimal planning decisions that degrade the overall query performance. Recently, machine learning has thus been used for learned cost models that do not need to make such simplifying assumptions. While it was shown that the cost estimates of such learned cost models are significantly more accurate than those of the traditional cost models, the existing approaches rely on workload-driven learning where models have to observe thousands of queries on the same database for which the cost prediction should be performed. This workload execution is required to gather the training data which can take hours (or days) since tens of thousands of queries need to be executed on potentially large databases In Figure 1, we show the cost estimation accuracy depending on how many hours we allow for gathering the training data for a workload-driven model. As we can see, even for a medium-sized database such as IMDB, it takes more than 5 hours of running queries on this database to gather enough training data such that the cost estimation model can provide a decent accuracy. Unfortunately, collecting training data by running queries is not a one-time effort. In fact, the training data collection has to be repeated for every new database a learned model should be deployed for. This is due to the fact that current model architectures for workload-driven learning tie a trained model to a particular database instance. Consequently, for every (new) unseen database we not only have to train a model from scratch but also gather training data in the form of queries. And even for the same database, in case of changed data characteristics due to updates, training data collection needs to be repeated. Overall, these repeated high costs for obtaining training data for unseen databases render workloaddriven learning unattractive for many practical deployments. 这里几个名词很奇怪 unseen database 和他 database 的概念也不同，实际上指的是具有特定数据特点的数据集。 对于 query optimization 需要收集很多数据集，并且精读不够高，而且对于新的数据集又要重新收集。zero-shot 希望能用一种训练模型来泛化数据集。 Contributions: In this paper, we thus suggest a new learning paradigm for cost estimation called zero-shot cost models that reduces these high efforts. The general idea behind zero-shot cost models is motivated by recent advances in transfer learning of models. Similar to other approaches such as GPT-3 which enable zero-shot learning for NLP, a zero-shot cost model is trained on a wide collection of different databases and workloads and can thus generalize out-of-the-box to a completely unseen database without the need to be trained particularly on that database. In fact as depicted in Figure 1, zero-shot cost models can provide a high accuracy and often even outperform existing workload-driven approaches that have been trained on large sets of training queries. Moreover, as we also show in Figure 1, zero-shot cost models can additionally be fine-tuned on the unseen database with just a few training queries and the resulting few-shot models further improve the accuracy. 本文的想法是类似语言模型训练，进行无标注的大量预先训练，所以可以对没见过的数据集直接进行代价估计，具有较高的准确率，甚至比 workload-driven 的表现好，同时也可以 fine-tuned。 One could now argue that it might be a significant effort to collect sufficient training data across databases for pre-training a zero-shot model. However, in contrast to workload-driven models which require training data for every unseen database, training data collection is a one-time effort; i.e., once trained the zero shot model can be used for any new unseen database. In fact, in our evaluation we show that zero-shot models can provide high accuracies for a wide-variety of real-world databases. Moreover, for historical traces can be used which eliminates the need to collect any t","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"Overview ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"Problem Statement 主要目标是预测在 unseen database 上的 query latency，并且没有提前知道任何 query。 文章说 zero-short cost estimation 和 stoa workload-driven cost model 有强烈的对比。并且希望能够在流数据库、图数据库做一些未来的工作。 Finally, while we believe that zero-shot learning for DBMSs is more generally applicable, we restrict ourselves in this paper to cost estimations for relational DBMSs (both single-node and distributed). In particular, zero-shot cost models for other types of systems such as graph-databases or streaming systems are interesting avenues of future work. ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#problem-statement"},{"categories":["Learned Data Systems"],"collections":null,"content":"Our Approach A key challenge for developing zero-shot cost models is the question how to design a model that allows to generalize across databases. Typically, these consist of two models: a database-agnostic model to estimate the runtime cost and a database dependent model (e.g., histograms) to capture data characteristics. When predicting the cost of a query, the estimated cardinalities and other characteristics (i.e., outputs of the database-dependent models) serve as input to the general database-agnostic cost model which captures the general system behavior (e.g., the costs of a sequential scan grows linearly w.r.t. the number of rows). While the classical models are lightweight, they often largely under or overestimates the true costs of a query since models are too simple to capture complex interactions in the query plan and data. classical cost model 包含两个 model，估计运行时 cost 和捕获 model？ 这个模型分开了关注点，用一个更加丰富的具有知识的 learned 模型， similarly takes data characteristics of the unseen database as input to predict query runtimes in a database-agnostic manner。用不同的 query plan 和 data characteristic of the plan 进行训练。 to predict the runtime of a query plan on a new (unseen) database, we feed the query plan together with its data characteristics into a zero-shot model. data characteristics 是什么？tuple width, intermediate cardinalities，所以需要数据驱动学习？但这是否违反了 zero-shot？还是说只需要训练数据特征而不是 query 训练然后推理，和语言模型类似 但是怎么 estimate runtime of a plan given its data characteristics? 本文提出了一种新的 query 表示方法，旧的不是 transferable 也不是 zero-shot。这种表现方式 completely relies on features that can be derived from any database to allow the model to generalize to unseen databases. For example, predicates for filter operations in a query are encoded by the general predicate structure (e.g., which data types and comparison operators are used in a predicate) instead of encoding the literals. In addition, data characteristics of a filter operator (e.g., input and output cardinality to express the selectivity) are provided as additional input to a zero-shot model. That way, a zero-shot model can learn the runtime overhead of a filter operation based on database-agnostic characteristics. We present details of our query representation in Section 3. Finally, a last important aspect of zero-shot cost models is that they can easily be extended to few-shot learning. Hence, instead of using the zero-shot model out-of-the box (which already can provide good performance), one can fine-tune the model with only a few training queries on an unseen database ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#our-approach"},{"categories":["Learned Data Systems"],"collections":null,"content":"Zero-Shot Cost Models how we devised such a transferable query representation how inference and training of a zero-shot model that uses this representation works ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#zero-shot-cost-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Query Representation State-of-the-art workload-driven models for cost estimation do not use transferable query representations and can thus only be used on the database they were trained on Query Representation for Workload-Driven Models: hard-code the model against a single database, attribute names (e.g., those used in filter predicates) are typically encoded using a one-hot encoding assigning each attribute present in the database a specific position in a feature vector. For instance, the attribute production_year of the IMDB dataset might be encoded using the vector (0,1,0)(assuming that there are only three attributes in total). If the same model should now be used to predict query costs for the SSB dataset, some attributes might not even exist or even worse they might exist but have very different data distributions or even a different data type. In fact, non-transferable feature encodings are not only used for attributes but in various places of the query representation such as encoding table names or literals in filter predicates. Query Representation for Zero-Shot Cost Models: Hence, for zero-shot cost models we require a new query representation that is transferable across databases. The main idea of the transferable representation we suggest in this paper is shown in Figure 3. At the core, a query plan and the involved tables and attributes are represented using a graph where graph nodes use transferable features 1 (i.e., features that provide meaningful information to predict runtime on different databases). This representation then serves as input for the training and inference process of zero shot cost models 2 ~ 4 that we explain in the subsequent sections. In the following, we discuss the graph encoding of the transferable featurization in detail Graph Encoding of Query Plans: While graph-based representations have been already used to represent query operators of a query plan [28], our representation has significant differences. First, as shown in Figure 3 1 , our representation not only encodes physical plan operators as nodes (gray) in the graph as in previous work [28], but it also covers all query plan information more holistically using different nodes types for attributes (green), tables (blue) as well as predicate information (red). Second, as discussed before, previous approaches also covered such information, however, they used one-hot-encodings (which are non-transferable) while our representation captures the query complexity in a transferable way For instance, to encode filter predicates, different from previous approaches we encode the predicate structure as nodes (red) without literals. In particular, we encode information such as data types of the attributes and operators used for comparisons. For example, the filter predicate company_type_id=2 for the query 0 in Figure 3, is encoded using an attribute node (𝑥5) with the comparison node = (𝑥7). As such, a zero-shot cost model provided with the transferable features (e.g., intermediate cardinalities which are given by the data-driven models) can infer the complexity of the predicates to estimate the query runtime Transferable Featurization: nodes in the graph 1 (e.g., plan operators as shown in gray) are transferable. In particular, when used on different databases, features should not encode any implicit information that hinder the transfer of the model to a new unseen database. The set of such features used for the different node types in our query representation is depicted in Table 1. For instance, attribute nodes (green) use features such as the data type or the width in bytes. Similarly, for tables (blue nodes), we use other transferable features (e.g., the number of rows as well as the number of pages on disk) transferable features can either characterize the query plan (e.g., operator types) or represent the data characteristics (e.g., intermediate cardinalities) For transferable features that represent data characteristics many can be de","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#query-representation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Inference on Zero-Shot Models 用了每个图节点的特征向量来做 MLP 多层感知器（机器学习），加上 GNN 做消息传递，用 DAG 表示查询 We thus use a novel bottom-up message passing scheme through the graph (i.e., in topological ordering) to obtain an updated hidden state ℎ′𝑣 of a node 𝑣 that contains all information of the child nodes. 机器学习内容，没太理解 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#inference-on-zero-shot-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Training Zero-Shot Models 在几个数据集和查询集上做了训练 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#training-zero-shot-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Deriving Data Characteristics zero shot model 和单个数据集的分布无关，所以能预测 unseen dataset，同时为了实现该特性，需要提供数据的特征（属性宽度、页面数量、表格行数量） 所以需要人为 label？没有解释清楚到底怎么预测 cardinality estimation 本模型是和 traditional approach 和 data-driven model 进行比较，但 zero-shot 最好可以和后者结合，传统方法作为 fallback。 In our evaluation, we see that zero-shot models can still produce reasonable estimates even if only cardinalities estimates from traditional models are available ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:5:4","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#deriving-data-characteristics"},{"categories":["Learned Data Systems"],"collections":null,"content":"Robustness of Zero-Shot Models 针对 unseen datasets, zero-shot model 具有优势，但在什么时候可以观察到足够多的不同训练数据库(和工作负载) ，从而可以鲁棒地推广到 unseen？ 零拍模型的运行时估计的鲁棒性，We then discuss a simple method to detect cases of workload drifts (i.e., the queries at runtime have substantially different characteristics than the training queries) as well as strategies how to tackle this problem. ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#robustness-of-zero-shot-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Estimating the Generalization Performance simply esimate the generalization errors (actually use) estimate if additional training databases will improve the generalization performance 所以训练时用了 subsets，然后用更大的 train set 如果没有改进，则无法提高 generalization 能力 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#estimating-the-generalization-performance"},{"categories":["Learned Data Systems"],"collections":null,"content":"Tackling Workload Drifts new database and workload, we suggest a strategy to detect cases of workload drifts by monitoring the test error and propose to tackle workload-drifts using few-shot learning 这是一个蛮新奇的 idea，用微调过的模型来检测和解决 workload drifts ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#tackling-workload-drifts"},{"categories":["Learned Data Systems"],"collections":null,"content":"Extensions of Zero-Shot Cost Models zero-shot cost estimation can be extended in various directions. ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#extensions-of-zero-shot-cost-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Distributed DBMSs 本文的研究对象还是 单机 DBMS，这一节讨论了如何支持分布式 DBMS，尤其是当查询执行时数据会被打乱 shuffle。并且讨论了一些列存/行存时，查询优化的区别。 然后本文提出了对 查询 进行扩展编码，包含 operator nodes for data shuffling as well as encode data formats (column or row) as a feature of the table node ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:7:1","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#distributed-dbmss"},{"categories":["Learned Data Systems"],"collections":null,"content":"Physical Design Tuning 没懂 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:7:2","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#physical-design-tuning"},{"categories":["Learned Data Systems"],"collections":null,"content":"Other Directions hardware parameters, database knob configurations\u0026hellip; ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:7:3","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#other-directions"},{"categories":["Learned Data Systems"],"collections":null,"content":"A New Benchmark 本文提出了一个新的 benchmark ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#a-new-benchmark"},{"categories":["Learned Data Systems"],"collections":null,"content":"Design Decisions 本文认为传统 TPC-H TPC-DS or SSB 不足够评估成本预测模型，并且因为这些数据是合成的，没法捕捉到相关性来提供 cardinality estimation，也有人基于 IMDB 数据集组了一些新的 workload 病故学习的成本和 cardinality estimation。但这也不能评估 zero-shot，因为要在不同的数据集上训练，需要一个跨越多种数据集的 benchmark。 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:8:1","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#design-decisions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Datasets correlations hardly resemble data distributions found in the real-world 本文使用了真实世界数据集，也加入了 SSB 和 TPC-H ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:8:2","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#datasets"},{"categories":["Learned Data Systems"],"collections":null,"content":"Workloads and Traces 20 个数据集 + 生成的查询 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:8:3","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#workloads-and-traces"},{"categories":["Learned Data Systems"],"collections":null,"content":"Experimental Evaluation Zero-Shot Accuracy, predict costs for unseen databases. Zero-Shot vs. Workload-Driven, compare the training overhead and accuracy with state-of-the-art workload driven approaches Generalization, workload drifts Extensions, distributed DBMSs and different physical designs Training and Inference Performance, compare training efforts to workload-driven models Ablation Study, alternatives of zero-shot models, how many database are sufficient for zero-shot cost models to generalize 实验结果就不看了，文章没有深入讨论他们的数据集的区别，实验结果看起来和 workload-driven 看上去也差不多 ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:9:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#experimental-evaluation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Conclusion The paper proposes a novel zero-shot cost model that enables learned cost estimation to unseen databases without extensive training on new datasets. The architecture of zero-shot cost model is very similar to the language model in NLP. It adopts the idea of ​​pre-training, so that it can perform well on different datasets, and can also be fine-tuned for special datasets. The paper details the methods used to enable zero-shot cost models, including the transferable query representation, training zero-shot models and deriving data characteristics.The paper also discusses the robustness and scalability of zero-shot models (distributed database). The paper proposes a new benchmark spanning multiple datasets, and gives experimental results, proving that zero-shot models can perform excellent cost estimation on unseen datasets The paper proposes a novel zero-shot cost model, and suggests a learning paradigm based on pre-trained cost models. A new model architecture and a representation technique for encoding query workloads as input to these models are introduced, which together allow a zero shot cost model to generalize to an unseen database. The paper derives a method to estimate how accurate the runtime estimations of zero-shot models will be for unseen databases, and provides a new benchmark necessary to evaluate cost estimation models more broadly on a variety of real-world databases. The paper not only discusses the zero-shot cost model based on a single-node DBMS, but also discusses future work, proving that zero-shot cost estimation can also be achieved in a distributed database. It can also be combined with data-driven models or even fine-tuned zero-shot models for better cost estimations and accuracy. The zero-shot cost model may not always accurately predict costs for queries on databases that significantly differ from those seen during the model’s training, because it relies on pre-trained models and there are often large differences between real-world datasets and query workloads. This paper somewhat confuses the concepts of datasets and databases. And while zero-shot models can eliminate the need for extensive training on each new database, they may still require some fine-tuning or a few-shot learning approach to achieve the best performance on specific datasets and queries, which is not completely zero-shot. Although the paper has used 20 widely different data sets for benchmarking, 19 of them were used for training and only 1 was used for testing. It should be tested with more data sets. First, I would try to create a larger and more diverse training and testing dataset that covers a wide range of database schemas and query workloads and extend the benchmark used for evaluating cost models to include more real-world databases and complex queries. This would provide a more comprehensive assessment of the model’s performance and its ability to generalize. Second, extending the zero-shot model to distributed databases or more databases seems very feasible. I might try to expand this part a little further, especially to implement zero-shot cost models fordistributed stream processing or graph databases, because the queries and datasets for stream processing and graph database will be very different. The other parts of the paper are very perfect, especially the architecture that draws on language models, which is very clever. ","date":"2024-03-16","objectID":"/posts/zero-shot-learned/:10:0","tags":["Paper Reading"],"title":"Paper Reading: Zero-Shot Cost Models for Out-of-the-box Learned Cost Prediction [VLDB 2022]","uri":"/posts/zero-shot-learned/#conclusion"},{"categories":["Learned Data Systems"],"collections":null,"content":"SPADE: Synthesizing Assertions for Large Language Model Pipelines Synthesizing Assertions Pipelines 合成断言、流水线 ","date":"2024-03-01","objectID":"/posts/spade-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#spade-synthesizing-assertions-for-large-language-model-pipelines"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT 将大型语言模型（LLM）用于定制、重复数据 pipeline 的操作具有挑战性，特别是由于其不可预测和潜在的灾难性故障。认识到这些错误的不可避免性，我们将重点放在识别 LLM 在作为数据生成 pipeline 的一部分重复使用时可能生成错误响应的情况。我们提出了 spade，一种自动合成断言的方法，可以识别不良的 LLM 输出。SPADE 分析提示版本历史，创建候选断言函数，然后选择满足覆盖率和准确性要求的最小集合。在对九种不同的实际 LLM pipeline 进行测试时，与简单的基线相比，spade 有效地减少了 14% 的断言数量，并将错误失败率降低了 21%。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION 在数据 pipeline 中使用大型语言模型（LLM）引起了广泛关注[17]。这种热情很大程度上归功于其简单性：无需大型标注数据集，只需用自然语言提示 LLM，就能轻松创建一个自动化的智能 pipeline ，在数秒内执行任意数据生成任务。然而，将 LLM 用于无监督、重复或大规模的数据生成任务却面临着巨大的挑战[34]，例如潜在的错误、不恰当的反应或幻觉[44, 57]。 Data Management For Large Language Models: A Survey，没太理解引用这篇论文的意思，是在预训练、微调时管理数据 用 LLM 制作应用时，始终无法避免 potential errors, inappropriate responses, or hallucination 考虑使用 LLM 生成个性化电影推荐说明的电影流媒体平台。开发人员可能会编写类似的提示模板： \u0026ldquo;根据用户的以下信息，为用户应该观看 {movie_name} 的原因编写个性化说明： {personal_info}\u0026ldquo;这样的提示模板，针对多个用户-电影配对执行。模板化变量代表的信息将在运行时注入 pipeline ，使 pipeline 能够为各种输入生成数据。从理论上讲，这种提示似乎足够了，但开发人员在对一些示例输入进行测试时可能会发现一些问题：LLM 可能会引用用户从未看过的电影，引用敏感属性（如种族或民族），甚至出现响应过短等基本问题。 为了解决这些缺陷，有人提出了在 LLM pipeline 中加入断言 assertions 的框架，以便在到达最终用户之前过滤掉不良生成[37, 49]。鉴于 LLM 错误的不可避免性[23]，此类断言是成功部署生成数据的 LLM pipeline 的关键。然而，开发人员发现为自定义 LLM pipeline 编写断言非常困难 [35]。面临的挑战包括：预测所有可能的 LLM 故障模式；使用各种规范方法（如 Python 函数或 LLM 调用）编写断言耗时；断言（尤其是涉及 LLM 调用的断言）必须精确；以及许多 LLM pipeline 开发人员缺乏软件工程专业知识或编码经验 [26，57]。此外，如果断言过多或不具信息性，开发人员在监控其结果时可能会不知所措 在本文中，我们发现了一个新问题，即如何为任何利用 LLM 的数据生成 pipeline 自动生成一组好的断言。对于 LLM 响应，一个断言返回真（即成功）或假（即失败），而一组断言则返回单个断言的联结。一组 \u0026ldquo;好 \u0026ldquo;的断言具有最小的重叠，允许开发人员在错误的成功和错误的失败之间进行权衡。我们将问题分解为两个部分\u0026ndash;候选断言生成和过滤 candidate assertion generation and filtering \u0026ndash;并提出了 spade（ System for Prompt Analysis and Delta-Based Evaluation 图 1）。 delta 在这里是 diff 的意思？ candidate assertion 和 filtering 都是怎么实现的？看上去还是 LLM 实现的 对于候选断言的生成，我们可以考虑直接询问 LLM \u0026ldquo;为 x 提示编写断言\u0026rdquo;，但这可能无法满足开发人员的要求。我们建议从 prompt deltas（即两个连续提示符版本之间的差异）中生成候选断言，这通常表示 LLM 的特定故障模式。例如，开发人员可能会添加类似 \u0026ldquo;避免花哨语言 \u0026ldquo;的指令，从而促使断言检查响应语言。我们对来自 LangChain（一家帮助人们构建 LLM pipeline 的初创公司）用户的 19 个自定义数据生成 pipeline 和 pipeline 提示版本历史进行了分析，从而形成了一个 a taxonomy of prompt deltas。Spade 首先在分类法中自动对提示脱节进行分类，然后将 Python 函数（可能包括 LLM 调用）合成为可用断言。我们公开发布了 SPADE 的这一组件，展示了这些断言的潜力，它在金融、医药和 IT 等 10 多个领域有 1300 多个用途 [46] 。 在 langchain 上的 SPADE 例子 我们对候选断言的分析发现了冗余、不准确和大量函数，仅几个 prompt deltas 就往往超过 50 个。冗余源于对提示相似部分的重复完善，或提示指令含糊不清（如 \u0026ldquo;返回一个简洁的回复\u0026rdquo;）。减少冗余并不简单，即使对工程师来说也是如此，因为断言可能涉及精确度不同的 LLM 调用，这就需要一个自动过滤组件。一种方法是使用开发人员标记的 LLM 响应来估算每个断言的错误失败率（false failure rate FFR），并剔除每个超过开发人员定义的 FFR 阈值的断言。然而，其余断言的 FFR 可能会累计超过这个阈值，冗余可能会持续存在。我们的研究表明，选择一小部分断言来满足故障覆盖率和 FFR 标准是 NP-hard 的。也就是说，我们可以用整数线性规划（ILP）来表达这个问题，并使用 ILP 求解器来找出解决方案。然而，我们发现标记的响应可能无法代表所有故障模式，从而导致遗漏有价值的断言。例如，在我们的电影推荐场景中，如果开发人员标注样本中的所有回复都低于 200 字，那么能够正确验证 LLM 生成的注释是否少于 200 字的断言就会被放弃。为了扩大覆盖范围，可以使用主动学习和弱监督方法为每个候选断言采样和标注新的 LLM 提示-响应对，但这可能很昂贵，或者非程序员无法使用。我们引入了断言子块（assertion subsump-tion）作为确保全面覆盖的一种方法：如果一个断言不包含另一个断言的故障模式，那么两个断言都会被选中。因此，SPADE 会选择一组最小的断言，并遵守故障覆盖率、准确性和子集约束。总的来说，我们做出了以下贡献： Today’s research release of ChatGPT is the latest step in OpenAI’s iterative deployment of increasingly safe and useful AI systems. Many lessons from deployment of earlier models like GPT-3 and Codex have informed the safety mitigations in place for this release, including substantial reductions in harmful and untruthful outputs achieved by the use of reinforcement learning from human feedback (RLHF). https://openai.com/blog/chatgpt?ref=blog.cg-wire.com OpenAI 用 RL 和人工监督，来规避一些安全问题和减少不真实输出 Prompt Deltas for Candidate Assertion Generation：我们发现 prompt version history 是数据生成 LLM pipeline 正确性标准的丰富来源。我们根据 19 个具有版本历史的 LLM pipeline 的推断出了断言标准分类法，并将其公布于众。我们公开发布了一款工具，用于为任何 pipeline 生成候选断言，通过 1300 多次部署为自动生成断言的实用性提供了早期证据，同时也为筛选这些断言提供了机会。 Filtering Method that Requires Little Data: 我们证明，在覆盖故障模式和满足准确性要求的前提下，选择最小的断言集是一个 NP-Hard。我们将这一问题表述为 ILP。鉴于断言可能是冗余和不准确的，而且 LLM 开发人员可能不具备工程专业知识或足够的数据来使用现有的数据驱动方法进行 ML pipeline 测试（如训练模型），我们引入了断言归并作为在低数据量环境下覆盖率的新型替代方法。 Empirical Study: 我们介绍了 spade，这是我们为数据生成 LLM pipeline 自动生成断言的系统。对于九个具有提示版本历史的 LLM pipeline ，我们收集了带标签的 prompt-response pairs（其中八个已开源）。 https://spade-beta.streamlit.app/ 在所有 pipelines 中，Spade 生成的断言都具有 good failure coverage 和 few false failures（即无法做出良好响应）。我们基于子假设的 ILP 优于不考虑断言间交互的简单基线，减少了 14% 的断言，降低了 21% 的错误失败率。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"IDENTIFYING CANDIDATE ASSERTIONS 我们的第一个目标是生成一组 candidate assertions。我们将介绍 prompt deltas 如何为候选断言提供信息，并解释如何从中得出候选断言。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#identifying-candidate-assertions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Prompt Deltas 单步 LLM 流水线由一个提示模板 P 组成，该模板通过对输入元组 𝑡 进行格式化，生成一个提示 𝑝，并返回一个响应 𝑟。P 可以有很多版本，这取决于开发者如何迭代他们的 prompt template。让 P0P_0P0​ 表示空字符串，即第 0 个版本，让 PiP_iPi​ 表示模板的第 i 个版本。在第 1 节的电影推荐示例中，假设有 7 个版本，其中 P7 如下： Write a personalized note for why a user should watch {movie_name} given the following informa- tion about the user: {personal_info}. Ensure the recommendation note is concise, not exceeding 100 words. Mention the movie’s genre and any shared cast members between the {movie_name} and other movies the user has watched. Mention any awards or critical acclaim received by {movie_name}. Do not mention anything related to the user’s race, ethnicity, or any other sensitive attributes 我们将 prompt delta ΔP𝑖+1ΔP_{𝑖+1}ΔPi+1​ 定义为 PiP_iPi​ 与 Pi+1P_{i+1}Pi+1​ 之间的差值。具体来说，提示 ΔP 是一组句子，其中每个句子都标记为添加（即 \u0026ldquo;+\u0026quot;）或删除（即\u0026rdquo;-\u0026quot;）。例如，表 1 显示了多个版本的 ΔPs。ΔP𝑖 中的每个句子都由添加（即 P𝑖 中的新句子，P𝑖-1 中不存在）和删除（即 P𝑖-1 中的句子，P𝑖 中不存在）组成。对句子的修改由删除和添加表示\u0026ndash;例如，表 1 中的 ΔP6 包含了从 P5 中添加到句子中的一些新指令。如表 1 最右边一栏所示，ΔP𝑖 中的每一个添加都表示可能的断言标准。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#prompt-deltas"},{"categories":["Learned Data Systems"],"collections":null,"content":"Prompt Delta Analysis 我们分析了从 LangChain 用户那里收集到的 19 个 LLM pipeline，每个 pipeline 由 3 到 11 个历史提示模板版本组成。这些 pipeline 涵盖各种任务，从生成工作总结到自定义问答聊天机器人。附录 B 中的表 6 显示了这些 pipeline 的摘要，包括每个 pipeline 的描述和提示版本的数量。对于每个 pipeline ，我们将提示符（即 ΔP𝑖）分为不同类型\u0026ndash;例如，指示 LLM 在每个回复中包含一个新短语（即包含），或指示 LLM 以某种语气回复（即定性标准）。我们对分类进行了 4 次反复修改，最终形成了图 2 中的分类法。提示版本的分类注释数据集可在网上找到 3。大多数提示符都属于数据整合（即添加占位变量）和工作流程描述（即添加指示让 LLM 更准确地执行任务）。在表 2 中，我们使用引言中的电影推荐 pipeline 示例，展示了分类法中每个类别的提示分隔符示例 当从自动识别的提示分隔线中得出断言候选项时，断言质量显然取决于识别类别的准确性。因此，我们确认了 GPT-4 对提示分界点的正确分类（截至 2023 年 10 月）。我们为 19 个 pipeline 的所有提示版本分配了基本真实类别，GPT-4 的 F1 得分为 0.8135。用于从提示分段中提取类别的提示详见附录 C 什么是 F1 score？ 在二进制分类和信息检索系统的统计分析中，F-SCORE 或 F-MEASIE 是对预测性能的量度。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#prompt-delta-analysis"},{"categories":["Learned Data Systems"],"collections":null,"content":"From Taxonomy to Assertions 利用我们的分类法，SPADE 使用 LLM 为 prompt deltas 分类并生成断言，详见附录 C。对于每个 ΔP𝑖 ，SPADE 都会提示 LLM 为断言提出尽可能多的标准，每个标准都与分类标准的类别一致 (e.g., Figure 3) 标准 Criterion 被宽泛地定义为对示例进行操作并评估为 \u0026ldquo;真 \u0026ldquo;或 \u0026ldquo;假 \u0026ldquo;的一些自然语言表达式（例如，\u0026ldquo;check for conciseness\u0026rdquo;）。Spade 会分析每个 ΔP𝑖 而不是只分析最后一个提示版本，原因有以下几点：开发人员通常会删除提示中的指令以降低成本，同时期望获得相同的行为[35]；提示包含固有的歧义，并暗示了评估某些标准的多种方法；如果只分析一个版本，复杂的提示可能会导致遗漏断言。因此，分析每个 ΔP𝑖 会增加生成相关断言的可能性 对于每个 delta，spade 都会收集已识别的标准，并再次提示 LLM 创建 Python 断言函数。合成函数可以使用外部 Python 库，也可以针对复杂的标准向 LLM 提出二进制查询。在函数合成时，LLM 会被指示，如果标准指定得很模糊或可以解释，例如 \u0026ldquo;check for conciseness\u0026rdquo;，它就可以生成多个函数，每个函数都对标准进行评估。在这个简洁性示例中，LLM 可以返回多个函数\u0026ndash;一个将回复分成句子并确保不超过（例如）3 个句子的函数，一个将回复分成单词并确保不超过（例如）25 个单词的函数，或者一个将回复发送给 LLM 并询问回复是否简洁的函数。这一步骤的总体结果是候选函数的多集合 𝐹 = {𝑓1, \u0026hellip;, 𝑓𝑚} 。 我们的方法采用了两步流程，因为事实证明，将任务分解为多个步骤可以提高 LLM 的准确性 [21, 53, 55]。不过，值得注意的是，随着 LLM 日新月异的发展，一步式流程可能已经可行。此外，虽然我们的分类法现在可以指导 LLM 生成断言，但未来的 LLM 可能会通过人类反馈的强化学习来隐式地学习这些类别[12]。不过，了解与候选断言相关的基于分类学的类别可能有助于过滤它们 Designing LLM Chains by Adapting Techniques from Crowdsourcing Workflows. arXiv preprint arXiv:2312.11681 (2023) Chain-of-thought prompting elicits reasoning in large language models. Advances in Neural Information Processing Systems 任务分解为什么可以提高 LLM 的准确性？而且现在 ChatGPT 就是用的 Human Feedback RL 来增强的 Deep reinforcement learning from human preferences ","date":"2024-03-01","objectID":"/posts/spade-paper/:4:3","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#from-taxonomy-to-assertions"},{"categories":["Learned Data Systems"],"collections":null,"content":"LangChain Deployment 比较了早期版本和后期的区别，吹了一下自己生成的断言有多么好，涵盖了多少个领域，达到了 \u0026ldquo;perfect\u0026rdquo; 提到了一个总结文章的例子，具有 14 个 prompt 版本，对他 SPADE 后，可以有一些断言，必须要有一些关键词等等 但这些断言并不是正则匹配，而是字符串匹配，我觉得不太合理。 文中也提到说许多断言可能是不正确的，怎么知道断言是正确的呢？本文的实验可能也不够，于是采用了自动化 filtering ","date":"2024-03-01","objectID":"/posts/spade-paper/:4:4","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#langchain-deployment"},{"categories":["Learned Data Systems"],"collections":null,"content":"FILTERING CANDIDATE ASSERTIONS 在这里，我们解决了第 2.4 节中确定的冗余和不正确断言的问题，特别是在具有大量 prompt 版本的 pipeline 中。 过滤此候选集不仅可以提高部署断言以在生产中运行时的效率，而且还可以减少开发人员的认知开销 ","date":"2024-03-01","objectID":"/posts/spade-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#filtering-candidate-assertions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Definitions 将 𝑒𝑖 视为 LLM pipeline 在某些输入上的端到端执行（即运行）的示例。 令 𝐸 为所有此类示例运行的集合（该集合未预先提供，我们将很快处理）。 我们定义一个断言函数 𝑓 : 𝐸 → {0,1}，其中 1 表示成功，0 表示失败。 令 𝐹′ ={𝑓1, 𝑓2, . 。 ., 𝑓𝑘} 是一组 𝑘 断言。 当且仅当一个例子 𝑒𝑖 满足 𝐹\u0026rsquo; 中的所有断言时，该集合才认为它是成功的。 具体来说 一些数学定义，定义了 Coverage 和 False Failure Rate ","date":"2024-03-01","objectID":"/posts/spade-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#definitions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Coverage Problem Formulation 目标是找到最小的集合 minimal set of assertions 满足所有例子？ 找到一个 Coverage(𝐹′) ≥𝛼, FFR(𝐹′) ≤𝜏 找到限制，就变成了一个优化问题，集合覆盖问题 我们将此 ILP 的解决方案称为 spadecovspade_{cov}spadecov​。 简单地说，对于 𝜏 =0 和 𝛼 =1，这个问题是 NP-hard 的，通过简单地从集合覆盖归约，并且是 NP 问题，因为它可以用 ILP 形式表示 ","date":"2024-03-01","objectID":"/posts/spade-paper/:5:2","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#coverage-problem-formulation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Subsumption Problem Formulation 到目前为止，我们假设开发人员愿意提供一组全面的标记示例运行 𝐸′，在开发人员不愿意这样做的设置中，并且 𝐸′不包括 𝐸 中的所有故障类型，spade_cov 可能会 忽略 𝐹 中的有用断言，这些断言只能捕获 𝐸 \\𝐸′ 中的失败——如第 4 节中的经验所示。我们最初考虑使用主动学习 [6] 为每个断言采样更多的 LLM 响应，并使用弱监督来标记响应 [36 ]。 然而，这种方法对于最先进的 LLM 来说成本高昂，并且需要大量的手动工作来平衡每个断言的失败和成功示例，确保有意义的 FFR 并避免由于代表性不足的故障类型而排除断言。 对于这种设置，我们还引入了包含。 假设所有候选断言函数涵盖尽可能多的故障模式，我们的目标是选择 𝐹′ ⊆ 𝐹，使得 𝐹 \\𝐹′ 中的断言包含在 𝐹′ 中。 形式上，如果 𝑆 中函数的合取逻辑上意味着 𝑆 和 𝑓 中函数的合取，则一组函数 𝑆 包含某个函数 𝑓 。 太多定义了，可能主要是在解决这个集合覆盖吧 ","date":"2024-03-01","objectID":"/posts/spade-paper/:5:3","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#subsumption-problem-formulation"},{"categories":["Learned Data Systems"],"collections":null,"content":"EVALUATION 我们首先讨论 LLM pipelines 和数据集（即 𝐸′）； 然后，我们讨论方法和指标并展示我们的结果。 实验代码、数据集和 LLM 响应托管在 GitHub 上 ","date":"2024-03-01","objectID":"/posts/spade-paper/:6:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#evaluation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Pipeline and Dataset Descriptions 我们对 9 个 LLM pipeline 进行了评估，其中 8 个来自 LangChain Hub（LLM pipeline 的开源集合），以及 1 个专有 pipeline 。 六个 LangChain Hub pipeline 帮助开发了 prompt delta taxonomy（第 2.2 节），但在创建分类法后，从 spade 的 Streamlit 部署（第 2.4 节）添加了两条 pipeline 。 专有 pipeline 是 fashion pipeline，它为活动提供服装建议。 之所以包含此 pipeline ，是因为它使用了 LLM 培训中未包含的数据及其实际部署，展示了 Spade 的 real-world 性。 虽然我们使用真实的用户提示模板和历史记录（3 到 16 个提示版本之间），但我们构建了自己的一组示例提示-响应对和标签 (𝐸′) 进行测试。 LangChain Hub pipeline 的两个数据集来自 Kaggle，而其他数据集是使用 Chat GPT Pro（基于 GPT-4）综合生成的。 例如，对于使用 LLM 审查拉取请求的 codereviews pipeline ，我们要求 Chat GPT 生成涵盖各种编程语言、应用程序类型和差异大小的占位符值。 我们对 8 个 LangChain pipeline 的 LLM 回复进行了标记，以评估他们是否符合提示说明。 响应分为 GPT-3.5-Turbo 和 GPT-4。 对于 fashion pipeline ，标签是由开发人员在相应的启动时完成的。 表 3 提供了每个 LLM 流程和数据集的详细信息。 我们已经开源了 LangChain Hub 8 个 pipeline 的所有数据 ","date":"2024-03-01","objectID":"/posts/spade-paper/:6:1","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#pipeline-and-dataset-descriptions"},{"categories":["Learned Data Systems"],"collections":null,"content":"Method Comparison and Metrics 和之前一样，令 𝐸′ 为示例提示-响应对的数据集，以及响应是好（即 1）还是坏（即 0）的相应标签。 令 𝜏 为 FFR 阈值，𝐹 为 SPADE 第一步产生的候选断言集（第 2 节）。 如果候选函数 𝑓 导致某些示例 𝑒 出现运行时错误，我们表示 𝑓 (𝑒) =0（即失败）。 我们所有的代码都是用 Python 编写的，使用 PuLP Python 包来寻找 ILP 的解决方案。 我们使用默认的 PuLP 配置，它使用 CBC 求解器 [18]。 我们评估了 spade 的三个版本： • spadebasespade_basespadeb​ase 选择 𝐹 中的所有函数 𝑓，其中 FFR ({𝑓 }) ≤𝜏 • spade_cov 是第 3.2 节中定义的 ILP 的解 • spade_sub 是第 3.3.1 节中定义的 ILP 的解 让 𝐹′ 代表任何版本的 spade 所选择的断言集合。 我们测量四个指标： (1) 所选断言的分数（即 |𝐹′|/|𝐹|） (2) 排除的非包含函数的分数（即 |𝐺|/|𝐹|，其中 𝐺 ={𝑔 |𝑔 ε𝐹 \\𝐹′ 和 𝐹′ ̸=⇒ 𝑔}) (3) 错误失败率（定义 3.2） (4) 𝐸′ 的覆盖率（定义 3.1） 此外，spade_sub 成功的一个重要方面是包含的有效性 所有断言对之间的评估。 由于我们没有包含的基本事实，因此我们关注精度，计算为正确识别的包含对在 LLM 识别的所有包含对中的比例。 我们不评估召回率（GPT-4 是否识别了每一个可能的包含），因为为每个管道标记可能数万个断言对是不切实际的。 此外，精确度比召回率或准确性更重要，因为即使识别一些包含，spade_sub 也能用比 spadebasespade_basespadeb​ase 更少的选定断言来实现解决方案。 这里评估精确率，但却忽略召回率，感觉是论文不够全面的地方。 精确率：TP/ (TP + FP)，我觉得有故障中的真故障 / （我觉得有故障且真故障 + 我觉得有故障但没故障） 召回率：TP / (TP + FN), 我觉得有故障中的真故障 / （我觉得有故障且真故障 + 我觉得没故障但真故障） 能否理解为，精确率区分真假正确，召回率针对样本中有多少正确的是你给出来的。所以一般两者都要一起考虑？ ","date":"2024-03-01","objectID":"/posts/spade-paper/:6:2","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#method-comparison-and-metrics"},{"categories":["Learned Data Systems"],"collections":null,"content":"Results and Discussion 使用 GPT-4 评估所有 pipeline 的归并结果，平均精度为 0.82，如表 5 所示，证实了其有效性。 为简单起见，我们将所有管道的覆盖率和 FFR 阈值设置为相同（𝛼 =0.6，𝜏 =0.25）。 我们在表 4 中报告了三种方法的结果。例如，考虑 codereviews 管道，它使用 LLM 来审查任何代码存储库的拉取请求。 这里， spadebasespade_basespadeb​ase 选择 20 个断言， spadebasespade_basespadeb​ase 选择 2 个断言， spade_sub 选择 15 个断言。 通过选择更多函数，spade_sub 可确保包含所有未包含的函数。 所有三种方法都遵守 𝐸′ 覆盖约束，但 spadebasespade_basespadeb​ase 在 9 个管道中有 4 个违反了 FFR 约束。 平均而言，与 spadebasespade_basespadeb​ase 相比，spade_sub 选择的断言数量减少了约 14%，并且 FFR 显着降低，相对于 spadebasespade_basespadeb​ase 减少了约 21%。 spade_cov 平均排除了 44% 未包含在 𝐹′ 中的函数。 我们随后讨论不同 spade 实现之间的权衡 结果和例子都显示了比较高的 precision，而且用的是 GPT-4 其中 code review pipeline 的一些 assertion 会回去调用 LLM，询问是否包含代码修改的建议，review 是否精简，是否聚焦于该 PR 剩下的没仔细看，很多结果并不是很令人信服，尤其是对三个不同的 SPADE (base 选择所有的 f，cov 选择最小的集合，sub 是 Subsumption Constraints 限制的) 不同版本 SPADE 的结果，𝛼 =0.6 和 𝜏 =0.25。 勾号和 x 标记表示是否满足 𝛼 和 𝜏 约束。 每个条目都是该管道的候选断言总数的一部分（括号中是绝对数量）。 spade_cov 总体上选择最少的断言。 spade_sub 在优化包含时选择最少的断言 𝜶 and 𝝉 Threshold Sensitivity：Spade 中 ILP 求解器的解决方案的可行性取决于所选的 𝛼 和 𝜏 阈值。 如果找不到可行的解决方案，开发人员可能需要以二分搜索的方式调整这些值。 在我们的例子中，所有 9 个 LLM 管道都产生了 𝛼 = 0.6 和 𝜏 = 0.25 的可行解决方案。 然而，𝐸′ 的小尺寸使得 spade_cov 对 𝛼 特别敏感。 在管道中，我们观察到 1 到 5 个断言覆盖了 60% 的失败。 例如，spade_cov 只为电子邮件管道选择了一个断言 FFR Tradeoffs: 考虑到对于三个 LLM 管道，为 spade_base 和 spade_sub 选择的函数比例之间的差异小于 10%，人们可能想知道 spade_sub 的复杂性是否值得。 spade_sub 通常更可取，因为 spade_base 无法始终满足 FFR 阈值 𝜏。 我们观察到，随着提示版本的增加，断言的数量也会增加，从而对 spade_base 产生不利影响。 一组的最坏情况 FFR 是各个 FFR 的总和，如公式 (2) 所示。 因此，在存在大量独立断言的情况下，总 FFR 很可能会超过阈值。这个问题在 fashion 和 lecturesummaries 管道中很明显，尽管 67 和 32 个断言中的每一个都单独满足 FFR 约束，但 spade_base 的总 FFR 分别达到 0.88 和 0.53。 实际上，如果将 spade 部署在交互式系统中，其中 spade 可以实时观察每个 LLM 调用（例如，作为 OpenAI API 的包装器），则大量的提示版本进一步需要基于整体 FFR 过滤断言 。 这强调了对更复杂的 spade_cov 或 spade_sub 方法的需要 ","date":"2024-03-01","objectID":"/posts/spade-paper/:6:3","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#results-and-discussion"},{"categories":["Learned Data Systems"],"collections":null,"content":"Limitations and Future Work Improving Quality of LLMs: 虽然 LLM（封闭式和开源）正在迅速改进，并且我们没有明确研究 spade 的 prompt engineering strategies，但补充的研究想法是探索此类策略或微调小型开源模型以生成断言。 此外，我们提出将 subsumption 作为覆盖范围代理，但没有探索 prompt 工程甚至非 LLM 策略（例如，断言来源）来评估包含。 尽管 LLM 取得了进步，但 SPADE 的过滤阶段对于减少冗余和确保准确性仍然至关重要，特别是因为断言可能涉及 LLM。 Collecting Labeled Examples: 获取标记数据（𝐸′）很困难。 我们的大多数数据集的提示版本很少（只有提交给 LangChain Hub 的版本），但实际上，开发人员可能会对其提示进行数十或数百次迭代。 未来的工作可能涉及通过 LLM API 包装器进行被动示例收集或收集开发人员对断言的反馈。 对不同类型的断言进行优先级排序并在 spade 中将这些优先级形式化是另一个需要探索的领域。 此外，在有限的 𝐸′ 下评估 FFR 估计的准确性，并探索在缺乏大型标记数据集的情况下提高 FFR 准确性的方法（例如，通过预测驱动的推理 [2]），为未来的工作提出了一个有趣的领域 Supporting More Complex LLM Pipelines: 我们的研究重点是单提示 LLM pipeline，但更复杂的管道（例如涉及多个提示、外部资源和人工交互的管道）为每个管道组件自动生成断言提供了机会。 例如，在检索增强生成管道中[28]，甚至可以在生成 LLM 响应之前将断言应用于检索到的上下文。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:6:4","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#limitations-and-future-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"RELATED WORK Prompt Engineering: 对于非技术用户 [57] 和技术用户 [34, 47] 来说，Prompt 工程很困难，原因有几个：提示措辞 [4, 29] 或指令或上下文的顺序 [30] 的微小变化可能会显着影响输出。 此外，随着 LLM 在 API 下发生变化（即提示漂移），输出可能会在开发人员不知情的情况下发生变化 [9]。 帮助提示管理和实验的工具和论文不断出现，甚至使用 LLM 来编写提示 [3,11,54,55,59]。 此外，部署的提示引入了新的挑战，例如“用更少的标记平衡更多的上下文”和“争论提示输出”以满足用户定义的标准[35]。 我们的工作并不明确地专注于帮助开发人员创建更好的提示，但它可以通过推荐的断言间接支持开发人员改进提示。 ML and LLM Evaluation: 众所周知，评估和监控已部署的机器学习模型具有挑战性 [33, 45]。 在部署环境中评估 LLM 更具挑战性，因为 LLM 通常用于生成任务，其输出是自由形式的[13]。 一些 LLM 管道类型，例如使用检索增强生成管道的问答 [28]，可以使用标准化的自动化指标 [14, 39]，但其他类型则因未知指标和缺乏标记数据集而面临挑战 [8, 35, 56]。 通常，组织依靠人类评估者来评估 LLM 的输出[19,35,52]，但最近的研究表明 LLM 可以通过详细的“记分卡”进行有效的自我评估[7,26,58]。 然而，编写这些记分卡可能具有挑战性 [35]，从而激励自动生成的评估者 LLMs for Software Testing: LLM 越来越多地用于软件测试，主要用于生成单元测试和测试用例[27,40,48,50,51]。 研究探讨了 LLM 的激励策略、幻觉和不确定性如何影响代码或测试的准确性 [10,15,16,32]。 我们的工作是互补的，并利用 LLM 为 LLM 管道生成基于代码的断言 Testing in ML Pipelines: ML pipelines 在生产中很难管理。 许多有关机器学习测试的文献都是通过分析数据质量 [5, 22, 42, 43] 或来源 [31, 41] 来验证结构化数据。 ML 测试平台通常提供自动实验跟踪和防止过度拟合 [1, 38]，以及数据分布调试工具 [20]。 特定于模型的断言通常需要人类规范[25]，或者至少需要大量数据来训练学习断言[24]。 LLM 链或管道是一类新型 ML 管道，LLM 本身可以用很少的数据生成断言。 最近的一项研究强调了测试“copilot”类产品的 LLM 管道的难度：开发人员希望确保准确性，同时避免过度使用资源，例如运行数百个断言 [35]——这激发了我们的断言过滤方法。 ","date":"2024-03-01","objectID":"/posts/spade-paper/:7:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"CONCLUSION 我们的工作引入了自动生成断言以捕获 LLM 管道中的故障的新问题，以及包含两个组件的解决方案：首先，它 synthesizes candidate assertions； 然后，它会 filter 它们。 我们提出了断言合成的快速编辑分类法，通过与 LangChain 的集成和部署展示了其潜力。 我们将用于高精度覆盖故障的最佳断言集的选择表示为整数线性程序（ILP）。 我们提出了断言包含来涵盖数据稀缺场景中的故障，并将其纳入我们的 ILP 中。 我们的自动生成断言系统 Spade 在 9 个真实数据生成 LLM 管道上进行了评估。 我们已公开我们的代码和数据集以供进一步研究和分析 没有理解 SPADE base, cov, sub 之间的区别，只是选择的集合不同吗 The paper presents SPADE, which is a method to detect incorrect outputs from large language models (LLMs) by automatically synthesizing assertions when developing LLM pipelines (such as codereviews and lecturesummaries). SPADE analyses the prompt version histories and creating candidate assertion functions based on the prompt deltas. Then, SPADE selects a minimal set of assertions or a subset that satisfies the coverage and accuracy requirements, meaning that they can catch most of the errors without causing too many false failures. The paper also uses a pruning technique to remove redundant or contradictory assertions from the final set. Finally, The paper evaluates SPADE on 9 real-world LLM pipelines and shows that it can reduce the number of assertions by 14% and decrease false failures by 21% compared to simpler baselines. The paper proposes a novel formulation of synthesizing assertions for LLM pipelines, which takes into account the prompt version histories, the coverage and accuracy requirements, and the trade-off between assertion complexity and number. It also provides an open-source implementation of SPADE and deploy it on the Langchain. The SPADE can effectively reduce the number of assertions and false failures compared to simpler baselines, and that spade-generated assertions can help developers improve their LLM pipelines. Spade reduces the number of assertions by 14% and the number of false failures by 21% compared to simpler baselines. It evaluates SPADE on nine real-world LLM pipelines, and demonstrates its effectiveness in reducing the number of assertions, increasing the coverage and accuracy of assertions, and identifying bad LLM outputs. Like all the applications built on LLM, it relies on the quality of the language model, the ability of LLM correct classificationand the availability of prompt version histories. If the prompt is too large or the language model performs bad, the assertions (python code or string match) might not work. The paper does not compare SPADE with Reinforcement Learning from Human Feedback(ChatGPT is enhanced with RLHF to filter inaccurate text and harmful output). SPADE also ignored the very important metrics, recall rate, in the experiment. SPADE does not discuss the different effects brought by different assertion implementation methods. For example, most assertions are string matching and many of them might even not work, which may cause some assertions to fail and increase FFR. I would try to first compare SPADE with other existing methods for LLM quality control, such as RLHF. Then explore more appropriate assertion implementation methods instead of simple string matching, and discuss the impact of different assertions on FFR. And during the experiment, try to deeply explore the impact of different coverage rates on recall and precision. Finally, try to generalize SPADE to more language models, such as smaller models with less parameters, making the LLM pipeline construction cheaper ","date":"2024-03-01","objectID":"/posts/spade-paper/:8:0","tags":["Paper Reading"],"title":"Paper Reading: Spade Synthesizing Assertions for Large Language Model Pipelines","uri":"/posts/spade-paper/#conclusion"},{"categories":["Learned Data Systems"],"collections":null,"content":"SEED: Domain-Specific Data Curation With Large Language Models 使用大型语言模型的 领域特定 数据管理 ","date":"2024-03-01","objectID":"/posts/seed-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#seed-domain-specific-data-curation-with-large-language-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"ABSTRACT 准备分析数据的数据管理任务 Data Curation 对于将数据转换为可行的见解至关重要。但是，由于不同域中的应用程序的不同要求，通用的现成工具通常不足。结果，数据科学家通常必须开发针对数据集和任务量身定制的特定领域 domain-specific 解决方案，例如在足够数量的带注释的示例上编写特定于域的代码或培训机器学习模型。众所周知，这个过程很困难且耗时。我们提出了 SEED ，这是一种 LLM-as-compiler 方法，该方法自动通过大语言模型（LLMS）自动生成特定于域的数据策划解决方案。用户描述了一个任务，输入数据和预期的量后，SEED compiler 编译器就会生产由 LLM 生成的代码，小型模型和数据访问模块组成的可执行管道。SEED 使用这些生成的模块来处理大部分数据记录，并动态决定 LLM 何时应该介入直接处理一些单独的记录，可能使用数据访问模块从数据源检索相关信息，以协助 LLM 解决任务。为了验证这种新的、革命性的方法，我们在跨越 5 个数据管理任务的 9 个数据集上进行了实验。结果表明，SEED 生成的特定领域的解决方案显著优于它们的通用对应方案，通常接近使用数千个标记训练示例的手动管理解决方案的性能。此外，与在每个数据记录上使用 LLM 的解决方案相比，SEED 实现了最先进的或相当少的性能，同时显着减少了 LLM 调用的数量 ","date":"2024-03-01","objectID":"/posts/seed-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"INTRODUCTION 数据管理任务是 discover, extract, transform, clean, and integrate data，对各种各样的组织都至关重要。尽管数据管理界做出了巨大的努力，但许多来源仍然报告称，数据科学家仍然将 80% 以上的时间花在这些任务上。这样做的一个关键原因是，不同领域的应用程序有不同的需求，即使是针对单一的数据管理任务，也没有一个通用的解决方案。例如，对于数据提取任务，可以通过正则表达式有效地提取货币金额，例如搜索后面跟着以逗号和句号分隔的数字的美元符号，即$\\d[\\d|，|。而提取人名则需要一种完全不同的方法，比如在 Mr.或 Ms.等称呼附近搜索大写的单词。由于这样的案例，通用现成的工具很少足够。取而代之的是，数据科学家通常必须开发针对数据集和问题域量身定制的特定应用解决方案，例如特定领域的特定解决方案代码（如上面的正则表达式）或在大量带注释的示例中训练以执行这些类型的任务的机器学习模型。结果，为特定方案设计数据策展解决方案是耗时的，需要进行多轮需求生成，培训数据收集，模型/算法开发以及与数据科学家和域专家进行测试，并且很少可以从一个部署中重复使用到下一个。对于企业而言，这可能是非常昂贵的,例如，Citadel 雇用 50 多名数据管理专家来向其分析师提供高质量的清洁数据,每年使他们花费数千万。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"Our Approach: SEED 在这项工作中，我们提出了 SEED，一种 LLM-as-compiler 的方法，它给出任务的用户描述、输入数据和预期输出(作为 JSON 规范)，自动将该规范编译成专门针对手头数据和应用程序定制的特定领域数据管理解决方案。 关键见解是，LLM 具有令人印象深刻的生成代码能力，执行推理，理解数据的语义和编码常识的能力，将导致数据策划研究的范式转移，并可以在该数据策划方面构建数据策划解决方案 on the fly。实际上，先前的工作表明，LLM 可以在解决特定数据策展任务方面非常有效。与这些先前的工作不同，这些工作直接依赖于 LLM 来处理数据策展任务中的每个记录，SEED 旨在使用 LLMS 来生成不同数据策划任务的域特异性模块，其中一些可能涉及 LLMS 的直接调用和一些其中是 LLM 生成的，但一旦生产了 LLM。 LLM 真的能推理吗？ 关键的见解是，LLMs 具有令人印象深刻的生成代码、执行推理、理解数据语义和编码常识的能力，这将导致数据整理研究范式的转变，并使即时构建数据整理解决方案成为可能。事实上，先前的工作已经表明，LLM 在解决特定数据整理任务方面非常有效。与这些直接依赖 LLM 处理数据整理任务中每条记录的前期工作不同，SEED 的目标是利用 LLM 为不同的数据整理任务生成特定领域的模块，其中有些模块可能需要直接调用 LLM，有些模块由 LLM 生成，但生成后并不使用 LLM。 Simran Arora et al. 2023. Language Models Enable Simple Systems for Generating Structured Views of Heterogeneous Data Lakes. Proc. VLDB Endow. 17, 2 (2023), 92–105 看上去这非常依赖 LLM 的性能 具体来说，SEED 编译器会生成一个执行管道 由代码、小型模型和数据访问模块以及 和数据访问模块组成的执行流水线。记录的直接调用。在这个执行流水线中，模块以各种方式使用 LLM。方式使用 LLM。例如，代码或小型模型模块由 例如，由 LLM 合成代码或小型模型模块，以提供特定领域的解决方案（如用于提取货币金额的正则表达式）。表达式）。如果这些模块 如果这些模块对某些记录的结果没有信心，SEED 将把它们转发给 LLM 模块，由 LLM 模块对这些结果进行分析。该模块虽然成本高昂，但通常能 该模块虽然价格昂贵，但通常能够对数据项执行复杂的、类似于人类推理的任务。对于 对于每个请求，LLM 模块可能会进一步使用一个数据访问模块，该模块会从一个数据库中检索相关信息。模块，从数据库或其他用户提供的数据中检索相关信息 用户提供的数据中检索相关信息，以协助 LLM 完成任务。在此，SEED 利用 LLM 的推理能力来逐个确定 哪些额外信息和工具将有助于 解决特定任务 这样，SEED 就能利用 LLM 的合成 synthesis、推理 reasoning 和语义理解能力以及编码的常识来构建特定领域的解决方案。理想情况下，用户无需手动编码模块或注释大量训练示例。此外，与之前将 LLM 用于数据整理任务的工作不同，SEED 不需要在每条数据记录上都调用昂贵的 LLM，因为在处理大型数据集时，LLM 会面临可扩展性、效率和成本问题。SEED 分两步进行流水线编译。首先，由于不同的模块可能适合不同的应用，SEED 会解析用户提供的配置文件，生成一个数据整理计划，指定要启用的模块。正如第 2 章中进一步讨论的那样，对于不同的应用，SEED 会根据任务、应用的具体要求和数据的属性，生成不同的计划，这些计划会使用不同的模块组合。接下来，在给定数据整理计划的情况下，SEED 使用 LLM 构建相应的模块，如代码、小型模型或数据访问模块，并将这些生成的模块组装成可执行流水线，逐一处理数据记录。第 3、4 和 5 节将讨论这些模块的细节。 SEED 分两步进行流水线编译。首先，由于不同的模块可能适合不同的应用，SEED 会解析用户提供的配置文件，生成一个数据整理计划，指定要启用的模块。正如第 2 章中进一步讨论的那样，对于不同的应用，SEED 会根据任务、应用的具体要求和数据的属性，生成不同的计划，这些计划会使用不同的模块组合。 接下来，在给定数据整理计划的情况下，SEED 使用 LLM 构建相应的模块，如代码、小型模型或数据访问模块，并将这些生成的模块组装成可执行流水线，逐一处理数据记录。第 3、4 和 5 节将讨论这些模块的细节。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:3:1","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#our-approach-seed"},{"categories":["Learned Data Systems"],"collections":null,"content":"Contributions 我们的主要贡献在于证明了 LLM 能够以一种全新的、革命性的方法来处理数据整理任务。与传统的数据整理工具不同，SEED 的目标不是提供现成的解决方案供用户应用于自己的数据集。相反，SEED 会根据给定数据集的属性以及用户的特定需求，为给定数据集即时编译实例优化解决方案。在此过程中，用户无需编写任何代码或执行提示工程。 为了验证这一想法，我们构建了 SEED 的初始版本。结果令人鼓舞：通过对构建模块的一些基本优化（我们将在下文介绍），SEED 能够为多个数据整理问题提供高效、有效的解决方案，证明了其在实践中的潜力。特别是，通过在 9 个数据集上的实验，我们发现 SEED 所生成的解决方案大大优于通用解决方案，通常接近于在数千个示例中训练出来的解决方案的性能，这些数据集涵盖了数据估算、数据提取、数据注释、实体解析和数据发现等各种数据整理任务。此外，与在每条数据记录上都使用 LLM 的解决方案相比，SEED 实现了最先进的或可与之媲美的一次性性能，同时大大减少了所需的 LLM 调用次数 我们的技术贡献主要集中在有效生成模块本身，包括 代码生成。我们提出了新颖的技术，以解决 LLM 在生成高质量和稳健代码以及避免人工提示工程和调试方面的局限性。为了支持具有复杂逻辑的场景，SEED 生成了一系列代码片段，这些代码片段可共同解决任务，因此比单段代码更强大。 模型生成。SEED 利用作为 annotators and teachers 的 LLM 生成小型模型。具体来说，我们利用 LLM 提供标签，从而避免了传统学习方法中的人工标注工作。我们设计的算法可以明智地选择要提交给 LLM 的记录，从而最大限度地减少查询 LLM 的频率，同时最大限度地提高所学小型模型的效率。 数据访问模块。我们提出了包括批量查询和迭代工具调用在内的优化方案，利用从数据库中提取的相关信息来增强 LLM，从而大大降低执行成本，同时提高准确性。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:3:2","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#contributions"},{"categories":["Learned Data Systems"],"collections":null,"content":"SEED OVERVIEW 下面我们将介绍 SEED 的操作。 User Configuration。SEED 通过一个简单的配置文件与用户交互。图 1 显示了实体解析任务的配置。给定一组属性为 \u0026ldquo;名称\u0026rdquo;、\u0026ldquo;制造商 \u0026ldquo;和 \u0026ldquo;价格 \u0026ldquo;的产品，用户需要确定两个产品是否指的是同一件商品。配置主要包括两部分：（1）用自然语言描述任务、输入和输出；（2）（可选）提供可用数据和工具。用户首先指定任务名称、输入和输出。任务名称可以是任意文本。每个输入还包括名称、数据类型和自然语言描述，例如，描述可以包含用户对属性的领域知识。用户以同样的方式定义输出。用户还可以选择提供更多的领域知识。这可以通过自然语言描述、供 SEED 使用的 API 形式的自定义工具或包含示例的数据文件（无论是否加注）来实现。 对于常见的数据整理任务，包括数据提取、数据发现、实体解析、数据估算和数据发现，SEED 提供内置配置模板。这些模板会自动填充配置文件中的所需字段，用户可根据需要对其进行调整。在上述实体解析任务中，模板会在输入字段中填充两个实体，并在输出字段中填充以下信息： \u0026ldquo;名称：is_same；类型：bool；描述：如果两个标题相同，则为 1： 如果两个标题相同，则为 1，否则为 0\u0026rdquo;。选择实体解析模板后，用户只需选择性地提供提示和一些示例 Compilation. 用户完成配置后，SEED 会将其编译成符合用户规范的功能 API（例如，在图 1 中的示例中，生成了一个函数 entity_resolution(entity1，entity2) →is_same）。该函数以数据记录对的格式接收数据，并输出一个布尔值，表示其中一对是否匹配。 SEED 分两步编译解决方案： (1) 制定数据整理计划，确定启用哪些模块 (2) 构建模块并将这些模块连接到可执行管道中。 (1) 制定数据管理计划。针对不同的任务和数据集，SEED 使用基于规则的方法来生成不同的计划，并使用不同的模块组合。 作为高级但昂贵的数据分析工具，LLM 模块被默认选择为轻量级代码和小型模型模块的备用模块。一些需要语义理解能力的复杂任务可能会单独使用 LLM 模块，例如，在数据发现中，推理一个表是否与用户的问题相关是 LLM 比简单方法更具优势的领域 代码模块适用于数据整理任务，在这些任务中，大多数情况都可以使用基于规则的方法有效解决，例如数据清理和数据提取。例如，在基于领域知识的数据清理中，用户通常会假定正常数据中存在一些有规律的模式，并将错误识别为违反这些模式的数据记录。正如我们在数据估算任务（第 6.1 节）中的实验所证实的那样，由 LLM 生成的小程序可以有效地实现这一逻辑 Small model modules 小型模型模块适用于可建模为预测或生成机器学习任务的数据整理任务。例如，实体解析可视为一个分类问题，它将一对对象分为匹配或不匹配，而数据估算可视为一个生成问题，它根据上下文生成缺失的属性。这些问题可以通过机器学习模型很好地解决，该模型由 LLM 生成的示例或用户提供的示例训练而成 Data access modules 数据访问模块总是与 LLM 模块耦合在一起。它们对于需要大量数据访问的任务特别有用。例如，数据发现需要搜索企业数据，以发现与用户问题最相关的数据。SEED 提供了一套专门用于数据库操作的预定义工具。此外，它还允许使用其他模块（代码或小型模型）作为工具，以及用户提供的其他工具。要在数据访问模块中注册工具，用户只需告知 SEED 工具的 API 并提供简要说明即可。 除了任务之外，在制定数据整理计划时，SEED 还会考虑特定领域的要求或数据的独特属性。例如，如果用户在描述他们的实体解析任务时提出了一些可以用规则轻松表达的特定要求，如产品表中的两条记录如果价格不同就不属于同一项目，那么 SEED 就会生成一个首先使用代码模块而不是小模型的计划。这再次利用了 LLM 的语义理解能力。也就是说，SEED 使用 LLM 来增强基于规则的方法。它读取任务描述，评估基于规则的方法生成的计划，并在必要时对其进行修改 (2) 构建可执行管道。生成数据整理计划后，SEED 会使用 LLM 来帮助构建计划中的模块，并将它们链接到可执行管道中，从而动态协调模块之间的关系。对于使用代码模块的计划，SEED 首先使用预定义模板将配置文件转换为任务描述提示。然后将该提示发送到 LLM，生成一系列自动评估和完善的代码片段（第 3 章）。这样就生成了一个可调用的方法，如实体解决代码（entity_resolution_code()）。同样，对于具有小型模型的计划，SEED 会生成一个提示模板，请求 LLM 对数据进行注释，然后生成一个可调用的方法，例如 entity_resolution_model()，该方法与在 LLM 注释数据上训练的小型模型相对应（第 4 节）。对于启用了数据访问模块的计划，SEED 会生成一个提示，将 LLM 连接到与给定任务相关的数据访问接口，并为 LLM 选择性地使用这些接口提取数据编制一个迭代过程（第 5.2 节）。这也会生成一个可调用的方法，如 entity_resolution_da()。最终，SEED 将这些内部方法调用连接起来，生成一个最终方法，例如，entity_resolution(entity1, entity2) → is_same 请注意，虽然这不是本文的重点，但我们在 SEED 中花费了大量精力，精心设计了动态提示组成模板，这些模板在这些类型的数据整理任务中普遍表现良好，从而消除了用户对提示工程的需求。有关提示模板，请参阅扩展版附录 D [36]。 模块执行。生成的方法根据数据整理计划依次执行模块，逐一处理每条数据记录。每个模块决定是直接返回结果还是将结果传递给下一个模块。对于代码模块，当遇到不确定的情况时，生成的代码片段会被明确指示放弃（第 3.1 节），这些代码片段将继续流向下一个模块，而小模型模块在产生置信度较低的预测结果时会放弃（第 4.2 节）。数据访问模块从数据中检索相关信息，并将其传递给 LLM，因为它始终与 LLM 模块耦合。接下来，我们将介绍构建单个代码模块、小型模型模块和数据访问模块的技术。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#seed-overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"CODE GENERATION 尽管使用 LLM 生成代码吸引了大量关注，但它也面临着挑战。LLM 通常难以生成高质量和健壮的代码 。同时，提示工程和手动验证生成代码的正确性需要投入大量时间，这使得 LLM 生成代码的效率较低。此外，LLM 通常是在小型代码生成任务中进行评估，在生成大型应用或需要复杂逻辑的程序时仍有困难。不过，一些复杂的数据整理任务可能需要实现与多个组合规则相对应的复杂逻辑。例如，从非结构化数据中提取多种类型目标信息的数据提取（如从 HTML 文件中抓取数据记录）很难通过单个代码片段完成 SEED 使用两种技术来应对上述挑战，它们共同提高了生成代码的准确性和稳健性，同时最大限度地减少了人力。首先，建议代码生成与验证将代码生成和调试作为一个统一的交互过程，充分利用了 LLM 的推理能力。这一过程是自动的，同时保证了代码的质量。其次，代码集合与演化可以产生一组相互补偿的代码片段。这样，代码组合就能有效处理程序中需要复杂逻辑的情况。我们设计了一种进化方法来迭代优化代码集合。接下来，我们将详细阐述这两种技术。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#code-generation"},{"categories":["Learned Data Systems"],"collections":null,"content":"Code Generation with Advising and Validation ","date":"2024-03-01","objectID":"/posts/seed-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#code-generation-with-advising-and-validation"},{"categories":["Learned Data Systems"],"collections":null,"content":"\u0026hellip; 这篇文章的重点在我看来是他们的 Cache 设计，结合 Local LM 显著降低对 LLM/GPT-4 的调用，同时保证了精度，但是论文写得有点多而且不容易读，切入的角度是 data curation 任务，没有接触过就暂时不读了。代码生成部分也应该有意思，回头研究下他们的代码。 ","date":"2024-03-01","objectID":"/posts/seed-paper/:5:2","tags":["Paper Reading"],"title":"Paper Reading: SEED Domain-Specific Data Curation With Large Language Models","uri":"/posts/seed-paper/#heading"},{"categories":["Database"],"collections":null,"content":"AnalyticDB-V: A Hybrid Analytical Engine Towards Query Fusion for Structured and Unstructured Data ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#analyticdb-v-a-hybrid-analytical-engine-towards-query-fusion-for-structured-and-unstructured-data"},{"categories":["Database"],"collections":null,"content":"ABSTRACT 随着非结构化数据的爆炸性增长（例如图像，视频和音频），非结构化数据分析在真实世界应用的丰富脉络中广泛存在。许多数据库系统开始合并非结构化数据分析以满足此类需求。但是，在大多数系统中，对非结构化和结构化数据的查询通常被视为 disjoint tasks，在大多数系统中，混合查询（即涉及两种数据类型）尚未得到充分支持。 在本文中，我们在阿里巴巴（AnalyticDB-V（ADBV））提出了一种混合分析引擎，以满足这种新兴需求。ADBV 提供一个接口，该接口通过将非结构化数据转换为高维 vectors，以使用 SQL 语义来表达混合查询。ADBVA 采用 lambda 框架并利用了近似最近的邻居搜索（ANN）技术的优点来支持混合数据分析。此外，提出了一种新颖的 ANNS 算法来提高代表大量非结构化数据的大型向量的准确性。所有 ANNS 算法均在 ADBV 中作为物理运营商实施，同时提出了基于成本的优化技术，以确定有效的执行计划。对公共和内部数据集的实验结果表明，ADBV 及其有效性所取得的出色性能。ADBVHA 已成功部署在阿里巴巴云上，为各种现实世界应用提供了混合查询处理服务。 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#abstract"},{"categories":["Database"],"collections":null,"content":"INTRODUCTION 由于智能手机，监视设备和社交媒体的流行，每天都会生成大量的非结构化数据，例如图像，视频和音频。例如，在 2019 年单身日的全球购物节期间，高达 500％的非结构化数据被纳入阿里巴巴的核心存储系统。为了促进对非结构化数据的分析，通常利用基于内容的检索系统。在这些系统中，首先将每个非结构化数据（例如，图像）转换为高维特征向量，然后在这些向量上进行后续检索。这种向量检索在各种领域都广泛，例如面部识别，人/车辆重新识别，推荐和语音识别识别。在阿里巴巴，我们还在生产系统中采用这种方法。 尽管基于内容的检索系统支持未经检测的数据分析，但在许多情况下，由于各种原因，都应 jointly queried 非结构化和结构化数据（我们称它们为混合查询）。首先，对非结构化数据的查询可能不足以描述所需的对象，其中混合查询有助于证明其表现力。例如，在淘宝（Taobao）等电子商务平台上，一个潜在的客户可以寻找价格（不到 100 美元），发货（free-shipping），评级（超过 4.5）和样式的连衣裙（视觉上类似电影明星穿着的衣服）。其次，最先进的特征矢量提取算法的准确性远非令人满意，尤其是在大型数据集上，混合查询有助于提高准确性。对于检查，当图像数量从 0.64 milion 缩放到 1200 万时，false-negative 识别率增加了 40 倍。因此，对结构化属性（例如性别，年龄，图像捕获的位置，时间戳）对矢量搜索空间缩小并有效提高准确性可以施加约束。总而言之，混合查询对大量新兴应用程序具有很高的价值。 但是，大多数现有系统不提供混合查询的 native support。开发人员必须依靠两个隔离引擎来进行混合查询处理：矢量相似性搜索引擎用于非结构化数据和用于结构化数据的数据库系统。这种做法具有固有的局限性。首先，我们必须在两个系统上实现额外的逻辑和 post-processing 后期处理步骤，以确保数据一致性和查询正确性。其次，混合查询不能联合优化，因为子查询是在两个引擎上独立执行的。 为了应对这一挑战，我们在 Alibaba Cloud 的 OLAP System AnalyticDB（ADB）内设计和实施了一个新的分析引擎，称为 AnalyticDB-V（ADBV），该引擎在 Alibaba Cloud 上进行了管理，该引擎可以管理大量功能向量和结构化数据并且本机支持混合查询。在系统的设计和开发过程中，我们遇到并解决了几个重大的挑战： 高维向量的实时管理。从非结构化数据中提取的特征向量通常具有极高的维数。例如，在阿里巴巴，非结构化数据的向量在许多场景中可以达到 500 多个维度，例如在线购物应用程序。此外，这些向量是实时生成的。对这些高维向量的实时管理(即 CRUD 操作)对于现有的数据库和向量搜索引擎来说是繁重的。一方面，只支持相似度搜索的在线数据库系统 (例如 Post-greSQL 和 MySQL) 仅适用于多达数十维的向量。另一方面，向量相似性搜索引擎（例如 faiss）采用 ANN（大约最近的邻居搜索）方法以离线方式处理和索引高维向量，这些方式无法处理实时更新 混合查询优化。混合查询为 joint execution and optimization 提供了新的机会，并考虑了特征向量和结构化属性。但是，混合查询优化本质上比现有优化更为复杂。支持 TOP-K 运营的经典优化器不必考虑准确性问题，即所有查询计划都会带来相同的结果。但是，对于混合查询，ANN（在向量上）返回近似结果，以避免进行过度搜索，因此 Top-K 操作的准确性随 ANN 方法和参数设置的选择而变化。要平衡近似结果的质量和查询处理速度，还有一项非常重要的任务 高扩展性和并发性。在我们的许多生产环境中，向量以极大的规模进行管理。例如，在智能城市运输方案中，我们必须每天管理超过 117 亿道路或车辆快照，每天有 1 亿个新插入的记录。此外，至少需要每秒处理 5,000 个查询，其中 90％以上是新兴的查询。在 Freshippo 超市的另一种应用程序方案中，阿里巴巴集团的数字化零售商店，8 亿 512 维矢量存储在 ADBV 中。峰值负载为每秒 4000 个查询（QPS），高于 80％的查询是混合查询。分布式体系结构对于如此大规模的工作负载至关重要。此外，必须维持大量矢量和新摄入数据的快速索引 在 ADBV，我们解决了上述挑战，并做出了以下主要贡献： 用于混合查询的实时分析引擎。我们提出了一种分析引擎，该引擎本地支持融合结构化和非结构化的实时更新的数据。为了满足实时需求，我们采用了带有不同 ANN 索引的 Lambda 框架，用于流层和批处理层。流层中基于邻域的 ANNS 方法支持实时插入，但会消耗大量内存。批处理层中基于编码的 ANN 方法消耗的内存较少，但需要在 construction 之前离线训练。Lambda 框架可以定期合并从流层中新摄入的数据中，并将其从分批层中。 一种新的 ANNS 算法。为了提高代表大量非结构化数据的大规模矢量的准确性，提出了一种新的 ANNS 索引，称为 Voronoi Graph Product Quantization (VGPQ)。与 IVFPQ 相比，该算法可以有效地缩小矢量空间中的搜索 scope。根据实证研究，VGPQ 比 IVFPQ 更有效地对 massive vectors 进行快速索引和查询更有效 Accuracy-aware 的基于成本的混合查询优化。在 ADBV 中，ANNS 算法被包裹为 physical operators。因此，我们可以依靠查询优化器来有效地支持混合查询过程。关系数据库中的物理运算符始终返回确切的结果。但是，这些新引入的物理运营商可能不会严格遵循关系代数，而是输出近似结果。由于近似的性质，我们提供了新的操作规则，以达到最佳的查询效率。这些规则自然嵌入了 ADBV 的优化器中 在以下各节中，我们将提供 ADBV 的详细信息。§2 介绍了混合查询和 SQL dialects 的背景。§3，§4 和§5 介绍了我们的设计和实现，即总体系统设计，矢量处理（ANNS）算法和基于准确性的基于成本的混合查询优化。实验评估是在§6 中进行的。相关工作将在§7 中讨论，并在第 8 节中得出结论。 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#introduction"},{"categories":["Database"],"collections":null,"content":"BACKGROUND ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#background"},{"categories":["Database"],"collections":null,"content":"Motivation 为了准确地检索感兴趣的记录，典型的混合查询包括特征向量上的相似性约束(从非结构化数据中提取)和结构化数据上的值约束。考虑图 1 所示的示例，目标是检索在视觉上与查询图像相似的衣服，但是是红色的。传统地，这两种约束由两个独立的系统处理。开发人员需要查询使用矢量搜索引擎（例如 Faiss，Vearch）的 Top-K 图像，同时从数据库中检索颜色信息。之后，从两个系统获得的记录结合合并以得出最终结果。 这种实践引起了额外的发展工作和构成成本。可能会遇到少于 k 记录（从矢量搜索引擎返回）成功地传递用户查询中表达的颜色或样式约束，因此无法构造顶部的结果以满足用户中明确提及的量子要求询问。因此，开发人员必须仔细设置矢量搜索引擎要检索的记录数量。此外，执行效率具有优化的巨大潜力。例如，如果只有一小部分衣服满足结构化的结构（即红色），则首先使用结构化约束来检索记录，然后直接从检索到的设置中识别出最接近的特征向量，将会更有效。因此，ADBV 的动机是解决上述问题。 ADBV 允许用户将混合查询作为 SQL 语句表示，并在无需手动调整的情况下有效地执行它。请注意，非结构化和结构化数据都可以存储在一个表中。特别是，在插入阶段将非结构化数据转换为向量（通过特征提取功能），并存储在列中。 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#motivation"},{"categories":["Database"],"collections":null,"content":"SQL dialects ADBV 提供灵活且易于使用的 SQL 接口。开发人员可以轻松将其应用程序移植到最小的努力中 略 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#sql-dialects"},{"categories":["Database"],"collections":null,"content":"SYSTEM DESIGN ADBV 建立在阿里巴巴的 PB-scale OLAP 数据库系统 AnalyticDB 之上，该系统依赖于两个基本组件，即盘古，用于可靠和永久的分布式存储。伏羲，用于资源管理和计算作业调度。ADBV 增强了 AnalyticDB 的向量支持和混合查询功能。在本节中，我们介绍了改进矢量管理和混合查询处理的功能和有效性的关键系统设计 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#system-design"},{"categories":["Database"],"collections":null,"content":"Architecture overview 图 2 中介绍的 ADBV 的架构，主要由三种类型的节点组成：Coordinator 协调器，Write Node, and Read Node。 Coordinators 接受，解析，优化 SQL 语句，并将其派遣以读/写节点。ADBV 采用典型的读/写解耦合，它以低查询延迟和高写入量的一致性进行了交易。因此，写节点仅用于写请求（即插入，删除和更新），而读取节点则用于精选查询。新摄入的数据在承诺后将其冲入 盘古。ADBV 在存储层（第 3.2 节）中采用 LAMBDA 框架有效地管理 vectors：streaming 层涉及实时数据插入和修改；批处理层周期从压缩新插入的向量和重建 ANN 索引。此外，ADBV 将几个昂贵的谓词 push down 到存储层，以便充分利用这些节点的 计算 能力 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#architecture-overview"},{"categories":["Database"],"collections":null,"content":"Lambda framework 由于搜索整个矢量数据集的复杂性是不可接受的，因此必须建立索引来降低成本。 然而，在低维中广泛使用的传统索引技术，如 KD-tree 和 ball-tree ，对于深度学习模型生成的高维向量来说效果不佳。 经验证明，此类解决方案对于高维向量表现出线性复杂性。 因此，提出了 HNSW（Hierarchical Navigable Small World）和 LSH（Locality-SensitiveHash）等算法，以近似的方式在向量上进行实时索引构建。 然而，现有的算法要么由于内存消耗大而无法处理大量数据，要么无法提供足够准确的结果。 以 HNSW 为例，它需要将索引数据和特征向量持久存储在内存中以避免磁盘 I/O，否则其性能将显着下降。 除了原始数据之外，每条记录还需要大约 400 bytes 的内存用于其索引。 我们采用 lambda 框架来解决支持实时插入的挑战。 在此框架下，ADBV 使用 HNSW 实时为新插入的向量（即增量数据）建立索引。 ADBV 根据建议的 VGPQ 算法（第 4.2 节）定期将增量数据与基线数据合并到全局索引（第 3.2.2 节）中，并丢弃 HNSW 索引。 图 3 描述了 lambda 框架，它由三层组成：批处理层、流处理层和服务层。 这些层共同处理每个传入的查询。 批处理层根据基线数据返回搜索结果（第 3.2.2 节中讨论）。 对于流层，它执行两项任务：处理数据修改（即 INSERT、DELETE 和 UPDATE），以及在增量数据上生成搜索结果。 对于 SELECT 语句，批处理层和流处理层的部分结果由协调器合并以得出最终结果。 服务层负责向批处理层和流处理层发出请求并将结果返回给客户端。 不同的层驻留在不同类型的节点中，即协调器中的服务层、读取节点中的批处理层以及读取节点和写入节点中的流层。 ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:5:2","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#lambda-framework"},{"categories":["Database"],"collections":null,"content":"Clustering-based partitioning ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:5:3","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#clustering-based-partitioning"},{"categories":["Database"],"collections":null,"content":"VECTOR PROCESSING ALGORITHMS ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:6:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#vector-processing-algorithms"},{"categories":["Database"],"collections":null,"content":"Vector query processing ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:6:1","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#vector-query-processing"},{"categories":["Database"],"collections":null,"content":"Voronoi graph product quantization ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:6:2","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#voronoi-graph-product-quantization"},{"categories":["Database"],"collections":null,"content":"Storage design for VGPQ ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:6:3","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#storage-design-for-vgpq"},{"categories":["Database"],"collections":null,"content":"HYBRID QUERY OPTIMIZATION ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:7:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#hybrid-query-optimization"},{"categories":["Database"],"collections":null,"content":"Hybrid query execution ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:7:1","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#hybrid-query-execution"},{"categories":["Database"],"collections":null,"content":"Cost model for optimization ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:7:2","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#cost-model-for-optimization"},{"categories":["Database"],"collections":null,"content":"Accuracy-aware hyperparameter tuning ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:7:3","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#accuracy-aware-hyperparameter-tuning"},{"categories":["Database"],"collections":null,"content":"EXPERIMENTS ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:0","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#experiments"},{"categories":["Database"],"collections":null,"content":"Experimental setup ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:1","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#experimental-setup"},{"categories":["Database"],"collections":null,"content":"VGPQ ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:2","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#vgpq"},{"categories":["Database"],"collections":null,"content":"Clustering-based partition pruning ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:3","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#clustering-based-partition-pruning"},{"categories":["Database"],"collections":null,"content":"Hybrid query optimization ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:4","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#hybrid-query-optimization-1"},{"categories":["Database"],"collections":null,"content":"Two-step solution vs. ADBV ","date":"2024-02-25","objectID":"/posts/analyticdb-paper/:8:5","tags":["Paper Reading"],"title":"Paper Reading: AnalyticDB-V","uri":"/posts/analyticdb-paper/#two-step-solution-vs-adbv"},{"categories":["Database"],"collections":null,"content":"Milvus：A Purpose-Built Vector Data Management System ","date":"2024-02-25","objectID":"/posts/milvus-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: Milvus A Purpose-Built Vector Data Management System","uri":"/posts/milvus-paper/#milvusa-purpose-built-vector-data-management-system"},{"categories":["Database"],"collections":null,"content":"ABSTRACT 最近，数据科学和人工智能应用中迫切需要管理高维向量数据。 非结构化数据和机器学习 (ML) 的激增推动了这一趋势，其中 ML 模型通常将非结构化数据转换为特征向量以进行数据分析，例如产品推荐。 现有的向量数据管理系统和算法有两个局限性：（1）在处理大规模动态向量数据时会出现严重的性能问题； (2)它们提供的功能有限，不能满足多功能应用的要求。 本文介绍了 Milvus，这是一个专门构建的数据管理系统，用于有效管理大规模向量数据。 Milvus 支持易于使用的应用程序接口（包括 SDK 和 RESTful API）； 针对具有现代 CPU 和 GPU 的异构计算平台进行优化； 实现超越简单向量相似性搜索的高级查询处理； 处理动态数据以实现快速更新，同时确保高效的查询处理； 并将数据分布到多个节点以实现可扩展性和可用性。 我们首先描述 Milvus 的设计和实现。 然后我们演示 Milvus 支持的实际用例。 特别是，我们在 Milvus 之上构建了一系列 10 个应用程序（例如图像/视频搜索、化学结构分析、COVID-19 数据集搜索、个性化推荐、生物多因素认证、智能问答）。 最后，我们使用广泛的系统对 Milvus 进行实验评估，包括两个开源系统（Veach 和 Microsoft SPTAG）和三个商业系统。 实验表明，Milvus 比竞争对手快两个数量级，同时提供更多功能。 现在 Milvus 已被全球数百家组织部署，也被认定为 LF AI \u0026amp; Data Foundation 的孵化阶段项目。 Milvus 开源于 https://github.com/milvus-io/milvus。 ","date":"2024-02-25","objectID":"/posts/milvus-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: Milvus A Purpose-Built Vector Data Management System","uri":"/posts/milvus-paper/#abstract"},{"categories":["Database"],"collections":null,"content":"INTRODUCTION 在 Zilliz，我们遇到了各种客户在许多数据科学和人工智能应用中管理大规模高维向量数据（维度从 10 到 1000 维）不断增长的需求。 这主要是由于两个趋势。 第一个是由于智能手机、物联网设备和社交媒体应用程序的普及，图像、视频、文本、医疗数据和住房数据等非结构化数据的爆炸式增长。 据 IDC 称，到 2025 年，80% 的数据将是非结构化的。 第二个趋势是机器学习的快速发展，可以有效地将非结构化数据转换为学习的特征向量以进行数据分析。 特别是，推荐系统中最近流行的方法称为向量嵌入，它将项目转换为特征向量（例如 item2vec 、word2vec 、doc2vec 、graph2vec ）并通过 提供推荐 寻找相似的向量。 例如，YouTube 将视频嵌入到向量中； Airbnb 用向量建模房屋 ； 生物科学家使用载体描述药物化合物的分子结构信息。 除此之外，图像和文本也自然地由向量表示。 不断增长的例子是怎么样的？什么情况下一个向量会不断增加维度 这些应用程序对设计可扩展的向量数据管理系统提出了独特的要求和挑战。 其中包括：（1）不仅需要支持大规模向量数据的快速查询处理，还需要支持动态向量数据的高效处理（例如插入和删除）。 例如，Youtube 每分钟上传 500 小时的用户生成视频，同时提供实时推荐 。（2）除了简单的向量相似性搜索之外，还需要提供高级查询处理，例如属性过滤和多向量查询处理。 这里的属性过滤是只搜索满足给定过滤条件的向量，这在电子商务应用中很有用，例如，找到与给定图像向量相似且成本低于 100 美元的 T 恤。 多向量查询处理目标适用于每个对象由多个向量描述的场景，例如，在许多计算机视觉应用中使用面部向量和姿势向量来分析人物。 现有的矢量数据管理工作主要集中于矢量相似度搜索，但由于性能较差（针对大规模和动态矢量数据）和功能有限（例如不支持属性过滤和多矢量查询），无法满足上述要求，以支持多功能数据科学和人工智能应用。 更具体地说，我们将现有的工作分为两类：算法和系统。 对于向量相似性搜索的算法工作及其开源实现库（以 Facebook Faiss 和 Microsoft SPTAG 为例），存在一些局限性。 (1) 它们是算法和库，而不是管理矢量数据的成熟系统。 他们不能很好地处理大量数据，因为他们假设所有数据和索引都存储在主内存中并且不能跨越多台机器。 (2)这些工作通常假设数据一旦被引入系统就是静态的，并且不能轻松地处理动态数据，同时确保快速的实时搜索。 (3) 它们不支持高级查询处理。 （4）这些工作没有针对 CPU 和 GPU 的异构计算架构进行优化 Milvus 主要的工作： full-fledged system that manages vector data, handle dynamic data while ensuring fast real-time searches, support advanced query processing, optimized for the heterogeneous computing architecture with CPUs and GPUs 对于矢量相似性搜索的系统，例如阿里巴巴 AnalyticDB-V 和阿里巴巴 PASE（PostgreSQL），它们遵循一刀切的方法通过添加一个名为“矢量列”的表列来扩展关系数据库以支持矢量数据 ”来存储向量。 然而，这些系统并不是专门用于管理矢量数据的，也不将矢量视为 first-class citizens。 (1) 优化器和存储引擎等传统数据库组件无法对向量进行微调优化，例如，查询优化器错过了充分利用 CPU 和 GPU 处理向量数据的重要机会。 (2)不支持多向量查询等高级查询处理 best leverage CPU and GPU for processing vector data, support advanced query processing such as multi-vector queries 另一个相关系统是 Vearch，它是为向量搜索而设计的。 但 Vearch 在大规模数据上效率不高。 实验（图 8 和图 15）表明，本文介绍的系统 Milvus 比 Vearch 快 6.4x ~ 47.0x。 此外，Veach 不支持多向量查询处理。 large scale data, multi-vector query processing 本文介绍了 Milvus，这是一种专门构建的数据管理系统，用于为数据科学和人工智能应用高效存储和搜索大规模矢量数据。 它是一个专门用于高维向量的系统，遵循 one-size-not-fits-all 的设计实践，与泛化关系数据库以支持向量不同。 Milvus 提供了许多应用程序接口（包括 Python/Java/Go/C++ 的 SDK 和 RESTful API），可以方便应用程序使用。 Milvus 针对具有现代 CPU 和 GPU（多个 GPU 设备）的异构计算架构进行了高度调优，以实现最佳效率。 它支持多种查询类型，例如具有各种相似性函数的向量相似性搜索、属性过滤和多向量查询处理。 它提供了不同类型的索引（例如，基于量化的索引和基于图的索引），并开发了一个可扩展的接口，可以轻松地将新索引合并到系统中。 Milvus 通过基于 LSM 的结构管理动态矢量数据（例如插入和删除），同时通过快照隔离提供一致的实时搜索。 Milvus 也是一个跨多个节点部署的分布式数据管理系统，以实现可扩展性和可用性。 表 1 突出显示了 Milvus 与其他系统之间的主要区别。 在实现方面，Milvus 构建于 Facebook Faiss 之上，后者是一个用于向量相似性搜索的开源 C++ 库。 但 Milvus 显着增强了 Faiss，提高了性能（例如第 3 节中针对异构计算平台的优化、第 2.3 节中高效支持动态数据管理以及第 5.3 节中的分布式查询处理）、增强的功能（例如属性过滤和多重查询）。 第 4 节中的矢量查询处理），以及更好的可用性（例如第 2.1 节中的应用程序接口），成为成熟的易于使用的矢量数据管理系统 产品影响： Milvus 已被全球数百个组织和机构采用，涉及图像处理、计算机视觉、自然语言处理、语音识别、推荐系统和药物发现等各个领域。 更重要的是，Milvus 于 2020 年 1 月被 LF AI \u0026amp; Data Foundation 接纳为孵化阶段项目。1 贡献。 本文做出以下贡献： • 系统设计和实现（第 2 节和第 5 节）：总体贡献是 Milvus 的设计和实现，这是一个专门构建的矢量数据管理系统，用于管理大规模动态矢量数据，以支持数据科学和人工智能应用 。 Milvus 开源于 https://github.com/milvus-io/milvus。 • 异构计算（第 3 部分）：我们针对具有现代 CPU 和 GPU 的异构硬件平台优化 Milvus，以实现快速查询处理。 对于面向 CPU 的设计，我们建议缓存感知和 SIMD 感知（例如 SSE、AVX、AVX2、AVX512）优化。 对于面向 GPU 的设计，我们设计了一种新的混合索引，充分利用了 CPU 和 GPU 的优点，并且我们还开发了一种新的调度策略来支持多个 GPU 设备。 • 高级查询处理（第 4 节）：除了简单的向量相似性搜索之外，我们还支持 Milvus 中的属性过滤和多向量查询处理。 特别是，我们设计了一种新的基于分区的属性过滤算法和两种用于多向量查询处理的算法（向量融合和迭代合并）。 • 新颖的应用程序（第 6 节）：我们描述由 Milvus 提供支持的新颖的应用程序。 特别是，我们在 Milvus 之上构建了一系列 10 个应用程序，以展示其广泛的适用性，包括图像搜索、视频搜索、化学结构分析、COVID-19 数据集搜索、个性化推荐、生物多因素认证、智能问答、图像-文本检索、跨模式行人搜索和食谱食物搜索 ","date":"2024-02-25","objectID":"/posts/milvus-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: Milvus A Purpose-Built Vector Data Management System","uri":"/posts/milvus-paper/#introduction"},{"categories":["Database"],"collections":null,"content":"SYSTEM DESIGN 下次再读 ","date":"2024-02-25","objectID":"/posts/milvus-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: Milvus A Purpose-Built Vector Data Management System","uri":"/posts/milvus-paper/#system-design"},{"categories":["Database"],"collections":null,"content":"VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity VBase: 通过 Relaxed Monotonicity (松弛单调性) 统一在线矢量相似性搜索和关系查询 ","date":"2024-02-17","objectID":"/posts/vbase-paper/:1:0","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#vbase-unifying-online-vector-similarity-search-and-relational-queries-via-relaxed-monotonicity"},{"categories":["Database"],"collections":null,"content":"Abstract 基于高维向量索引的近似相似度查询 approximate similarity queries已经成为许多关键在线服务的基础。对更复杂的向量查询的日益增长的需求要求将向量搜索系统与关系数据库集成在一起。然而，高维向量盘不表现单调性，而单调性是常规指标的一个重要性质。缺乏单调性迫使现有的向量系统依赖于 monotonicity-preserving tentative indices（为目标矢量的 TopK 近邻临时设置）来方便查询，由于难以预测最优 K，这将导致次优性能。本文介绍的 VBASE 系统能有效支持近似相似性搜索和关系运算符的复杂查询。VBASE 发现了一个共同属性 relaxed monotonicity 松弛单调性，从而将两个看似不兼容的系统统一起来。这一共同属性使 VBASE 能够规避仅 TopK 的限制，从而显著提高效率，同时证明它保留了基于 TopK 的语义。评估结果表明，在复杂的在线向量查询中，VBASE 的性能比最先进的向量系统高出三个数量级。VBASE 还能进行分析性相似性查询 analytical similarity queries，而以往的向量系统则无法做到这一点，而且在精确查询方面，VBASE 提高了 7000x speedup 的同时准确率达到 99.9%。 ","date":"2024-02-17","objectID":"/posts/vbase-paper/:2:0","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#abstract"},{"categories":["Database"],"collections":null,"content":"Introduction 深度学习（embedding）模型的最新进展是将几乎所有类型的数据（如图像、视频、文档）映射成高维向量。对高维向量的查询可以进行复杂的语义分析，这在以前即使不是不可能，也是很困难的，因此它们成为许多重要在线服务的基石，如搜索、电子商务和推荐系统。这些服务的 \u0026ldquo;在线\u0026rdquo; 性质要求矢量搜索以毫秒为单位完成。这种严格的延迟要求与精确搜索算法的固有高成本相冲突，迫使终端用户只能选择高维向量上的近似查询结果。随着新的矢量搜索应用不断涌现，矢量查询变得越来越复杂，通常涉及标量数据和矢量数据的混合搜索。这自然而然地推动了矢量搜索系统与关系数据库的整合。 矢量搜索和数据库系统使用索引的方式各不相同，而索引是加快查询速度的关键结构。传统索引（如 B-Tree）的一个重要特性是单调性。这一特性确保了查询可以在索引的引导下沿着某个方向单调地遍历数据集。这通常可以避免总数据扫描，从而提高查询执行效率。然而，由于维度诅咒（curse of dimensionality），对于高维向量索引来说，保持单调性的代价过于昂贵。取而代之的是，它们通常被组织成图或基于聚类的不规则结构，这种结构近似于单调性。遍历这样的向量索引并不能保证目标向量距离的严格单调性，但它能让系统有效地确定新的遍历何时不太可能到达比当前 K 个向量更接近目标的向量。因此，现代向量索引只支持近似 TopK，即近似查找 K 个近邻。TopK 查询会遍历向量索引足够多的步数，直到确定不可能找到比当前 K 个最近向量更近的邻居为止。 为了整合向量搜索和数据库系统，现有的向量数据库系统选择了严格的单调性。为了支持 TopK 以外的相似性查询，它们首先利用 TopK 收集 K 个向量，然后根据与目标向量的距离对 K 个向量进行排序，这样就建立了一个保持单调性的临时索引。因此，复杂的关系运算符可以按传统方式在临时索引上执行。考虑下面的向量搜索查询：\u0026ldquo;查找 X 个与图片最相似但低于某一价格的产品\u0026rdquo;。数据库规划者首先会在图像嵌入的向量属性上运行向量搜索运算符，以找到 K 个最近的图元，然后在价格属性上应用过滤运算符。但是，我们无法准确预测有多少图元会通过过滤运算符，这个数字可能远小于 K。因此，这种做法本身就难以确定能产生精确 X 结果的最佳 K。因此，它要么采用保守的大 K 设置，要么采用多个 K 的反复试验，这两种方法都会导致次优查询性能。 在本文中，我们介绍了 VBASE，这是一个新系统，能够高效地为复杂的在线查询提供服务，这些查询既涉及近似相似性搜索，也涉及标量和矢量数据集上的关系运算符。VBASE 将松弛单调性（Relaxed Monotonicity）作为从向量搜索系统和关系数据库这两个看似不同的系统中抽象出来的共同属性。松弛单调性要求索引遍历仅近似遵循单调性。我们观察到，最先进的矢量索引都以两阶段模式遵循松弛单调性属性：索引遍历首先近似定位离目标矢量最近的区域，然后以近似方式逐步远离目标区域。基于这一观察结果，我们正式定义了 \u0026ldquo;松弛单调性 \u0026ldquo;属性，它抽象了大多数现有矢量索引已经具备的核心索引遍历模式。松弛单调性可以看作是单调性的一种广义形式，因此也适用于传统的标量索引，如 B-Tree。因此，松弛单调性可以作为矢量搜索和数据库系统的共同基础 通过松弛单调性，VBASE 建立了一个统一的查询执行引擎，以支持对标量和矢量数据的各种查询，包括跨多个异构索引的查询 queries across multiple heterogeneous indices。VBASE 的统一引擎基于 Next 接口，而不是 TopK，以支持向量和标量索引的遍历。同时，该引擎允许从松弛单调性中推导出通用终止条件，以便及时停止查询的执行。 VBASE 的独特之处在于其基于松弛单调性的查询执行引擎可以证明其查询结果与最优 K 的 TopKonly 解决方案所产生的查询结果相当。这一强大的特性使 VBASE 能够规避 TopKonly 接口的限制，在保留基于 TopK 的查询语义的同时，显著提高效率。特别是，基于推导出的广义终止条件，VBASE 能够在索引遍历过程中检测 K，而无需预测 K。对于比 TopK 搜索更复杂的查询，VBASE 可以在结果准确性相似（即相同甚至更好的 recalls）的情况下，实现比最先进系统低达三个数量级的平均和尾部查询延迟。 此外，通过放松单调性，VBASE 甚至可以支持以往系统不支持的近似查询类型，并显示出卓越的查询性能和准确性。例如，VBASE 可以在 16 秒内完成 join-based vector query with 99.9%+ recall rate，比暴力表扫描快 7000 倍。 总之，本文做出了以下贡献： VBASE 确定并正式定义了 \u0026ldquo;松弛单调性 Relaxed Monotonicity\u0026quot;，这一特性首次揭示了精心设计的向量指数的核心，以及它们在实践中有效工作的原因。 VBASE 基于松弛单调性构建了一个统一数据库引擎，可利用矢量和标量数据索引进行功能强大的复杂查询。 我们证明了 VBASE 的统一引擎使用向量索引产生的结果与纯 TopK 方法相当，其 execution plan 比纯 TopK 方法更有效。 我们在 PostgreSQL 的基础上实现了 VBASE，只增加了 2000 行代码，并展示了对混合 100 万配方数据集上的八个复杂 SQL 查询的端到端评估，这些数据集既有矢量属性，也有标量属性。我们计划将 VBASE 开源，以满足人工智能时代新出现的重要矢量分析应用。 ","date":"2024-02-17","objectID":"/posts/vbase-paper/:3:0","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#introduction"},{"categories":["Database"],"collections":null,"content":"Background ","date":"2024-02-17","objectID":"/posts/vbase-paper/:4:0","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#background"},{"categories":["Database"],"collections":null,"content":"Emerging Online Vector Queries 在人工智能时代，矢量已成为一种重要的数据表示形式。深度学习已经促成了越来越多以向量为中心的在线应用，包括基于嵌入的检索、人脸识别、代码检索、问题解答、谷歌多重搜索、Facebook 近似重复检测等。最近，人工智能应用利用 ChatGPT 的检索插件，将其专有知识、个人文档和聊天上下文转换成向量。这样就能检索到有价格、类别、地点或时间限制的相关向量，从而在聊天中构建提示。 传统应用也受益于人工智能的向量。例如，搜索引擎将网络文档转化为词袋稀疏向量和深度学习填充密集向量，以提高搜索结果的相关性。而推荐系统则将图片、视频和商品描述转化为不同的向量。结合商品价格和类别等标量数据，这些向量可用于提升推荐体验。 所有这些都需要一个通用系统来高效运行复杂的矢量和标量查询。总之，这些向量应用场景可分为以下几类。 S1: 基于嵌入的检索、推荐和问题解答基本上都是在给定查询向量的情况下，在向量数据集中搜索 K 个最接近的向量。这类查询可以很自然地用单向量列上的 TopK 运算符来表达。 S2：单矢量 TopK 加标量属性过滤。在某些标量属性限制条件下，还需要找到 TopK 结果。Google Multisearch 就属于这一类。它允许用户在进行相似性图像搜索时提供额外的文本提示。 S3: 多列 TopK。有些矢量分析需要将不同矢量属性的多个 TopK 搜索结果进行交叉。例如，图像-食谱检索 是一种对（矢量化）成分关键词和菜肴图像样本的多模态数据属性进行的食谱搜索。最近的研究表明，多列 TopK 搜索可以提高问题解答等应用的结果质量。 S4: 向量相似性过滤器。相似性过滤是典型的向量分析应用场景。例如，人脸识别和 Facebook 的近乎完全重复检测都是从具有相似性阈值的数据集中搜索相似图像（给定图像）。为了支持这类应用，我们可以使用基于两张图片之间距离相似性的矢量过滤，即基于距离的范围查询。所有这些矢量查询类型都有严格的延迟要求（如毫秒）。遗憾的是，现有系统都无法全面有效地支持所有这些在线相似性查询（见表 1）。 ","date":"2024-02-17","objectID":"/posts/vbase-paper/:4:1","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#emerging-online-vector-queries"},{"categories":["Database"],"collections":null,"content":"The Division Between Databases and Vector Search Systems 虽然数据库可以通过关系代数来表达上述查询，但由于向量索引和传统数据库索引在语义上存在分歧，因此很难提供一个统一的系统来高效运行各类复杂的在线向量查询，如表 1 所示。 关系数据库。关系数据库是运行复杂查询的最主要工具之一。为了满足低延迟的 \u0026ldquo;在线\u0026rdquo; 要求，数据库广泛采用索引来加速查询执行，如 B 树、B+ 树等。这些索引具有单调性（monotonicity），这种特性允许查询沿某个方向单调地遍历索引，例如，按降序或升序遍历。 在新兴向量场景中，最重要的在线查询类型之一是 TopK 查询。而传统的数据库索引可以通过按升序或降序遍历索引并在收集到 K 个结果后立即终止查询来加快 TopK 的速度。这种优化适用于许多 TopK 变体，如 TopK + 过滤和多列 TopK 查询。然而，这种优化的有效性依赖于单调性假设，而高维向量索引并不遵循单调性假设。接下来我们将详细说明。 Approximate vector search 近似向量搜索：最近，人工智能模型的爆发产生了大量且不断增长的高维向量数据。为了获得更好的学习表示，一个向量可以有数百个维度。由于维度诅咒，没有任何解决方案能在亚线性时间内完成高维向量查询。为了应对 \u0026ldquo;在线\u0026rdquo; 场景，现代向量搜索系统采用近似方法，在保持相对较高的结果准确率（90% 以上的召回率）的同时，大幅降低查询延迟（毫秒级）。这些系统通常被称为近似近邻搜索（approximate nearest neighbor search ANNS）系统。 与关系数据库一样，矢量索引也被用来促进近似矢量搜索。有代表性的矢量索引或以分区的形式组织（基于聚类、哈希、高维树，或以邻域图）。在高维空间中定位一个向量的难度迫使这些向量索引对近似 TopK 进行优化。在 TopK 查询中，索引遍历由查询向量 q 引导，根据 q 与某些锚点（如群集中心点或采样图顶点）之间的距离，近似地向最近的邻居迂回前进。在遍历过程中，q 的方向可能会发生巨大变化，因此该过程不能保证在每一步遍历中都接近 q，而且向量索引遍历也不是单调的。 矢量索引缺乏单调性使得数据库系统无法直接使用矢量索引来加速查询，这也是数据库和矢量搜索系统之间产生分歧的主要原因。 基于 TopK 的解决方案消除 division：由于矢量索引是为 TopK 优化的，ANNS 系统只提供一个 TopK 接口。为了缩小数据库和矢量搜索系统之间的单调性差距，目前的做法是使用 ANNS TopK 接口，根据与目标矢量的距离排序的 K 个矢量创建暂定索引。这种暂定索引能保持单调性，从而在数据库中实现快速矢量查询处理。 然而，基于 TopK 的暂定索引解决方案并不令人满意。要预测暂定索引 K 的正确大小是很困难的，甚至是不可能的，因为在查询中，带有过滤约束的后续关系运算符可以收集到正确数量的结果。这种限制普遍适用于 TopK + 过滤查询、向量相似性过滤查询等。因此，基于 TopK 的暂定索引不可避免地会导致保守地选择一个非常大的 K，或者对不同大小的 K 进行反复试验，这都会产生过多的数据访问和计算量。 ","date":"2024-02-17","objectID":"/posts/vbase-paper/:4:2","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#the-division-between-databases-and-vector-search-systems"},{"categories":["Database"],"collections":null,"content":"VBASE Design ","date":"2024-02-17","objectID":"/posts/vbase-paper/:5:0","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#vbase-design"},{"categories":["Database"],"collections":null,"content":"Relaxed Monotonicity 与传统标量索引不同，高维向量索引是为近似 TopK 而设计的，并不遵循单调性。图 1 显示了 FAISS IVVFlat 和 HNSW 这两种常用矢量索引的 TopK 遍历模式。如图所示，矢量指数遍历不符合单调性，随着遍历的进行，到目标矢量的距离会出现不可预测的波动。由于这些矢量索引缺乏单调性，关系数据库无法直接使用它们来加速查询。 两阶段向量索引遍历模式：尽管如此，图 1 显示了两个矢量索引的两阶段索引遍历模式。在第一阶段，尽管矢量距离有较大波动，但索引遍历大致接近目标矢量区域。在第二阶段，指数遍历趋于稳定，并以近似方式稳步偏离目标矢量区域。这种两阶段遍历模式在我们研究的大多数向量指数中都很常见。我们认为，设计良好的矢量索引的本质是一种有效的数据结构，它隐含了这种遍历模式。因此，当 TopK 搜索查询进入第二阶段时，由于进一步遍历不可能识别出更多相似向量，因此可以提前终止。松弛单调性的正式定义。根据两阶段遍历模式，我们可以正式定义 \u0026ldquo;松弛单调性\u0026rdquo;（Relaxed Monotonicity），以确定向量索引遍历是否已进入第二阶段。该定义建立在向量 TopK 搜索内部执行方式的直观基础之上 图 2 展示了对查询向量 q 进行一般向量搜索的过程。图中虚线箭头表示与 q 相关的索引遍历路径。在高维空间中，q 的邻域由以 q 为中心的邻域球定义，如图 2 中的圆形所示。之后，索引遍历离开球体，进入第二阶段、 在这一阶段，查询可以终止。 图 2：松弛单调性直观示意图。向量查询 q 沿着遍历路径的前进方向发现了 q 与 E 个最近向量的邻域、 图 2 显示，要确定是否进入第二阶段，查询需要了解以 q 为中心的邻域球半径 Rq，以及查询的当前索引遍历位置（表示为遍历步长 s）与 q 之间的距离 Msq 是否大于 Rq，即是否超出了 q 的邻域。 形式上，Rq（q 邻域的半径）定义为： Rq = Max(To pE({Distance(q,v j )|j ∈[1,s −1]})), (1) 其中 To pE 表示 遍历过程中观察到的 q 的 E 个最近邻，假设到目前为止遍历已经到达步骤 s。 对于 K 个最近向量搜索查询，需要 E ≥K 才能产生足够的最终结果。 在图 2 中，圆圈内的 E 向量是迄今为止访问的所有 s 个向量中 q 的最近邻。 在索引遍历过程中，球体半径 Rq 在第 1 阶段会逐渐减小，并在第 2 阶段变得稳定。 给定 Rq 的定义，系统需要定义 Msq，即目标向量 q 与当前索引遍历位置之间的距离测量，表示为遍历步长 s。 然后使用 Msq 来判断遍历是否进入阶段 2，即离开邻居球体。 数学上，Msq 被定义为最近 w 步中遍历的所有向量到 q 的中值距离，即遍历窗口。 Msq = Median({Distance(q,vi)|i ∈[s −w +1,s]}), (2) 其中 Distance(q,vi) 表示 q 与向量 vi 之间的距离，在过去的遍历中 窗户。 请注意，我们使用中位数而不是均值来忽略遍历窗口中的任何离群向量，这些向量与 q 的距离比其他向量过大或过小。 例如图 2 所示遍历窗口中最左边和最右边位置的两个离群值向量。 看不懂 Summarization: The paper identifies a common property, relaxed monotonicity, to unify two seemingly incompatible systems: vector search systems and relational databases, and proposes VBase that can handle complex queries of both approximate similarity search and relational operators on high-dimensional vector data. Relaxed monotonicity allows VBase to circumvent the constraints of a TopK-only interface and achieve significantly higher efficiency, while provably preserving the semantics of TopK-based solutions. The paper also presents a PostgreSQL-based implementation of VBase and compares it with other open source systems. Contribution: The paper introduces the concept of relaxed monotonicity, a property that can unify vector similarity search and relational queries, and leverages it to perform early termination of vector search. VBase builds a unified database engine based on relaxed monotonicity and modifies the original topk vector query interface so that the vector index can continuously spit out results to downstream operators, it can be easily connected to the volcano model of traditional relational databases. VBase has the same accuracy as the pure TopK method, and the execution plan is more efficient The paper implements VBASE based on PostgreSQL, adding only 2,000 additional lines of code, and compares it with other open source systems. It shows that VBase achieves up to three orders-of-magnitude higher performance than state-of-the-art vector systems on complex online vector queries. Limitation Key concepts and algorithms lack relatively clear definitions and descriptions, and there may be some confusion. It does not provide a clear and formal definition of relaxed monotonicity, the cost estimation and query planning methods. The paper does not discuss the limitations and assumptions of the relaxed monotonicity property. For example, how does it cope with dynamic and evolving data and queries? Improve: Run a series of hybrid queries on VBase and AnalyticDB-V systems, and measure their performance and accuracy using various metrics. Analyze the results and identify the strengths and weaknesses of both systems since they both combine vector similarity search and relational queries. Provide more rigorous and formal definitions and proofs of the relaxed monotonicity, the cost estimation and query planning methods, and discuss their limitations and assumptions to help to clarify and validate the core concept of the paper. ","date":"2024-02-17","objectID":"/posts/vbase-paper/:5:1","tags":["Paper Reading"],"title":"Paper Reading: VBase: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity","uri":"/posts/vbase-paper/#relaxed-monotonicity"},{"categories":["Database"],"collections":null,"content":"第二章：数据模型与查询语言 数据模型可能是软件开发中最重要的部分了，因为它们的影响如此深远：不仅仅影响着软件的编写方式，而且影响着我们的 解题思路。 多数应用使用层层叠加的数据模型构建。 现实世界：采用对象或数据结构，以及操控那些数据结构的 API 来进行建模 存储数据结构，通用数据模型如 JSON 或 XML 文档、关系数据库中的表或图模型。 内存、磁盘或网络上的字节来表示 JSON / XML/ 关系 / 图数据。这类表示形式使数据有可能以各种方式来查询，搜索，操纵和处理。 硬件工程师已经想出了使用电流、光脉冲、磁场或者其他东西来表示字节的方法 一个复杂的应用程序可能会有更多的中间层次，比如基于 API 的 API，不过基本思想仍然是一样的：每个层都通过提供一个明确的数据模型来隐藏更低层次中的复杂性。这些抽象允许不同的人群有效地协作（例如数据库厂商的工程师和使用数据库的应用程序开发人员）。 选择一个适合的数据模型是非常重要的。 数据模型到底是什么？用来描述、组织、操作数据，对现实进行抽象？ 本章介绍用于数据存储和查询的通用数据模型（比较关系模型、文档模型、图数据模型）和查询语言，以及存储引擎和数据模型如何实现的 ","date":"2024-02-10","objectID":"/posts/ddia-2/:1:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 第二章","uri":"/posts/ddia-2/#第二章数据模型与查询语言"},{"categories":["Database"],"collections":null,"content":"关系模型与文档模型 现在最著名的数据模型可能是 SQL。它基于 Edgar Codd 在 1970 年提出的关系模型：数据被组织成 关系（SQL 中称作 表），其中每个关系是 元组（SQL 中称作 行） 的无序集合。 关系模型曾是一个理论性的提议，当时很多人都怀疑是否能够有效实现它。然而到了 20 世纪 80 年代中期，关系数据库管理系统（RDBMSes）和 SQL 已成为大多数人们存储和查询某些常规结构的数据的首选工具。 关系数据库起源于·，在 20 世纪 60 年代和 70 年代用大型计算机来执行。从今天的角度来看，那些用例显得很平常：典型的 事务处理（将销售或银行交易，航空公司预订，库存管理信息记录在库）和 批处理（客户发票，工资单，报告） 当时的其他数据库迫使应用程序开发人员必须考虑数据库内部的数据表示形式。关系模型致力于将上述实现细节隐藏在更简洁的接口之后 多年来，在数据存储和查询方面存在着许多相互竞争的方法。在 20 世纪 70 年代和 80 年代初，网状模型（network model）和层次模型（hierarchical model）曾是主要的选择，但关系模型（relational model）随后占据了主导地位。对象数据库在 20 世纪 80 年代末和 90 年代初来了又去。XML 数据库在二十一世纪初出现，但只有小众采用过。关系模型的每个竞争者都在其时代产生了大量的炒作，但从来没有持续。 随着电脑越来越强大和互联，它们开始用于日益多样化的目的。关系数据库非常成功地被推广到业务数据处理的原始范围之外更为广泛的用例上。你今天在网上看到的大部分内容依旧是由关系数据库来提供支持，无论是在线发布，讨论，社交网络，电子商务，游戏，软件即服务生产力应用程序等等内容。 ","date":"2024-02-10","objectID":"/posts/ddia-2/:2:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 第二章","uri":"/posts/ddia-2/#关系模型与文档模型"},{"categories":["Database"],"collections":null,"content":"NoSQL 的诞生 现在 - 2010 年代，NoSQL 开始了最新一轮尝试，试图推翻关系模型的统治地位。“NoSQL” 这个名字让人遗憾，因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的 Twitter 标签，用在 2009 年一个关于分布式，非关系数据库上的开源聚会上。无论如何，这个术语触动了某些神经，并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与 #NoSQL 标签相关联，并且 NoSQL 被追溯性地重新解释为 不仅是 SQL（Not Only SQL） 采用 NoSQL 数据库的背后有几个驱动因素，其中包括： 需要比关系数据库更好的可伸缩性，包括非常大的数据集或非常高的写入吞吐量 相比商业数据库产品，免费和开源软件更受偏爱 关系模型不能很好地支持一些特殊的查询操作 受挫于关系模型的限制性，渴望一种更具多动态性与表现力的数据模型 不同的应用程序有不同的需求，一个用例的最佳技术选择可能不同于另一个用例的最佳技术选择。因此，在可预见的未来，关系数据库似乎可能会继续与各种非关系数据库一起使用 - 这种想法有时也被称为 混合持久化（polyglot persistence）。 ","date":"2024-02-10","objectID":"/posts/ddia-2/:3:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 第二章","uri":"/posts/ddia-2/#nosql-的诞生"},{"categories":["Database"],"collections":null,"content":"对象关系不匹配 目前大多数应用程序开发都使用面向对象的编程语言来开发，这导致了对 SQL 数据模型的普遍批评：如果数据存储在关系表中，那么需要一个笨拙的转换层，处于应用程序代码中的对象和表，行，列的数据库模型之间。模型之间的不连贯有时被称为 阻抗不匹配（impedance mismatch）。 像 ActiveRecord 和 Hibernate 这样的 对象关系映射（ORM object-relational mapping） 框架可以减少这个转换层所需的样板代码的数量，但是它们不能完全隐藏这两个模型之间的差异。 ORM 框架和手写 SQL 应该怎么选择？ 例如，图 2-1 展示了如何在关系模式中表示简历（一个 LinkedIn 简介）。整个简介可以通过一个唯一的标识符 user_id 来标识。像 first_name 和 last_name 这样的字段每个用户只出现一次，所以可以在 User 表上将其建模为列。但是，大多数人在职业生涯中拥有多于一份的工作，人们可能有不同样的教育阶段和任意数量的联系信息。从用户到这些项目之间存在一对多的关系，可以用多种方式来表示： 传统 SQL 模型（SQL：1999 之前）中，最常见的规范化表示形式是将职位，教育和联系信息放在单独的表中，对 User 表提供外键引用 后续的 SQL 标准增加了对结构化数据类型和 XML 数据的支持；这允许将多值数据存储在单行内，并支持在这些文档内查询和索引。这些功能在 Oracle，IBM DB2，MS SQL Server 和 PostgreSQL 中都有不同程度的支持。JSON 数据类型也得到多个数据库的支持，包括 IBM DB2，MySQL 和 PostgreSQL。 第三种选择是将职业，教育和联系信息编码为 JSON 或 XML 文档，将其存储在数据库的文本列中，并让应用程序解析其结构和内容。这种配置下，通常不能使用数据库来查询该编码列中的值。 对于一个像简历这样自包含文档的数据结构而言，JSON 表示是非常合适的，JSON 比 XML 更简单。面向文档的数据库（如 MongoDB，RethinkDB，CouchDB 和 Espresso）支持这种数据模型。 有一些开发人员认为 JSON 模型减少了应用程序代码和存储层之间的阻抗不匹配。不过，正如我们将在 第四章 中看到的那样，JSON 作为数据编码格式也存在问题。 JSON 表示比 图 2-1 中的多表模式具有更好的 局部性（locality）。如果在前面的关系型示例中获取简介，那需要执行多个查询（通过 user_id 查询每个表），或者在 User 表与其下属表之间混乱地执行多路连接。而在 JSON 表示中，所有相关信息都在同一个地方，一个查询就足够了。 从用户简介文件到用户职位，教育历史和联系信息，这种一对多关系隐含了数据中的一个树状结构，而 JSON 表示使得这个树状结构变得明确 ","date":"2024-02-10","objectID":"/posts/ddia-2/:4:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 第二章","uri":"/posts/ddia-2/#对象关系不匹配"},{"categories":["Database"],"collections":null,"content":"多对一和多对多的关系 region_id 和 industry_id 是以 ID，而不是纯字符串 “Greater Seattle Area” 和 “Philanthropy” 的形式给出的。为什么？ 如果用户界面用一个自由文本字段来输入区域和行业，那么将他们存储为纯文本字符串是合理的。另一方式是给出地理区域和行业的标准化的列表，并让用户从下拉列表或自动填充器中进行选择，其优势如下： 各个简介之间样式和拼写统一 避免歧义（例如，如果有几个同名的城市） 易于更新 —— 名称只存储在一个地方，如果需要更改（例如，由于政治事件而改变城市名称），很容易进行全面更新。 本地化支持 —— 当网站翻译成其他语言时，标准化的列表可以被本地化，使得地区和行业可以使用用户的语言来显示 更好的搜索 —— 例如，搜索华盛顿州的慈善家就会匹配这份简介，因为地区列表可以编码记录西雅图在华盛顿这一事实（从 “Greater Seattle Area” 这个字符串中看不出来） ","date":"2024-02-10","objectID":"/posts/ddia-2/:5:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 第二章","uri":"/posts/ddia-2/#多对一和多对多的关系"},{"categories":["Learned Data Systems"],"collections":null,"content":"DB-BERT: a databse tuning tool that reads the manual DB-BERT，一个读了手册的数据库调优工具 https://arxiv.org/pdf/2112.10925.pdf ","date":"2024-02-04","objectID":"/posts/llm-capability/:1:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#db-bert-a-databse-tuning-tool-that-reads-the-manual"},{"categories":["Learned Data Systems"],"collections":null,"content":"Abstract DB-BERT 是一种数据库调优工具，可利用通过对手册和其他相关文本文档进行自然语言分析而获得的信息。它利用文本来识别需要调整的数据库系统参数以及推荐的参数值。DB-BERT 应用预先训练好的大型语言模型（特别是 BERT 模型）进行文本分析。在初始训练阶段，它会微调模型权重，以便将自然语言提示转化为推荐设置。在运行时，DB-BERT 会学习汇总、调整和优先处理提示，以实现特定数据库系统和基准的最佳性能。这两个阶段都是迭代式的，并使用强化学习来指导选择要评估的调整设置（惩罚数据库系统拒绝接受的设置，同时奖励提高性能的设置）。在实验中，我们利用数百篇有关数据基础调整的文本文档作为 DB-BERT 的输入。我们将 DB-BERT 与不同的基准（TPC-C 和 TPC-H）、指标（吞吐量和运行时间）以及数据库系统（Postgres 和 MySQL）进行了比较。在所有情况下，DB-BERT 都能在所有比较方法中找到最佳参数设置。源码：https://itrummer.github.io/dbbert/ ","date":"2024-02-04","objectID":"/posts/llm-capability/:2:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#abstract"},{"categories":["Learned Data Systems"],"collections":null,"content":"Introduction Give me a user manual, and I’m happy for hours. \u0026ndash; Lennon Parham When all else fails, read the instructions. \u0026ndash; Anonymous 手册是很有用的，例如，在开始调优数据库管理系统（DBMS）之前，建议先阅读相关手册。但一直以来这些手册似乎只适用于人类，而无法被机器阅读，但是随着基于 Transformer 架构的，强大的预训练语言模型的出现，情况发生了改变。本文 的 DB-BERT 就基于 BERT 模型，通过阅读（自然语言分析）数据库手册和数百份关于调优的文本，来寻找更有希望的数据库系统参数进行调优。 近年来，为特定工作负载和性能指标寻找 DBMS 参数（也称 tuning knobs）的最佳参数值的问题受到了广泛关注。现在的 DBMS 有数百个参数，因此很难手动找到最佳设置。这就激发了自动参数调整的计算方法。目前的主流方法是机器学习，尤其是强化学习。在这种方法中，调优工具会根据特定设置的基准运行结果，有原则地选择要尝试的数据库管理系统参数值组合。然而，这种方法成本高昂（最近的工作在每次调整过程中都要进行数百次迭代），如果能以数据库专家的意见为指导，预先选择一小部分要调整的参数和合理的数值范围，效果会更好。我们的目标是用通过分析文本文档自动获取的信息来替代这些输入。我们将相应的问题称为自然语言处理（NLP）增强型数据库调整。 DB-BERT 从文本中提取调优提示，为特定参数推荐特定值。表 1 显示了带有来源的示例以及每个提取提示的相关正式表示。其中一些提示（第二个示例）推荐绝对值，而另一些提示（第一个和第三个示例）则推荐相对值。对于后者，将提示转化为具体的推荐值需要了解系统属性，如 RAM 的大小。一些提示（前两个示例）明确提到了参数，而另一些提示（最后一个示例）只是隐含地提到了参数。DB-BERT 可以利用表 1 中显示的所有提示。 对于给定的文本片段，DB-BERT 使用 BERT 变换器模型的微调版本来解决四项任务。首先，它决定文本片段是否包含提示。其次，它将提示转化为公式，如表 1 所示。这可能需要解决隐式参数引用和相对推荐的步骤。第三，DB-BERT 可能会决定在预先定义的范围内偏离建议值，而不是完全依赖提示。最后，考虑到多个来源的提示可能相互冲突，DB-BERT 会为提示选择权重，代表其相对重要性 DB-BERT 并不只依赖于调优提示。相反，它使用调优提示作为基于强化学习的调整方法的指导。在调优过程中，DB-BERT 会不断迭代，直到用户定义的优化时间预算用完为止。在每次迭代中，DB-BERT 都会选择一个或多个 DBMS 配置（即参数设置）进行尝试。DB-BERT 将这些运行过程中观察到的性能（在用户定义的基准上）转化为奖励值。 在未来的迭代中，该奖励值将使用双深度 Q-Networks 强化学习算法来指导配置的选择。为了应用该算法，我们将数据库调整表述为一个具有离散状态和行为的马尔可夫决策过程（MDP）。我们将对每个提示的处理表示为一系列决策，确定提示类型（如相对值与绝对值）以及提示权重。为了在这些决策中利用 NLP，我们将每个决策选项与文本标签相关联。这样，DB-BERT 就能使用 BERT Transformer 比较提示文本和决策标签。我们以独立于系统和基准的方式训练 DB-BERT，然后将其应用于特定的调整任务。原则上，我们可以使用人工标注的调整文档进行训练（为与标注一致的提示翻译分配高奖励）。然而，生成这样的数据需要专家知识，很难实现众包（相比之下，标注只需要常识性知识）。相反，我们利用数据库系统本身的（噪声）反馈。我们假设，调整提示如果翻译正确，往往会推荐不会显著降低性能的可接受值。因此，我们在训练 DB-BERT 时，会为翻译成可接受参数设置（即 DBMS 接受该设置）的提示分配奖励。另一方面，我们会对导致不可接受的参数设置（即 DBMS 拒绝接受该设置）或显著降低简单示例工作负载性能的设置进行惩罚。 训练的结果是一个模型（即经过微调的 BERT 模型的约 1.1 亿个参数的权重），可用作在其他基准上调整其他数据库系统的起点。我们只知道最近有一篇关于利用文本文档进行数据库调整的论文。作者提出了一种基于监督学习的简单方法。该方法通过人工标注了提示翻译的调整提示进行训练。与此相反，DB-BERT 使用未标记的文本作为输入。输入文本无需人工预处理。与提示翻译步骤相关的选择由人工提供的文本标签（共 15 个标签）进行注释。不过，这些标签并不依赖于场景，我们在所有实验中都使用了相同的标签（表 3 显示了 15 个标签中的 5 个）。这同样适用于下文介绍的所有其他调整参数。除了人工标注开销方面的差异，先前的方法纯粹基于输入文本，没有整合任何性能测量，因此无法根据特定基准或指标调整推荐。 在我们的实验中，我们与后一项工作以及无输入文本数据库调整的最新方法进行了比较。我们利用通过发布带有相关关键词的谷歌查询挖掘出的大型文档集作为 DB-BERT 的文本输入。我们考虑了不同的 benchmarks（TPC-C 和 TPC-H）、指标（吞吐量和延迟）和数据库系统（MySQL 和 Postgres）。实验清楚地表明，通过文本分析获得的信息对 DB-BERT 大有裨益。总之，我们的原创性科学贡献如下： 介绍了 DB-BERT，这是一个结合自然语言文本文档和基准评估的运行时间反馈来指导数据库调整的系统。 描述了 DB-BERT 用于提取、优先排序、翻译、汇总和评估调整提示的机制。 使用多种基准、指标和数据库系统对 DB-BERT 进行了实验性评估，并与 benchmarks 进行了比较。 本文的提示组织如下。第 2 节介绍了学习和 NLP 的必要背景。然后，在第 3 节中介绍我们的问题模型和术语。第 4 节概述 DB-BERT。然后，在第 5 节中，我们将介绍 DB-BERT 如何从文本文档中提取候选提示并对其进行优先排序。我们在第 6 节展示 DB-BERT 如何翻译单个提示，在第 7 节展示 DB-BERT 如何汇总和评估提示。在第 8 节中，我们报告了实验结果，最后以第 9 节作为结束。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:3:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#introduction"},{"categories":["Learned Data Systems"],"collections":null,"content":"Background and related work ","date":"2024-02-04","objectID":"/posts/llm-capability/:4:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#background-and-related-work"},{"categories":["Learned Data Systems"],"collections":null,"content":"Pre-Trained Language Models 略去 LLM 介绍 ","date":"2024-02-04","objectID":"/posts/llm-capability/:4:1","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#pre-trained-language-models"},{"categories":["Learned Data Systems"],"collections":null,"content":"Natural Language Query Interfaces 自然语言 Query Interfaces 是预训练模型在数据库中最流行的应用。在撰写本文时，相应的方法构成了文本到 SQL 翻译基准的最新技术，如 WikiSQL 或 SPIDER 。将文本翻译成查询的问题与从文本中提取调整提示的问题具有某些共同特点。在这两种情况下，文本都被翻译成形式化的表示。然而，文本到 SQL 方法通常是将单句翻译成单个 SQL 查询，而 DB-BERT 则是从多句文本段落中提取多个调整提示。此外，DB-BERT 还必须对从多个来源获得的相互冲突的提示进行汇总和优先排序（这是自然语言查询界面中没有出现过的子问题）。与之前大多数有关文本到 SQL 翻译的工作不同，DB-BERT 并不假定存在标注的训练样本 ","date":"2024-02-04","objectID":"/posts/llm-capability/:4:2","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#natural-language-query-interfaces"},{"categories":["Learned Data Systems"],"collections":null,"content":"Reinforcement Learning 略去强化学习介绍 具体来说，DB-BERT 使用了双深度 Q-Networks 算法。该算法通过深度学习来估计特定状态下的行动值，使用两个独立的模型来选择行动和评估行动。强化学习已被用于数据库领域的各种问题，包括调整问题（接下来将详细讨论）。与之前的工作不同，我们将强化学习与 NLP 结合起来，以找到有前景的参数设置。更广泛地说，我们的工作与之前利用文本进行强化学习的工作相联系，特别是之前关于指令跟踪的工作。不过，之前的工作并没有考虑性能调整，特别是数据库调整。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:4:3","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#reinforcement-learning"},{"categories":["Learned Data Systems"],"collections":null,"content":"Database Tuning 之前的工作通过监督学习训练 Transformer 模型来识别包含调整提示的句子。对于被归类为调整提示的句子，它会根据简单的启发式提取参数和数值。这种方法仅使用文本输入，但没有运行时反馈。它从文档集中提取固定的建议集，无法适应特定的工作负载和性能指标。另一方面，DB-BERT 仅将从文本中提取的提示作为起点。它支持更广泛的调整提示（如隐含提示），并且在训练过程中不需要注释调整提示。我们在表 2 中总结了其中的一些差异，并在第 8 节中对这两种方法进行了实验比较。如今，机器学习已成为各种数据库优化问题的首选方法，从查询优化到物理设计决策再到数据库系统参数调整。我们要解决的是后一个问题的扩展版本，通过自然语言文本文档来扩展输入内容。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:4:4","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#database-tuning"},{"categories":["Learned Data Systems"],"collections":null,"content":"Problem Model 我们调整数据库系统参数的配置。 定义 3.1（配置）。 每个数据库管理系统都与一组配置参数 P 相关联。用 V 表示可接受的参数值集。配置为每个参数分配一个有效值，并表示为一个函数 P ↦ V。等价地，我们将该函数表示为参数值对的集合 {⟨𝑝𝑖,𝑣𝑖⟩} for 𝑝𝑖∈ Pand 𝑣𝑖 ∈V of parameter-value pairs。配置中未引用的参数保持默认值。 我们的目标是找到优化性能的配置。传统上，我们使用以下问题模型。 定义 3.2（数据库调整）。数据库调整问题由一个元组⟨𝑏, P,V⟩ 描述。这里，𝑏 是一个基准，定义了一组查询（或事务工作量），以及需要优化的性能指标（如运行时间或吞吐量）。解决方案将从 V 中选择的用于调整的参数 P 赋值，并根据基准 𝑏 理想地优化性能。在本研究中，我们将讨论该问题模型的一个变体。 定义 3.3（NLP 增强调整）。一个 NLP 增强数据库调优实例由一个元组⟨𝑏,𝑇,𝑆⟩描述。这里，𝑏 是要优化的基准，𝑇 是包含调整提示的文本文档集合。我们的目标是利用通过自然语言分析从 𝑏 𝑇 中提取的调整提示，在考虑所有 DBMS 调整旋钮（更准确地说，我们当前的实现考虑了每个系统的所有整数、数值和布尔参数）的情况下，找到 𝑏 的最佳配置。𝑆 是一个包含数值系统属性（如内存大小或内核数量）的向量，用于将可能包含相对值建议的提示转化为具体值。我们不希望用户指定要调整的参数，也不希望用户建议参数的取值范围。我们依靠自然语言分析来确定相关参数和建议值。不过，本工作中介绍的方法假定可以访问数据库管理系统实例。通过该接口，我们可以验证提取的参数名称是否有效，以及参数类型是否在我们的范围之内（我们当前的实现考虑了整数、布尔和数值参数）。 定义 3.4（调整提示）。调优提示建议一个 DBMS 参数的值。我们将调优提示建模为一个三元组 ⟨𝑡,𝑝,𝑣⟩，其中 𝑡 是包含提示的文本片段，𝑝 是特定参数，𝑣 是 𝑡 中提到的特定值。如果 𝑝 在 𝑡 中被明确提及，我们就称该提示为显式提示，反之则为隐式提示。在伪代码中，我们使用 \u0026ldquo;𝑝 \u0026ldquo;或 \u0026ldquo;𝑡 \u0026ldquo;来指代提示 \u0026ldquo;𝑝 \u0026ldquo;的参数或文本。请注意，一个文本片段 𝑡 可能包含多个参数的建议或同一参数的多个建议值。因此，我们需要使用 𝑝 和 𝑣 来识别 𝑡 中的特定提示。𝑣 值不一定就是为 𝑝 提出的具体值。这就是为什么我们要将调整提示转化为公式的原因。 定义 3.5（翻译提示）。我们将调整提示⟨𝑡,𝑝,𝑣⟩ 转换为 𝑝 =𝑓 (𝑣,𝑆)形式的公式，其中 𝑓 是公式，𝑆 是数值系统属性（如主内存容量）的向量。我们考虑了 𝑓 (𝑣,𝑆) =𝑣 -𝑚 以及 𝑓 (𝑣,𝑆) =𝑣 -𝑆𝑖 -𝑚 类型的公式，其中 𝑆𝑖 是 𝑆 的 𝑖-th 分量，𝑚 ∈Ra 乘法器（从离散的 𝑀 乘法器集合中选取）。我们将举例说明调整提示及其转换。例 3.6. 假设 𝑆 =⟨8𝐺𝐵,4,1𝑇𝐵⟩描述了目标系统的内存容量、内核数量和磁盘空间大小。那么，𝑝 =shared_buffers 和 𝑣 =0.25 的调整提示 𝑓 (𝑣,𝑆) =𝑣 -𝑆0-1（其中 1 代表乘法器）应转换为公式 𝑓 (𝑣,𝑆) =𝑣-𝑆0-1（其中 1 代表乘法器），其值为 2 GB。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:5:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#problem-model"},{"categories":["Learned Data Systems"],"collections":null,"content":"System Overview DB-BERT 搜索 DBMS 调节旋钮的设置，以根据特定基准（指定工作负载和性能指标）最大化性能。 DB-BERT 与之前的调优系统不同，它利用有关 DBMS 的文本文档来调优，例如 DBMS 手册，作为附加输入。 DB-BERT 获取要调整的基准作为输入、包含调整旋钮建议设置的文本文档集合以及描述硬件平台的数字属性（即，我们的实现期望 RAM 数量、核心数量和 磁盘空间作为输入）。后一个输入对于翻译使用相对建议的文本文档中的调整提示（例如，建议缓冲区大小占 RAM 量的百分比）是必要的。 请注意，DB-BERT 不限于与上述硬件属性相关的参数。 DB-BERT 可以处理任意参数的提示，只要将推荐值指定为文本中的绝对值即可。DB-BERT 不单独使用文本输入来确定参数设置（将其与之前的 NLP 增强数据库调优工作分开）。 相反，它利用通过对 DBMS 上的特定配置进行基准测试获得的运行时反馈来进行调整。 因此，DB-BERT 需要连接到 DBMS 实例。 在调优会话开始时，DB-BERT 将输入文本划分为文本片段，并尝试从每个片段中提取调优提示（图 1 中的步骤 A）。 调整提示对应于对特定参数的特定值的推荐。 从文本片段中提取提示并非易事，特别是因为参数引用可能是隐式的（即文本没有明确提及要调整的参数的名称）。 接下来，DB-BERT 确定在以下阶段考虑提示的顺序（图 1 中的步骤 B）。 理想情况下，首先考虑最重要的提示。 DB-BERT 使用启发式方法对提示进行排序，优先考虑经常提到的参数的提示，同时限制同一参数连续考虑的提示数量。 接下来，DB-BERT 根据调整提示迭代构建配置（即调整旋钮的值分配）。 它通过试运行在输入基准上评估这些配置。 迭代继续，直到用户中断优化或达到用户指定的优化时间限制。 在每次迭代中，DB-BERT 都会考虑一批调优提示（而不是整组调优提示）。它按照调整会话开始时建立的顺序考虑提示，从而首先考虑看似最重要的提示。 对于每个提示，DB-BERT 都会做出三种类型的决策。 首先，它将提示文本转换为一个简单的方程，为参数分配一个值（图 1 中的步骤 C）。 其次，在步骤 D 中，决定是否偏离推荐值（即是否将推荐值乘以常数）。 第三，它为提示分配权重（步骤 E）。 这些权重决定了在关于同一调谐旋钮的建议相互冲突的情况下如何确定优先级。 在处理当前批次中的所有提示后，DB-BERT 将它们聚合成一小组配置（步骤 F），使用提示权重在不一致的推荐之间进行调解。 它通过试运行在用户指定的基准上评估这些配置（图 1 中的步骤 G）。 DB-BERT 在调优过程中学习改进提示的翻译、调整和加权方式。 这使得 DB-BERT 能够针对当前的基准测试和平台进行专门的配置。 DB-BERT 使用强化学习来做出与图 1 中的步骤 C 到 E 相关的所有决策。因此，学习过程由系统试图最大化的奖励函数驱动。 对于 DB-BERT，该奖励函数基于试运行期间特定配置的性能结果。 DBMS 接受的配置（即，尝试将参数设置为特定值不会导致错误）并实现高性能，从而产生高奖励值。 根据收到的奖励，系统学习在接下来的迭代中改进其决策（图 1 中的步骤 H）。 DB-BERT 使用深度强化学习。 这意味着与特定选择相关的即时和未来奖励值是使用神经网络来估计的。具体来说，DB-BERT 使用预训练语言模型 BERT 作为神经网络。 由于经过预训练，该模型具有开箱即用的强大自然语言分析功能。 为了估计步骤 C 到 E 中特定选择的价值，将 BERT 应用于文本片段对。 第一个片段取自调整提示的文本，第二个片段是表示该选择的语义的文本标签（有关示例标签，请参阅第 6 节中的表 3）。 根据收到的奖励值，BERT 模型的初始权重在调优过程中（步骤 H）进行细化。 算法 1 表示由 DB-BERT 以伪代码形式执行的主函数。 输入集成了用户提供的输入（如图 1 所示）以及其他参数，自动提取或在系统和基准测试中保持不变。 其中包括从 DBMS 中提取的整套整数、布尔和数字调节旋钮、𝑃、一组乘法器 𝑀（偏离文本中提出的值）、一组权重 𝑊（用于确定冲突提示之间的相对重要性） 来自不同来源），以及参数 𝑙、𝑒 和 𝑛 分别用于选择每个参数和迭代处理的提示数量、每次迭代考虑的提示总数以及每次迭代评估的配置数量。 这些参数的语义将在以下部分中更详细地描述。 算法 1 中的第 8 行实现了图 1 中的步骤 A，第 10 行实现了步骤 B。主循环迭代直到调整时间预算耗尽。 函数 Batches(𝐻𝑜,𝑒) 按照之前建立的提示顺序将提示分为最多 𝑒 大小的批次。 每次调用 RunEpisode 都会实现图 1 中的步骤 C 到 H。最后，DB-BERT 推荐出最佳配置。第 5 节讨论提示提取和排序。 第 6 节更详细地描述了学习过程，第 7 节概述了如何将提示聚合到配置中。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:6:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#system-overview"},{"categories":["Learned Data Systems"],"collections":null,"content":"Extracting candidate hints 第一步，DB-BERT 提取候选调整提示。 根据定义 3.4，调整提示由文本片段、参数引用和值引用组成。 算法 2 描述了提取过程（如图 2 所示）。 它提取显式和隐式参数引用。 通过将文本（向量）的 BERT 编码与参数名称的 BERT 编码进行比较，选择具有最小余弦距离的参数来获得隐式引用。 我们将文本中出现的所有数字（可能与尺寸单位组合）视为潜在的价值建议。 默认情况下，我们将表示布尔标志的打开和关闭值的值 0 和 1 添加到值集中（在调整提示中通常不会明确提及打开和关闭值）。 给定文本片段的候选提示集是参数引用和值之间的笛卡尔积。 这意味着我们的候选者可能包含错误的提示（即未通过文本链接的参数值组合）。 将实际提示与错误提示分开的任务在翻译阶段得到解决，如下一节所述。 提取候选提示后，DB-BERT 使用算法 3 对它们进行排序。我们的目标是在按排序顺序考虑提示时增加找到有希望的配置的机会。 我们考虑两条经验法则。首先，我们期望在更多文档中提及重要参数。 其次，当考虑到关于同一参数的越来越多的提示时，我们预计收益会递减。 因此，我们优先考虑更多文档中出现的参数的提示。 然而，在切换到下一个参数之前，我们最多考虑关于同一参数的固定数量的提示。 算法 3 实现了这些高级原则。 按参数对提示进行分组后，它会迭代提示索引范围。 对于每个索引范围，它按出现次数降序迭代参数，在切换到下一个参数之前为每个参数添加 𝑙 提示（直到没有任何新提示可供为任何参数添加） 例 5.1。 图 3 说明了具有三个参数的提示排序。 蓝色矩形代表每个参数的提示。 水平宽度与提示数量成正比。 从最常提到的参数开始，我们为每个参数添加有限数量的提示。处理完最不常提及的参数（由红色箭头表示）参数 3 后，我们再次从第一个参数开始，直到不再有提示为止 ","date":"2024-02-04","objectID":"/posts/llm-capability/:7:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#extracting-candidate-hints"},{"categories":["Learned Data Systems"],"collections":null,"content":"Translating single hints DB-BERT 将调优提示转换为算术公式（详细信息请参见定义 3.5）。 这些公式可能取决于文本中指定的值以及系统属性（例如主内存量）。 评估公式会产生调优旋钮的值建议。 对于每个调整提示，我们将翻译建模为一系列决策。 我们学习使用强化学习来翻译调整提示。 强化学习通常应用于马尔可夫决策过程（MDP），由一组状态、动作、将状态和动作对映射到新状态的转换函数以及奖励函数指定。 强化学习代理学习以观察为指导，做出最大化预期奖励的决策。 在我们的场景中，状态代表（部分指定的）算术公式。 操作指定公式的各个部分。 转换函数将部分指定的公式和操作链接到表示公式的状态，并按照操作中的指定完成。 奖励函数基于 DBMS 的反馈，惩罚导致不可接受的配置的翻译，同时奖励提高性能的更改。 我们在 6.1 节中描述环境的结构（即状态、动作、转换和奖励），在 6.2 节中描述学习代理的结构。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:8:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#translating-single-hints"},{"categories":["Learned Data Systems"],"collections":null,"content":"Learning Environment 算法 4 实现了 DB-BERT 用于翻译单个提示的转换函数（伪代码接近于相应 OpenAI Gym 环境中步骤函数的实现 2）。在算法 4 中，对于固定调整提示，当前状态由部分指定的公式 (𝑓) 和变量 𝑑（下一个决策的整数 ID）来表征。 对于每个提示，我们从一个空公式 𝑓 和 𝑑 =0 开始。 我们将动作（输入 𝑎）表示为从 1 到 5 的整数。 动作的语义取决于 𝑑 的值。 对于 𝑑 =0，该操作决定当前提示是否错误（常量 NO_HINT），如果没有，则决定提示是否建议相对或绝对参数值。 相对值表示为系统属性的百分比，例如主内存或核心数量（存储在向量 𝑆 中，𝑆𝑎 代表特定向量分量）。 对于相对值，我们将 𝑓 设置为值 𝑣 与相应系统属性之间的乘积。 我们通过设置 𝑆1=1 来统一相对值和绝对值的处理（即 𝑎=1 代表绝对值） 对于 𝑑 = 1，该操作从 𝑀 中选择一个允许偏离建议值的乘数。 与之前仅提取调整提示的工作不同，这种乘法器允许 DB-BERT 适应特定的基准。 在下一节中，我们将介绍一个权衡提示的附加决策。 在这里，我们经过两次决策，已经完全明确了公式。 接下来，我们尝试将参数 𝑝 设置为公式评估结果。 如果该设置被 DBMS 拒绝，我们将直接进入结束状态（常量 END）。 这种情况会产生负奖励（激励我们的代理学习将提示转化为可接受的公式）。否则，我们评估输入基准 𝑏 的性能。 结果是奖励值。 更高的奖励与更好的绩效相关。 我们通过将性能与配置进行比较来计算奖励，以评估默认设置下的性能。 对于 OLAP 基准（例如，TPC-H），我们使用运行时间的增量（按常数缩放）。 对于 OLTP 基准（例如 TPC-C），我们使用吞吐量增量。 我们奖励可接受并提高性能的配置。 这两个指标与调整直接相关。 我们在应用 DB-BERT 来针对特定基准调整特定系统时使用它们。 在将 DB-BERT 应用于特定的调优任务之前，我们会执行一个训练阶段来微调 DB-BERT 的语言模型，以实现一般的提示翻译。 为了加速收敛，仅在训练期间，我们在奖励函数中添加了一个额外的组件。 该组件奖励看起来更有可能的设置，例如 因为它们与参数的默认设置具有相同的数量级。 这种启发式方法取代了先前工作中使用的手动生成的提示翻译。 图 4 说明了转换过程背后的 MDP（图 4 中的一些状态未在算法 4 中明确表示）。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:8:1","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#learning-environment"},{"categories":["Learned Data Systems"],"collections":null,"content":"Learning Agent DB-BERT 引入了一个学习 agent 来选择操作以最大化奖励。 在每种状态下，代理都会在一组离散的选项中进行选择。 每个选项都可以表示为自然语言语句。 我们可以通过将该语句与调整提示文本进行比较来找出哪个选项是正确的。 因此，我们将动作选择建模为“多项选择问题回答问题”。 预训练的语言模型可以用来解决这个问题（在我们的实现中，我们使用 BertForMultipleChoice Transformer 模型 3）。 我们在训练期间根据收到的奖励微调模型权重。 算法 5 显示了智能体如何根据观察来评估特定操作。 除了要评估的操作之外，输入还包括当前调整提示（调整文本 𝑡、参数 𝑝 和值 𝑣）以及当前翻译步骤（决策 𝑑）的描述。 我们将行动和决策的每个组合与标签相关联。 包含这些标签的数组通过伪代码中的常量 CHOICE_LABEL 表示。 标签是自然语言句子，代表相关选择的语义。 它包含调整提示中具体参数和值的占位符。 Instantiate 函数用具体值替换占位符。 BERT 模型使用三个输入：输入文本、将输入标记与两种输入类型之一相关联的类型标记，以及指示要考虑的标记的掩码。 在这里，我们连接提示文本和实例化标签以形成输入文本。 键入将提示文本与标签分开。 默认情况下，所有输入文本都会被考虑进行处理。 在我们的通用训练阶段发生了异常（更多详细信息，请参阅第 6.1 节）。 在这里，我们希望避免学习特定参数的名称，因为它们不能跨系统泛化。 因此，我们屏蔽所有出现的当前参数名称（函数屏蔽）。 另一方面，如果学习系统和基准测试特定配置以解决具体的调优问题，则没有理由隐藏信息。 算法 5 使用布尔标志 (MASKED_MODE) 在这两种模式之间切换。 表 3 显示了与不同操作和第一决策级别相关的标签。 在此级别，我们决定候选提示是否代表实际提示，如果是，则确定该值是相对值还是绝对值。 最后，我们通过一个例子来说明翻译。 例 6.1。 考虑调整提示 ⟨𝑡,𝑝,𝑣⟩，其中 𝑡 = “将共享缓冲区设置为 RAM 的 25%”，𝑝 = shared_buffers 和 𝑣 = 25%。 首先，代理决定提示是否有效以及是否推荐绝对值或相对值。 使用表 3 中的标签，代理根据提示文本评估替代操作。 例如，对于操作 1，代理生成输入文本“将共享缓冲区设置为 RAM 的 25%。 shared_buffers and 25%与主内存相关。”，通过类型规范分隔两个句子。 如果激活屏蔽模式，则两次出现的 shared_buffers 参数将被屏蔽。 为了做出选择，代理会在内部比较将 BERT 应用于每个可能操作的输入所产生的值。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:8:2","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#learning-agent"},{"categories":["Learned Data Systems"],"collections":null,"content":"Aggregating Hints 最后一节描述如何翻译单个调整提示。 然而，我们经常需要集成多个提示（可能来自不同来源）以获得最佳性能。 DB-BERT 根据提示组创建配置。 这需要汇总来自不同来源的可能相互冲突的提示。 为了支持这一点，我们扩展了上一节中介绍的 MDP。 我们不考虑单个提示，而是考虑整批提示。 对于每个提示，我们添加一个额外的决策，将提示分配给权重。 该权重决定了将提示与其他提示聚合到配置中时的优先级。 算法 6 显示了 DB-BERT 主循环的一次迭代期间执行的完整伪代码（算法 6 由算法 1 调用）。 从强化学习的角度来看，每次迭代对应于相关 MDP 的一个情节。 每个情节都从相同的起始状态开始，代表默认配置。 因此，与默认配置相比，每集考虑的提示数量确实限制了最大更改数量。 然而，正如最近的工作所示，调整少量的调谐旋钮通常足以实现接近最佳的性能。 算法 6 获取一批候选提示作为输入。 它迭代这些提示并使用算法 4（函数 Tstep）来转换单个提示（分别确定候选提示是错误的且不应被考虑）。 我们通过指定 “-” 作为 Tstep 的基准参数来推迟基准评估。 如果成功将当前提示转换为公式（即 𝑓 ≠ −），算法 6 会分配一个权重（第 18 行）。 权重是从离散的可能性集合 𝑊 中选择的，并由学习代理（函数 ChooseAction）分配。 最后，该算法组装了一组加权调整提示 𝐻𝑤。 接下来，我们使用加权提示组装一个或多个配置进行评估。 算法 7 使用加权提示作为输入来选择和评估配置。 它迭代提示中提到的参数（从第 23 行循环到第 30 行）并选择有限数量的 𝑛 值进行尝试（𝑛 是一个调整参数）。 选择值是为了尽可能覆盖建议值（在提示中）的范围。 我们迭代地选择值（从第 26 行循环到第 29 行）。 我们希望在以下意义上尽可能接近地涵盖提示中提出的值。 给定一个比较相同参数值的距离函数 𝛿，我们的目标是最小化提示中提出的值与最接近的选定值之间的最大加权距离。 给定一组加权值 𝑉 和一组选定的配置 𝐶，函数 MaxDist 计算后一个指标。 我们贪婪地选择值，在每一步中最小化上述成本函数。 请注意，某些调整旋钮只能设置为其值范围内的特定值（例如，MySQL 的 innodb_buffer_pool_size 必须是块大小的倍数）。 我们不能简单地平均建议值。 例 7.1。 假设我们收集建议参数 shared_buffers 的以下值的提示：1GB，权重 1，2GB，权重 8，8GB，权重 1。当选择 1GB 时，我们获得最大加权距离 8·|2−1| = 8 GB 与值 2 GB（仅距离 1 ·|8 -1| = 7 GB 与 8 GB）。 选择 2 GB 会产生距 8 GB 值 7 GB 的最大加权距离。 选择 8 GB 会产生距值 2 GB 的最大加权距离 48 GB。 因此，我们首先选择值 2 GB。 接下来，我们选择值 8 GB 以将最大距离最小化为 1 GB。 最后，我们将每个参数的选定值组合成 𝑛 配置（第 32 行）。 Function Evaluate 在给定基准 𝑏 上评估选定的配置。 它对 DBMS 不接受的配置进行惩罚，否则根据基准性能计算奖励（我们使用第 6.1 节中介绍的奖励函数）。 函数 EvalWeighted 返回任何配置获得的最大奖励。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:9:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#aggregating-hints"},{"categories":["Learned Data Systems"],"collections":null,"content":"Experiments ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#experiments"},{"categories":["Learned Data Systems"],"collections":null,"content":"Setup 我们比较了 MySQL 8.0 和 Postgres 13.2 的系统配置参数调整方法。 我们考虑这些系统提供的所有数字和布尔调整参数：Postgres 的 232 个参数和 MySQL 的 266 个参数。 我们使用比例因子为 1（第 8.4 节）和 10（第 8.5 节）的 TPC-H 以及比例因子为 20 的 TPCC 作为基准。 对于 TPC-C，我们使用 10 个终端，不限制到达率，预热和测量时间均为 60 秒。 除了这些参数之外，我们还使用 OLTP 基准测试中的 Postgres 和 MySQL 的默认 TPC-C 配置。 我们执行五次运行，并允许 25 分钟的调整时间（之前的工作使用相同的时间范围 ）。 所有实验均在具有 8 个 vCPU、61 GB RAM 和具有 16 GB 内存的 Tesla V100 GPU 的 p3.2xlarge EC2 实例上执行。 EC2 实例使用 Amazon Deep Learning AMI 和 Ubuntu 18.04。 我们与最近的 DDPG++ 算法 进行比较，作为没有 NLP 增强的调优的代表。 我们考虑调整参数的不同值范围，范围从默认值的两倍（即 𝑑/2 到 2·𝑑，其中 𝑑 是默认值）到 100。我们将这些版本表示为 DDPG2、DDPG10 和 DDPG100 以下情节。 此外，我们还与最近一篇关于 NLP 增强数据库调优的论文中描述的两个基线进行了比较。 在下文中，Prior-Main 表示先前工作提出的基于监督学习的主要方法。 此外，我们还与同一篇论文中描述的简单基线（表示为 Prior-Simple）进行比较 默认情况下，我们对 DB-BERT 使用以下配置参数。 DB-BERT 使用强化学习从一组固定的替代方案中为每个提示选择乘数值和权重。 对于所有实验，DB-BERT 从集合 {1/4,1/2,1,2,4} 中选择乘数，并从集合 {0,2,4,8,16} 中选择权重。 我们在每种情况下都使用相同数量的替代方案（五个）。 这使得使用 OpenAI Gym 框架对相关环境进行建模变得更加容易。 我们避免使用过小或过大的乘法器（如果最佳参数值在任何方向上与建议值的偏差超过四倍，则应忽略相关提示）。 权重替代集允许 DB-BERT 忽略提示（通过使用零权重），并使特定提示的重要性比其他具有非零权重的提示重要八倍。 我们将 𝑙 设置为 10，以便每个情节和参数最多允许 10 个提示。 我们总共考虑每集最多 50 个提示 (𝑒 = 50)，并评估每集的两种配置 (𝑛 = 2)。 DB-BERT 将文本文档分割成长度最多为 128 个标记的片段。 所有基线均在 Python 3.7 中实现，使用 Pytorch 1.8.1 和（对于 NLP 增强的调整基线）Huggingface Transformers 库。 DB-BERT 使用 Google 的可编程搜索引擎 API6 来检索文本文档。 此外，DB-BERT 使用自主学习库 7 中的 Double Deep Q-Networks 实现作为强化学习算法 ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:1","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#setup"},{"categories":["Learned Data Systems"],"collections":null,"content":"Tuning Text Documents DB-BERT 附带一个脚本，可以通过 Google 搜索检索文本文档并将其转换为 DB-BERT 所需的输入格式。 对于以下大部分实验，我们使用通过查询“Postgresql 性能调整提示”（2021 年 4 月 11 日发布）和“MySQL 性能调整提示”（2021 年 4 月 15 日发布）检索的两个文档集合。 我们将这两个查询的前 100 个 Google 结果包含到相应的文档集合中（Postgres 总共占 1.3 MB 文本，MySQL 总共占 2.4 MB 文本）。 结果多种多样，涵盖博客条目、论坛讨论（例如，数据库管理员 Stack Exchange8）以及两个数据库系统的在线手册。 下面我们将 Postgres Pg100 的文档集合和 MySQL Ms100 的文档集合称为。 图 5 显示了这些文档集合中参数提及和建议值分配的分布，这些文档集合是通过 DB-BERT 的候选提示提取机制生成的（参见第 5 节）。 显然，文档和参数上的提示分布是不均匀的。 对于这两个数据库系统，在多个文档中提到的参数很少，而大多数参数仅在单个文档中提到。 同样，多个来源提出了一些任务。 另一方面，大多数价值分配仅提出一次。 表 4 显示了 Postgres 和 MySQL 最常提到的参数。 其中与缓冲区大小相关的参数（例如，Postgres 的共享缓冲区和 MySQL 的 innodb_buffer_pool_size）尤为突出。 除此之外，与并行性（例如 max_parallel_workers_per_gather）或日志记录（例如 max_wal_size）相关的参数也经常被提及。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:2","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#tuning-text-documents"},{"categories":["Learned Data Systems"],"collections":null,"content":"Training 其中两种比较算法，即 DB-BERT 和 Prior-Main，在运行前使用训练。 Prior-Main 使用自然语言调整提示（用相关公式注释）作为训练数据。 我们使用与之前的工作相同的训练样本和训练参数。 与后一篇论文中的实验设置一致，我们应用在 Postgres 样本上训练的 Prior-Main 来调优 MySQL，并应用在 MySQL 样本上训练的 Prior-Main 来调优 Postgres。 目标是证明 NLP 增强的数据库调优不需要特定于系统的带注释的示例。 Prior-Main 不支持从固定文档集合中提取特定于基准测试的调整提示，如果使用相同的文档集合来调整多个基准测试，则这是一个缺点。 为了至少允许一定程度的可变性，我们为五个基准运行中的每一个单独训练 Prior-Main 模型。 这导致每次运行中的提取略有不同。 在第 8.1 节中概述的平台上训练 Prior-Main 对于 MySQL 样本需要 417 秒，对于 Postgres 样本需要 393 秒。 DB-BERT 不使用带注释的调优提示进行训练。 相反，它在训练阶段使用数据库系统本身进行运行时反馈。 与 Prior-Main 类似，我们在 Pg100 上训练 DB-BERT 来调整 MySQL，在 Ms100 上训练 DB-BERT 来调整 Postgres。 我们在训练期间激活 mask mode（参见第 6 节），这意味着参数名称被屏蔽。 这避免了学习系统特定的参数名称（这在我们的实验设置中无用），并将注意力集中在调整提示的句子结构上。 DB-BERT 的奖励信号（参见第 6 节和第 7 节）结合了根据调整提示成功更改参数值（意味着相应值有效）和获得的性能的奖励。 为了衡量性能，我们使用一个综合数据库，其中包含两个表，其中两列包含从 1 到 1,000,000 的连续数字。 我们使用简单的计数聚合查询通过相等谓词连接两个表。 对性能的奖励按比例缩小了 100 倍，以避免专门针对此人为基准（它仅用于惩罚特别糟糕的配置，例如将缓冲池大小设置为最小值）。 最后，我们为设置与默认设置相同数量级的参数值添加一个小的奖励奖金（假设与默认值的极端偏差是可能的，但可能性较小）。 DB-BERT 的训练从具有 1.1 亿个参数的 BERT 基础模型 [3] 开始。 所有模型参数均在训练期间调整。 我们在 Pg100 上训练 DB-BERT 5,000 次迭代，在 Ms100 上训练 DB-BERT 10,000 次迭代（由于该集合中的提示数量较多）。 Pg100 的训练时间为 43 分钟，Ms100 的训练时间为 84 分钟。 图 6 显示了 Pg100 的进度与训练步骤数的函数关系 ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:3","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#training"},{"categories":["Learned Data Systems"],"collections":null,"content":"Comparison with Baselines 我们将 DB-BERT 与 TPC-H（见图 7）和 TPC-C（见图 8）的基线进行比较。 我们每次运行都会调整 Postgres 和 MySQL 25 分钟。 我们使用吞吐量作为 TPC-C 的优化指标和 TPC-H 的执行时间。 我们将找到的最佳配置（y 轴）的性能显示为优化时间（x 轴）的函数。 在这些图中和下面的图中，我们报告了算术平均值以及五次运行的第 20 个和第 80 个百分位数（使用误差线显示百分位数）。 DDPG++ 是一种基于强化学习的数据库调优方法。 它被证明与各种其他最先进的调整方法具有竞争力。 然而，之前的论文评估了 DDPG++ 的数十个调整参数，并为每个调整会话分配 150 次迭代。 在这里，我们考虑了数百个参数进行调整，并只允许很少迭代的调整时间范围。 显然，在分配的时间范围内，DDPG++ 没有找到与 DB-BERT 质量相当的解决方案。 特别是对于 TPC-H，DDPG++ 经常尝试显着降低性能的参数更改（例如，优化器成本常量的更改触发不同的连接顺序）。 因此，对于 DDPG++，找到的最佳配置的性能几乎保持不变（接近通过初始迭代之前尝试的默认配置实现的性能）。 DDPG++ 可以受益于指定在调整期间要考虑的特定参数值范围。 例如，与默认设置相比，将缓冲池大小增加一个数量级通常是有益的。 然而，对于优化器成本常量（例如 Postgres 中的 random_page_cost），这样做是危险的。 我们的目标是表明此类输入可以部分地被从文本中自动挖掘的信息所替代。 Prior-Simple 和 Prior-Main 是两个最相关的基线，因为两者都使用调整文本作为输入，类似于 DB-BERT。 PriorSimple 使用朴素启发式进行翻译。 应用这种启发式方法很快，并且 Prior-Simple 通常是返回结果的第一个基线。 但是，它仅从 Pg100 中提取将 checkpoint_completion_target 设置为 0.9 的建议，而没有从 Ms100 中提取建议。 因此，它不会比默认配置有所改进。 Prior-Main 的性能明显更好。 由于训练中的微小差异，不同运行中的提取有所不同，从而导致较高的方差。 例如，对于 Pg100，Prior-Main 能够提取一个调整提示，建议在五次运行中有两次将共享缓冲区设置为主内存的 25%。 这可以显着提高性能，特别是对于 TPC-H。 然而，平均性能明显低于最佳性能。 由于 Prior-Main 在聚合调整提示之前对文档集合中的所有句子进行分类，因此其运行时间明显高于 Prior-Simple。 DB-BERT 在调优时间和结果质量之间实现了有吸引力的权衡。 与 DDPG++ 不同，它使用调整文本作为输入，可以快速识别最相关的参数和候选值。 与 Prior-Simple 和 Prior-Main 相比，它平均找到明显更好的解决方案。 特别是对于 MySQL，Prior-Main 通常无法找到质量相当的解决方案。 此外，与 DB-BERT 产生接近最优解的时间（即，与 DB-BERT 最终最优解的百分之一以内）相比，Prior-Main 分析所有文档所花费的时间通常要高出两到三倍。 ）。 表 5 和表 6 显示了 DB-BERT 在调优 Postgres 时发现的配置。 尽管从同一文档集合中提取提示，DB-BERT 仍能够找到特定于基准的配置 ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:4","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#comparison-with-baselines"},{"categories":["Learned Data Systems"],"collections":null,"content":"Further Analysis 我们研究不同因素对调优性能的影响。 首先，我们将 DB-BERT 与图 9 中的两个简化变体进行比较。我们与按文档顺序处理提示的 DB-BERT 变体进行比较（而不是按照第 5 节中的描述对提示进行优先级排序）。 此外，我们还与不考虑隐式提示的变体进行比较（即，仅显式提及参数名称的提示）。 显然，这两种简化都会降低 TPC-H 的调整性能。 考虑文档顺序中的提示可以防止 DB-BERT 首先调整最相关的参数。 丢弃隐式提示会减少可用提示的总数。 接下来，我们研究输入文本的影响。 我们用一篇博客文章 9 替换了包含数百个通用调整提示的 Pg100。 这篇文章描述了如何专门针对 TPC-H 调整 Postgres。 图 5 比较了所有 NLP 增强调整基线的不同输入文档的性能。 虽然 Prior-Simple 的性能不会随着输入文本的变化而变化，但当我们切换到较小的文档时，Prior-Main 的性能会下降。 Prior-大型文档集合的主要好处是冗余提示可以部分弥补不精确的提取。 对于较小的输入文档，它不会提取任何提示。 然而，DB-BERT 受益于更专业的调整提示。 使用特定于基准的输入文本，它可以更快地收敛到接近最佳的解决方案，并最终找到稍微更好的解决方案（与表 5 相比，使用更高的共享缓冲区参数值，如博客条目中所建议的）。 最后，我们扩大数据大小。 图 11 报告了比例因子为 10 的 TPC-H 的结果（并使用 TPC-H 特定调整文本）10。 与显示缩放因子 1 的结果的图 10 相比，DB-BERT 需要更长的时间才能找到接近最优的解决方案。 这是预期的，因为每个基准评估的运行时间较长会减少每个时间单位的 DB-BERT 迭代次数。 与其他基线相比，DB-BERT 再次找到了明显更好的解决方案。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:10:5","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#further-analysis"},{"categories":["Learned Data Systems"],"collections":null,"content":"Conclusion and Outlook 本文提出了 DB-BERT，这是一个数据库调优系统，可以从文本文档中提取调优提示。 我们的实验表明，此类提示可以带来明显更好的调整结果。 在未来的工作中，我们将考虑更加多样化的调优目标。 目前，DB-BERT 仅限于优化可轻松测量的指标，例如延迟或吞吐量。 然而，还有其他一些难以衡量的重要指标。 例如，如果愿意接受数据丢失的小风险，许多参数（例如 Postgres 中的 fsync 参数）可以提高性能。 数据库手册通常包含详细说明此类风险的警告。 我们计划扩展 DB-BERT，以提取难以从手册中测量的指标信息。 因此，它可以支持用户找到最大化性能的参数设置，同时遵守其他指标的约束。 ","date":"2024-02-04","objectID":"/posts/llm-capability/:11:0","tags":["Paper Reading"],"title":"Paper Reading: DB-BERT: a Database Tuning Tool that “Reads the Manual”","uri":"/posts/llm-capability/#conclusion-and-outlook"},{"categories":["Notes"],"collections":null,"content":"NodeJS 这学期要用 JS 写分布式，看 Ryan Dahl 在 2009 年 JSConf 上分享 NodeJS 背后的概念，刚好复习一下 JS 的一些知识 NodeJS 简述： Server Side Javascript Built on Google\u0026rsquo;s V8 Evented, non-blocking IO, similar to EventMachine or Python\u0026rsquo;s Twisted. CommonJS module system (用 ES6 Module 替代 CommonJS) 8k lines of C/C++, 2k lines of JavaScript, 14 Contributes ","date":"2024-01-30","objectID":"/posts/node-js-io/:1:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#nodejs"},{"categories":["Notes"],"collections":null,"content":"IO needs to be done differently 事件驱动，单线程异步 IO 是否能理解为非阻塞的协程？ 许多 Web 应用使用类似 var result = db.query(\u0026quot;select * from T); 的语句，当查询在运行时，框架在做什么呢？大部分时间下，它在一直等待。 IO Latency 在硬盘、网络级别是较长的 好的软件是可以处理多任务的，当前线程等待时，其他的线程可以执行其他任务 Apache 和 NGINX 是如何处理 IO 的？ 从 Benchmark 来看，随着处理量增长，concurrency NGINX 是 Apache 的两倍，但内存占用上，Apache 显著大于 NGINX。 Apache uses one thread per connection NGINX doesnot use threads, it uses an event loop 对于大量的并发操作，不能用 OS threads 处理每个连接：上下文切换是昂贵的，调用栈也非常占用空间。而是用单线程和事件循环。 green threads / coroutines can improve 但还是可能是阻塞的 threaded concurrency is a leaky abstraction 使用事件循环，，不需要 machinery，query 结束之后执行相应的函数： db.query(\u0026#39;select * from T\u0026#39;, () =\u0026gt; {});","date":"2024-01-30","objectID":"/posts/node-js-io/:2:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#io-needs-to-be-done-differently"},{"categories":["Notes"],"collections":null,"content":"Cultural Bias everybody is talking about threads 学 IO 的时候大部分人都是先接触阻塞的 puts() 和 gets() ","date":"2024-01-30","objectID":"/posts/node-js-io/:2:1","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#cultural-bias"},{"categories":["Notes"],"collections":null,"content":"Missing Infrastructure Why isn\u0026rsquo;t everyone using event loops? Single threaded event loops require I/O to be non-blocking POSIX async closures and aynonymous functions async queries \u0026hellip; ","date":"2024-01-30","objectID":"/posts/node-js-io/:2:2","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#missing-infrastructure"},{"categories":["Notes"],"collections":null,"content":"Too much Infrastructure EventMachine, Twisted, AnyEvent easy to create efficient servers (Ruby) ","date":"2024-01-30","objectID":"/posts/node-js-io/:2:3","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#too-much-infrastructure"},{"categories":["Notes"],"collections":null,"content":"Javascript 事件循环：微任务、宏任务 https://javascript.info/event-loop https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#queue Javascript designed specifically to be used with an event loop anonymous functions, closures only one callback at a time I/O through DOM event callbacks ","date":"2024-01-30","objectID":"/posts/node-js-io/:3:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#javascript"},{"categories":["Notes"],"collections":null,"content":"NodeJS provide a purely evented, non-blocking infrastructure to script highly concurrent programs ","date":"2024-01-30","objectID":"/posts/node-js-io/:4:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#nodejs-1"},{"categories":["Notes"],"collections":null,"content":"Design Goals no functions should direct perform I/O, to receive info from disk/network/another process there must be a callback ","date":"2024-01-30","objectID":"/posts/node-js-io/:5:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#design-goals"},{"categories":["Notes"],"collections":null,"content":"Low level 这部分讨论了 Ryan Dahl 是怎么考虑和设计的 stream everything, never force the buffering of data donot remove functionality present at the POSIX layer (support half-closed TCP connections) have built-in support for the most important protocols: TCP, DNS, HTTP support many HTTP features: chunked requests and responses keep-alive hang requests API should be both familiar to client-side JS programmers and old school UNIX hackers platform independent ","date":"2024-01-30","objectID":"/posts/node-js-io/:5:1","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#low-level"},{"categories":["Notes"],"collections":null,"content":"Usage and Examples 一些事件循环的例子，由于都比较久而且 PPT 实在看不清，这里用 MDN 最新的 (() =\u0026gt; { console.log(\u0026#34;this is the start\u0026#34;); setTimeout(() =\u0026gt; { console.log(\u0026#34;Callback 1: this is a msg from call back\u0026#34;); }); // has a default time value of 0 console.log(\u0026#34;this is just a message\u0026#34;); setTimeout(() =\u0026gt; { console.log(\u0026#34;Callback 2: this is a msg from call back\u0026#34;); }, 0); console.log(\u0026#34;this is the end\u0026#34;); })(); // \u0026#34;this is the start\u0026#34; // \u0026#34;this is just a message\u0026#34; // \u0026#34;this is the end\u0026#34; // \u0026#34;Callback 1: this is a msg from call back\u0026#34; // \u0026#34;Callback 2: this is a msg from call back\u0026#34; process object emits an event when it receives a signal. Like in the DOM, you need only add a listener to catch them. process.addListener(\u0026#39;SIGINT\u0026#39;, () =\u0026gt; { puts(\u0026#39;good bye\u0026#39;); process.exit(0); });A TCP server emits a \u0026ldquo;connection\u0026rdquo; event each time someone connects HTTP upload emits a \u0026ldquo;body\u0026rdquo; event on each packet. All objects which emit events are instances of process.EventEmitter ","date":"2024-01-30","objectID":"/posts/node-js-io/:6:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#usage-and-examples"},{"categories":["Notes"],"collections":null,"content":"File I/O is non-blocking something typically hard to do 这部分主要读取文件，代码实在看不清 A promise is a kind of EventEmitter which emits either \u0026ldquo;success\u0026rdquo; or \u0026ldquo;error\u0026rdquo; All file operations return a promise (API sugar) ","date":"2024-01-30","objectID":"/posts/node-js-io/:6:1","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#file-io-is-non-blocking"},{"categories":["Notes"],"collections":null,"content":"HTTP Server 展示了 Simple HTTP server 和 Streaming HTTP server，后者可以 hang request 此外还可以使用 sys.exec('ls -l /').addCallback() 这部分提到的 buffer 不太理解是什么意思，不知道是不是用于处理 TCP 流、文件系统操作、上下文之类的二进制流数据的 Buffer。 ","date":"2024-01-30","objectID":"/posts/node-js-io/:6:2","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#http-server"},{"categories":["Notes"],"collections":null,"content":"IRC demo Ryan 展示了一个 Internet Relay Chat 聊天服务器 https://gist.github.com/ry/a3d0bbbff196af633995 ","date":"2024-01-30","objectID":"/posts/node-js-io/:7:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#irc-demo"},{"categories":["Notes"],"collections":null,"content":"Internal Design V8 (Google) libuv / libev / libeio http-parser evocom udns Thread Pool underneath everything: Blocking (possibly blocking) system calls are executed in the thread pool Signal handlers and thread pool callbacks are marshaled back into to the main thread via a pipe STDIN_FILENO will refer to a file, cannot select() on files: read() will block Solution: start a pipe and pumping thread, pump data from blocking fd into pipe. Main thread can pool for data on the pipe. 这一段源码 https://github.com/nodejs/node/blob/d52f63d9b29446b26ec831acd8ec6da9147896e5/deps/coupling/coupling.c 不知道是不是已经被弃用了，最早的源代码很多都是 C 写的 ","date":"2024-01-30","objectID":"/posts/node-js-io/:8:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#internal-design"},{"categories":["Notes"],"collections":null,"content":"结语 09 年的时候提出这种单线程非阻塞的异步设计，是非常惊艳的。希望有时间可以看看 NodeJS 的源码。 03/06/2024 补充，当时忽略了 libuv 的引入，只知道 NodeJS 是事件循环的，但是不知道他是怎么实现的。 ","date":"2024-01-30","objectID":"/posts/node-js-io/:9:0","tags":["笔记"],"title":"阅读：Ryan Dahl: NodeJS","uri":"/posts/node-js-io/#结语"},{"categories":["Database"],"collections":null,"content":"DDIA 序章 http://ddia.vonng.com/#/ch1 数据密集型 的应用，数据是其主要挑战（数据量，数据复杂度或数据变化速度）。 计算密集型，处理器速度是其瓶颈。 新型数据库系统（NoSQL），消息队列，缓存，搜索索引，批处理和流处理框架 以及相关技术也非常重要。 ","date":"2024-01-05","objectID":"/posts/ddia-1/:1:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#ddia-序章"},{"categories":["Database"],"collections":null,"content":"大纲 第一部分：讨论设计数据密集型应用所赖的基本思想。目标：可靠性、可伸缩性和可维护性。方法：几种不同的数据模型和查询语言。存储引擎：数据库如何在磁盘上摆放数据，如何高效查找。数据编码（序列化），以及随时间演化的模式 第二部分：存储在一台机器上的数据转向讨论分布在多台机器上的数据。复制（第五章）、分区 / 分片（第六章）和事务（第七章）、分布式系统问题的更多细节（第八章）、分布式系统中实现一致性与共识意味着什么（第九章）。 第三部分：从其他数据集衍生出一些数据集的系统。第十章 批处理方法，第十一章 流处理，第十二章 中，讨论在将来构建可靠、可伸缩和可维护的应用程序的方法。 ","date":"2024-01-05","objectID":"/posts/ddia-1/:2:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#大纲"},{"categories":["Database"],"collections":null,"content":"第一部分：数据系统基础 数据系统底层的基础概念：单机和多机分布式 ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#第一部分数据系统基础"},{"categories":["Database"],"collections":null,"content":"第一章：可靠性、可伸缩性和可维护性 现今很多应用程序都是 数据密集型的，而非计算密集型的。许多程序都需要： 存储数据：数据库 加快读取速度：缓存 搜索、过滤：搜索索引 发送消息、异步处理：流处理 定期处理大批量数据：批处理 数据系统 Data System 数据库、消息队列、缓存等工具分属于几个差异显著的类别，但类别之间的界限变得越来越模糊，例如：数据存储可以被当成消息队列用（Redis），消息队列则带有类似数据库的持久保证（Apache Kafka） 数据库课上听到过一个说法：NoSQL 和关系型数据库并不对立，它们逐渐拥有对方的功能。 越来越多的应用程序有着各种严格而广泛的要求，单个工具不足以满足所有的数据处理和存储需求。取而代之的是，总体工作被拆分成一系列能被单个工具高效完成的任务，并通过应用代码将它们缝合起来。 如果一个工具能实现多种功能，它会有市场吗？是什么原因导致现在的架构设计需要用多种框架和组件？是低耦合的原则还是质量和效率限制？还是文中提到的可靠性、可伸缩性、可维护性？ 当系统出问题时，如何确保数据的正确性和完整性？当部分系统退化降级时，如何为客户提供始终如一的良好性能？当负载增加时，如何扩容应对？什么样的 API 才是好的 API？ 可靠性（Reliability）：系统在困境中仍可正常工作 可伸缩性（Scalability）：有合理的办法应对系统的增长 可维护性（Maintainability）：许多不同的人（工程师、运维）在不同的生命周期，都能高效地在系统上工作 ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:1","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#第一章可靠性可伸缩性和可维护性"},{"categories":["Database"],"collections":null,"content":"第一章：可靠性、可伸缩性和可维护性 现今很多应用程序都是 数据密集型的，而非计算密集型的。许多程序都需要： 存储数据：数据库 加快读取速度：缓存 搜索、过滤：搜索索引 发送消息、异步处理：流处理 定期处理大批量数据：批处理 数据系统 Data System 数据库、消息队列、缓存等工具分属于几个差异显著的类别，但类别之间的界限变得越来越模糊，例如：数据存储可以被当成消息队列用（Redis），消息队列则带有类似数据库的持久保证（Apache Kafka） 数据库课上听到过一个说法：NoSQL 和关系型数据库并不对立，它们逐渐拥有对方的功能。 越来越多的应用程序有着各种严格而广泛的要求，单个工具不足以满足所有的数据处理和存储需求。取而代之的是，总体工作被拆分成一系列能被单个工具高效完成的任务，并通过应用代码将它们缝合起来。 如果一个工具能实现多种功能，它会有市场吗？是什么原因导致现在的架构设计需要用多种框架和组件？是低耦合的原则还是质量和效率限制？还是文中提到的可靠性、可伸缩性、可维护性？ 当系统出问题时，如何确保数据的正确性和完整性？当部分系统退化降级时，如何为客户提供始终如一的良好性能？当负载增加时，如何扩容应对？什么样的 API 才是好的 API？ 可靠性（Reliability）：系统在困境中仍可正常工作 可伸缩性（Scalability）：有合理的办法应对系统的增长 可维护性（Maintainability）：许多不同的人（工程师、运维）在不同的生命周期，都能高效地在系统上工作 ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:1","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#数据系统-data-system"},{"categories":["Database"],"collections":null,"content":"可靠性 即使出现问题，也能继续正确工作。造成错误的原因叫做 故障（fault），能预料并应对故障的系统特性可称为 容错（fault-tolerant） 或 韧性（resilient） 反直觉的是，在这类容错系统中，通过故意触发来提高故障率是有意义的。尽管比起 阻止错误（prevent error），我们通常更倾向于 容忍错误。 硬件故障 硬盘的 平均无故障时间（MTTF, mean time to failure）：从数学期望上讲，在拥有 10000 个磁盘的存储集群上，平均每天会有 1 个磁盘出故障。 为了减少系统的故障率，第一反应通常都是增加单个硬件的冗余度，例如：磁盘可以组建 RAID，服务器可能有双路电源和热插拔 CPU，数据中心可能有电池和柴油发电机作为后备电源，某个组件挂掉时冗余组件可以立刻接管。但大量使用机器，会相应地增加硬件故障率。 云平台的设计就是优先考虑 灵活性（flexibility） 和 弹性（elasticity），而不是单机可靠性。 软件错误 通常认为硬件故障是随机的、相互独立的 另一类错误是内部的 系统性错误，因为是跨节点相关的，所以比起不相关的硬件故障往往可能造成更多的 系统失效 错误输入 失控进程用尽一些共享资源，包括 CPU 时间、内存、磁盘空间或网络带宽。 系统依赖的服务变慢，没有响应，错误响应 级联故障 办法：仔细考虑系统中的假设和交互；彻底的测试；进程隔离；允许进程崩溃并重启；测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证（例如在一个消息队列中，进入与发出的消息数量相等），那么系统就可以在运行时不断自检，并在出现 差异（discrepancy） 时报警 测试是相当重要的一环 人为错误 人类是不可靠的 以最小化犯错机会的方式设计系统：精心设计的抽象、API 和管理后台 解耦（decouple），提供一个功能齐全的非生产环境 沙箱（sandbox） 各个层次进行彻底的测试：单元测试、全系统集成测试到手动测试，覆盖正常情况中少见的 边缘场景 corner case 快速地恢复：快速回滚配置变更，分批发布新代码，并提供数据重算工具 配置详细和明确的监控，比如性能指标和错误率 良好的管理实践与充分的培训 人类是不可靠的 可靠性有多重要？ 核电站和空中交通管制软件，商务应用，电商网站 在某些情况下，我们可能会选择牺牲可靠性来降低开发成本，但我们偷工减料时，应该清楚意识到自己在做什么。 可靠性和正确性是否相关？ ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:2","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#可靠性"},{"categories":["Database"],"collections":null,"content":"可靠性 即使出现问题，也能继续正确工作。造成错误的原因叫做 故障（fault），能预料并应对故障的系统特性可称为 容错（fault-tolerant） 或 韧性（resilient） 反直觉的是，在这类容错系统中，通过故意触发来提高故障率是有意义的。尽管比起 阻止错误（prevent error），我们通常更倾向于 容忍错误。 硬件故障 硬盘的 平均无故障时间（MTTF, mean time to failure）：从数学期望上讲，在拥有 10000 个磁盘的存储集群上，平均每天会有 1 个磁盘出故障。 为了减少系统的故障率，第一反应通常都是增加单个硬件的冗余度，例如：磁盘可以组建 RAID，服务器可能有双路电源和热插拔 CPU，数据中心可能有电池和柴油发电机作为后备电源，某个组件挂掉时冗余组件可以立刻接管。但大量使用机器，会相应地增加硬件故障率。 云平台的设计就是优先考虑 灵活性（flexibility） 和 弹性（elasticity），而不是单机可靠性。 软件错误 通常认为硬件故障是随机的、相互独立的 另一类错误是内部的 系统性错误，因为是跨节点相关的，所以比起不相关的硬件故障往往可能造成更多的 系统失效 错误输入 失控进程用尽一些共享资源，包括 CPU 时间、内存、磁盘空间或网络带宽。 系统依赖的服务变慢，没有响应，错误响应 级联故障 办法：仔细考虑系统中的假设和交互；彻底的测试；进程隔离；允许进程崩溃并重启；测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证（例如在一个消息队列中，进入与发出的消息数量相等），那么系统就可以在运行时不断自检，并在出现 差异（discrepancy） 时报警 测试是相当重要的一环 人为错误 人类是不可靠的 以最小化犯错机会的方式设计系统：精心设计的抽象、API 和管理后台 解耦（decouple），提供一个功能齐全的非生产环境 沙箱（sandbox） 各个层次进行彻底的测试：单元测试、全系统集成测试到手动测试，覆盖正常情况中少见的 边缘场景 corner case 快速地恢复：快速回滚配置变更，分批发布新代码，并提供数据重算工具 配置详细和明确的监控，比如性能指标和错误率 良好的管理实践与充分的培训 人类是不可靠的 可靠性有多重要？ 核电站和空中交通管制软件，商务应用，电商网站 在某些情况下，我们可能会选择牺牲可靠性来降低开发成本，但我们偷工减料时，应该清楚意识到自己在做什么。 可靠性和正确性是否相关？ ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:2","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#硬件故障"},{"categories":["Database"],"collections":null,"content":"可靠性 即使出现问题，也能继续正确工作。造成错误的原因叫做 故障（fault），能预料并应对故障的系统特性可称为 容错（fault-tolerant） 或 韧性（resilient） 反直觉的是，在这类容错系统中，通过故意触发来提高故障率是有意义的。尽管比起 阻止错误（prevent error），我们通常更倾向于 容忍错误。 硬件故障 硬盘的 平均无故障时间（MTTF, mean time to failure）：从数学期望上讲，在拥有 10000 个磁盘的存储集群上，平均每天会有 1 个磁盘出故障。 为了减少系统的故障率，第一反应通常都是增加单个硬件的冗余度，例如：磁盘可以组建 RAID，服务器可能有双路电源和热插拔 CPU，数据中心可能有电池和柴油发电机作为后备电源，某个组件挂掉时冗余组件可以立刻接管。但大量使用机器，会相应地增加硬件故障率。 云平台的设计就是优先考虑 灵活性（flexibility） 和 弹性（elasticity），而不是单机可靠性。 软件错误 通常认为硬件故障是随机的、相互独立的 另一类错误是内部的 系统性错误，因为是跨节点相关的，所以比起不相关的硬件故障往往可能造成更多的 系统失效 错误输入 失控进程用尽一些共享资源，包括 CPU 时间、内存、磁盘空间或网络带宽。 系统依赖的服务变慢，没有响应，错误响应 级联故障 办法：仔细考虑系统中的假设和交互；彻底的测试；进程隔离；允许进程崩溃并重启；测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证（例如在一个消息队列中，进入与发出的消息数量相等），那么系统就可以在运行时不断自检，并在出现 差异（discrepancy） 时报警 测试是相当重要的一环 人为错误 人类是不可靠的 以最小化犯错机会的方式设计系统：精心设计的抽象、API 和管理后台 解耦（decouple），提供一个功能齐全的非生产环境 沙箱（sandbox） 各个层次进行彻底的测试：单元测试、全系统集成测试到手动测试，覆盖正常情况中少见的 边缘场景 corner case 快速地恢复：快速回滚配置变更，分批发布新代码，并提供数据重算工具 配置详细和明确的监控，比如性能指标和错误率 良好的管理实践与充分的培训 人类是不可靠的 可靠性有多重要？ 核电站和空中交通管制软件，商务应用，电商网站 在某些情况下，我们可能会选择牺牲可靠性来降低开发成本，但我们偷工减料时，应该清楚意识到自己在做什么。 可靠性和正确性是否相关？ ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:2","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#软件错误"},{"categories":["Database"],"collections":null,"content":"可靠性 即使出现问题，也能继续正确工作。造成错误的原因叫做 故障（fault），能预料并应对故障的系统特性可称为 容错（fault-tolerant） 或 韧性（resilient） 反直觉的是，在这类容错系统中，通过故意触发来提高故障率是有意义的。尽管比起 阻止错误（prevent error），我们通常更倾向于 容忍错误。 硬件故障 硬盘的 平均无故障时间（MTTF, mean time to failure）：从数学期望上讲，在拥有 10000 个磁盘的存储集群上，平均每天会有 1 个磁盘出故障。 为了减少系统的故障率，第一反应通常都是增加单个硬件的冗余度，例如：磁盘可以组建 RAID，服务器可能有双路电源和热插拔 CPU，数据中心可能有电池和柴油发电机作为后备电源，某个组件挂掉时冗余组件可以立刻接管。但大量使用机器，会相应地增加硬件故障率。 云平台的设计就是优先考虑 灵活性（flexibility） 和 弹性（elasticity），而不是单机可靠性。 软件错误 通常认为硬件故障是随机的、相互独立的 另一类错误是内部的 系统性错误，因为是跨节点相关的，所以比起不相关的硬件故障往往可能造成更多的 系统失效 错误输入 失控进程用尽一些共享资源，包括 CPU 时间、内存、磁盘空间或网络带宽。 系统依赖的服务变慢，没有响应，错误响应 级联故障 办法：仔细考虑系统中的假设和交互；彻底的测试；进程隔离；允许进程崩溃并重启；测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证（例如在一个消息队列中，进入与发出的消息数量相等），那么系统就可以在运行时不断自检，并在出现 差异（discrepancy） 时报警 测试是相当重要的一环 人为错误 人类是不可靠的 以最小化犯错机会的方式设计系统：精心设计的抽象、API 和管理后台 解耦（decouple），提供一个功能齐全的非生产环境 沙箱（sandbox） 各个层次进行彻底的测试：单元测试、全系统集成测试到手动测试，覆盖正常情况中少见的 边缘场景 corner case 快速地恢复：快速回滚配置变更，分批发布新代码，并提供数据重算工具 配置详细和明确的监控，比如性能指标和错误率 良好的管理实践与充分的培训 人类是不可靠的 可靠性有多重要？ 核电站和空中交通管制软件，商务应用，电商网站 在某些情况下，我们可能会选择牺牲可靠性来降低开发成本，但我们偷工减料时，应该清楚意识到自己在做什么。 可靠性和正确性是否相关？ ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:2","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#人为错误"},{"categories":["Database"],"collections":null,"content":"可靠性 即使出现问题，也能继续正确工作。造成错误的原因叫做 故障（fault），能预料并应对故障的系统特性可称为 容错（fault-tolerant） 或 韧性（resilient） 反直觉的是，在这类容错系统中，通过故意触发来提高故障率是有意义的。尽管比起 阻止错误（prevent error），我们通常更倾向于 容忍错误。 硬件故障 硬盘的 平均无故障时间（MTTF, mean time to failure）：从数学期望上讲，在拥有 10000 个磁盘的存储集群上，平均每天会有 1 个磁盘出故障。 为了减少系统的故障率，第一反应通常都是增加单个硬件的冗余度，例如：磁盘可以组建 RAID，服务器可能有双路电源和热插拔 CPU，数据中心可能有电池和柴油发电机作为后备电源，某个组件挂掉时冗余组件可以立刻接管。但大量使用机器，会相应地增加硬件故障率。 云平台的设计就是优先考虑 灵活性（flexibility） 和 弹性（elasticity），而不是单机可靠性。 软件错误 通常认为硬件故障是随机的、相互独立的 另一类错误是内部的 系统性错误，因为是跨节点相关的，所以比起不相关的硬件故障往往可能造成更多的 系统失效 错误输入 失控进程用尽一些共享资源，包括 CPU 时间、内存、磁盘空间或网络带宽。 系统依赖的服务变慢，没有响应，错误响应 级联故障 办法：仔细考虑系统中的假设和交互；彻底的测试；进程隔离；允许进程崩溃并重启；测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证（例如在一个消息队列中，进入与发出的消息数量相等），那么系统就可以在运行时不断自检，并在出现 差异（discrepancy） 时报警 测试是相当重要的一环 人为错误 人类是不可靠的 以最小化犯错机会的方式设计系统：精心设计的抽象、API 和管理后台 解耦（decouple），提供一个功能齐全的非生产环境 沙箱（sandbox） 各个层次进行彻底的测试：单元测试、全系统集成测试到手动测试，覆盖正常情况中少见的 边缘场景 corner case 快速地恢复：快速回滚配置变更，分批发布新代码，并提供数据重算工具 配置详细和明确的监控，比如性能指标和错误率 良好的管理实践与充分的培训 人类是不可靠的 可靠性有多重要？ 核电站和空中交通管制软件，商务应用，电商网站 在某些情况下，我们可能会选择牺牲可靠性来降低开发成本，但我们偷工减料时，应该清楚意识到自己在做什么。 可靠性和正确性是否相关？ ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:2","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#可靠性有多重要"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#可伸缩性"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#描述负载"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#描述性能"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#实践中的百分位点"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#应对负载的方法"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#可维护性"},{"categories":["Database"],"collections":null,"content":"可伸缩性 系统今天能可靠运行，并不意味未来也能可靠运行。 服务 降级（degradation） 的一个常见原因是负载增加，例如：系统负载已经从一万个并发用户增长到十万个并发用户。 可伸缩性（Scalability） 是用来描述系统应对负载增长能力的术语。讨论可伸缩性意味着考虑诸如 “如果系统以特定方式增长，有什么选项可以应对增长？” 和 “如何增加计算资源来处理额外的负载？” 等问题。 遇到过一个单机 AWS DocDB 查询慢的问题，通过增加多个实例能解决。但还是需要添加索引或是分片。 描述负载 在讨论增长问题（如果负载加倍会发生什么？）前，首先要能简要描述系统的当前负载：负载参数 load parameters，参数的最佳选择取决于系统架构，它可能是每秒向 Web 服务器发出的请求 QPS、数据库中的读写比率、聊天室中同时活跃的用户数量、缓存命中率或其他东西。除此之外，也许平均情况对你很重要，也许你的瓶颈是少数极端场景。 推特的两个主要业务是：发布推文（平均 4.6k 请求 / 秒，峰值超过 12k 请求 / 秒）、主页时间线（用户可以查阅他们关注的人发布的推文（300k 请求 / 秒）） 然而推特的伸缩性挑战并不是主要来自推特量，而是来自 扇出（fan-out）—— 每个用户关注了很多人，也被很多人关注。有两种实现方法： 扇出：在事务处理系统中，使用它来描述为了服务一个传入请求而需要执行其他服务的请求数量。 发布推文：将新推文插入全局推文集合，当一个用户请求自己的主页时间线时，首先查找他关注的所有人，查询这些被关注用户发布的推文并按时间顺序合并。在如 图 1-2 所示的关系型数据库中，可以编写这样的查询： SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user 如何设计这个表？follows 表存关注者和用户 ID；tweets 表存推文 id，发送者 id，推文，时间戳；users 表存 id，昵称，头像。符合最小化冗余和依赖，normalization。 为每个用户的主页时间线维护一个缓存，就像每个用户的推文收件箱。 当一个用户发布推文时，查找所有关注该用户的人，并将新的推文插入到每个主页时间线缓存中。 因此读取主页时间线的请求开销很小，因为结果已经提前计算好了。 方法一查询负载较高，而方法二效果更好，因为发推频率比查询主页时间线的频率几乎低了两个数量级，所以在这种情况下，最好在写入时做更多的工作，而在读取时做更少的工作。 但方法二的缺点是，在发推时需要做大量额外工作。平均来说，一条推文会发往约 75 个关注者，所以每秒 4.6k 的发推写入，变成了对主页时间线缓存每秒 345k 的写入。但这个平均值隐藏了用户粉丝数差异巨大这一现实，一些用户有超过 3000 万的粉丝，这意味着一条推文就可能会导致主页时间线缓存的 3000 万次写入！及时完成这种操作是一个巨大的挑战 —— 推特尝试在 5 秒内向粉丝发送推文。 每个用户粉丝数的分布（可能按这些用户的发推频率来加权）是探讨可伸缩性的一个关键负载参数，因为它决定了扇出负载。 推特采用混合方法，这种混合方法能始终如一地提供良好性能。 这一章讨论了许多 system design 需要考虑的一些指标，推特的例子也非常好 描述性能 当负载增加会发生什么？ 增加负载参数并保持系统资源（CPU、内存、网络带宽等）不变时，系统性能将受到什么影响？ 增加负载参数并希望保持性能不变时，需要增加多少系统资源？ 如何描述系统性能？ 对于 Hadoop 这样的批处理系统，通常关心的是 吞吐量（throughput），即每秒可以处理的记录数量，或者在特定规模数据集上运行作业的总时间 。对于在线系统，通常更重要的是服务的 响应时间（response time），即客户端发送请求到接收响应之间的时间。 理想情况下，批量作业的运行时间是数据集的大小除以吞吐量。 在实践中由于数据倾斜（数据不是均匀分布在每个工作进程中），需要等待最慢的任务完成，所以运行时间往往更长。 延迟和响应时间 延迟（latency） 和 响应时间（response time） 经常用作同义词，但实际上它们并不一样。响应时间是客户所看到的，除了实际处理请求的时间（ 服务时间（service time） ）之外，还包括网络延迟和排队延迟。延迟是某个请求等待处理的 持续时长，在此期间它处于 休眠（latent） 状态，并等待服务。 即使不断重复发送同样的请求，每次得到的响应时间也都会略有不同。将响应时间视为一个可以测量的数值 分布（distribution），而不是单个数值。 通常报表都会展示服务的平均响应时间。然而如果你想知道 “典型（typical）” 响应时间，那么平均值并不是一个非常好的指标，因为它不能告诉你有多少用户实际上经历了这个延迟。 通常使用 百分位点（percentiles） 会更好。如果将响应时间列表按最快到最慢排序，那么 中位数（median）就在正中间，可以表示一半的请求少于中位数。 如果想知道典型场景下用户需要等待多长时间，那么中位数是一个好的度量标准，一半用户请求的响应时间少于响应时间的中位数，另一半服务时间比中位数长。中位数也被称为第 50 百分位点，有时缩写为 p50。注意中位数是关于单个请求的；如果用户同时发出几个请求（在一个会话过程中，或者由于一个页面中包含了多个资源），则至少一个请求比中位数慢的概率远大于 50%。 为了弄清异常值有多糟糕，可以看看更高的百分位点，例如第 95、99 和 99.9 百分位点（缩写为 p95，p99 和 p999）。它们意味着 95%、99% 或 99.9% 的请求响应时间要比该阈值快。 响应时间的高百分位点（也称为 尾部延迟，即 tail latencies）非常重要，因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准，即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户，也可以说是最有价值的客户 —— 因为他们掏钱了。保证网站响应迅速对于保持客户的满意度非常重要，亚马逊观察到：响应时间增加 100 毫秒，销售量就减少 1%；而另一些报告说：慢 1 秒钟会让客户满意度指标减少 16%。另一方面，优化第 99.99 百分位点（一万个请求中最慢的一个）被认为太昂贵了，不能为亚马逊的目标带来足够好处。减小高百分位点处的响应时间相当困难，因为它很容易受到随机事件的影响，这超出了控制范围，而且效益也很小。 百分位点通常用于 服务级别目标（SLO, service level objectives） 和 服务级别协议（SLA, service level agreements），即定义服务预期性能和可用性的合同。 SLA 可能会声明，如果服务响应时间的中位数小于 200 毫秒，且 99.9 百分位点低于 1 秒，则认为服务工作正常（如果响应时间更长，就认为服务不达标）。这些指标为客户设定了期望值，并允许客户在 SLA 未达标的情况下要求退款。 排队延迟（queueing delay） 通常占了高百分位点处响应时间的很大一部分。由于服务器只能并行处理少量的事务（如受其 CPU 核数的限制），所以只要有少量缓慢的请求就能阻碍后续请求的处理，这种效应有时被称为 头部阻塞（head-of-line blocking） 。即使后续请求在服务器上处理的非常迅速，由于需要等待先前请求完成，客户端最终看到的是缓慢的总体响应时间。因为存在这种效应，测量客户端的响应时间非常重要。 实践中的百分位点 在多重调用的后端服务里，高百分位数变得特别重要。即使并行调用，最终用户请求仍然需要等待最慢的并行调用完成。 如果你想将响应时间百分点添加到你的服务的监视仪表板，则需要持续有效地计算它们。 简单的实现是在时间窗口内保存所有请求的响应时间列表，并且每分钟对列表进行排序。如果对你来说效率太低，那么有一些算法能够以最小的 CPU 和内存成本（如前向衰减、t-digest 或 HdrHistogram）来计算百分位数的近似值。 用于描述负载和性能的指标，这部分概念也太多了，经常听到 SLA 但不能直观理解。许多 Grafana 上的指标也没有深入去理解。 应对负载的方法 已经讨论了用于描述负载的参数和用于衡量性能的指标。可以开始认真讨论可伸缩性了：当负载参数增加时，如何保持良好的性能？ 纵向伸缩（scaling up，也称为垂直伸缩，即 vertical scaling，转向更强大的机器） 横向伸缩（scaling out，也称为水平伸缩，即 horizontal scaling，将负载分布到多台小机器上） 跨多台机器分配负载也称为 “无共享（shared-nothing）” 架构。可以在单台机器上运行的系统通常更简单，但高端机器可能非常贵，所以非常密集的负载通常无法避免地需要横向伸缩。现实世界中的优秀架构需要将这两种方法务实地结合，因为使用几台足够强大的机器可能比使用大量的小型虚拟机更简单也更便宜。 有些系统是 弹性（elastic） 的，这意味着可以在检测到负载增加时自动增加计算资源，而其他系统则是手动伸缩（人工分析容量并决定向系统添加更多的机器）。如果负载 极难预测（highly ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:3","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#简单性管理复杂度"},{"categories":["Database"],"collections":null,"content":"可演化性：拥抱变化 系统的需求永远不变，基本是不可能的。更可能的情况是，它们处于常态的变化中，例如：你了解了新的事实、出现意想不到的应用场景、业务优先级发生变化、用户要求新功能、新平台取代旧平台、法律或监管要求发生变化、系统增长迫使架构变化等。 在组织流程方面， 敏捷（agile） 工作模式为适应变化提供了一个框架。敏捷社区还开发了对在频繁变化的环境中开发软件很有帮助的技术工具和模式，如 测试驱动开发（TDD, test-driven development） 和 重构（refactoring） 。 重构 改善既有代码的设计第二版 也是一本没坚持看完的书 修改数据系统并使其适应不断变化需求的容易程度，是与 简单性 和 抽象性 密切相关的：简单易懂的系统通常比复杂系统更容易修改。但由于这是一个非常重要的概念，我们将用一个不同的词来指代数据系统层面的敏捷性： 可演化性（evolvability） ","date":"2024-01-05","objectID":"/posts/ddia-1/:3:4","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#可演化性拥抱变化"},{"categories":["Database"],"collections":null,"content":"本章总结 本章探讨了一些关于数据密集型应用的基本思考方式 一个应用必须满足各种需求才称得上有用：功能需求（它应该做什么，比如允许以各种方式存储，检索，搜索和处理数据）、非功能需求（通用属性，安全性、可靠性、合规性、可伸缩性、兼容性和可维护性） 详细讨论了可靠性、可伸缩性、可维护性 ","date":"2024-01-05","objectID":"/posts/ddia-1/:4:0","tags":["DDIA","笔记"],"title":"阅读：DDIA 序章以及第一章","uri":"/posts/ddia-1/#本章总结"},{"categories":["Notes"],"collections":null,"content":"总结过去 其实不太希望写一些小学生流水账来记录过去，但 2023 过的实在是太快，年纪到了也开始逐渐忘事，不写一些东西总会记不起自己做了什么，需要做什么。 2023 年过得很匆忙，其实每天就是实习和打打游戏就过去了。养成了天天睡前总爱刷视频的坏习惯，学习的动力也少了很多，碎片化学习的方式始终不适合我。 去年八月底到了美国读研，但也就是换了个地方打游戏和刷手机。找实习的路程过于坎坷，几百份申请下去不到两个面试，也都一面就挂了。其实有很多去年就该去做的事情，一直被拖到了现在。 ","date":"2024-01-04","objectID":"/posts/my-first-post/:1:0","tags":["笔记"],"title":"2024 的一些计划","uri":"/posts/my-first-post/#总结过去"},{"categories":["Notes"],"collections":null,"content":"新的开始 年初总有一些斗志，也想看看今年会不会有些不太一样。但每每新年的目标夹杂的旧年的不甘，其实也还是兜兜转转没什么不一样。 今年希望自己能完成的一些目标，又分短期的和长期的： 100km 每月 跑一次半马 Rust 参与一些开源项目 cmu-db/bustub MIT 6824 DDIA 每月总结 学 Rust 其实一直提不起劲，但经历了一次失败的 C++ 面试后，感觉学习 Rust 还是很有必要的。 ","date":"2024-01-04","objectID":"/posts/my-first-post/:2:0","tags":["笔记"],"title":"2024 的一些计划","uri":"/posts/my-first-post/#新的开始"},{"categories":["Notes"],"collections":null,"content":"结语 出国读研并不像很多人所想的那样光鲜艳丽，实际上留学对我来说，好像真的没有太大区别，倒是厨艺长进了不少，更加能认清自己适合什么。 希望等一月结束的时候，我还有这份动力和干劲再次总结。 ","date":"2024-01-04","objectID":"/posts/my-first-post/:3:0","tags":["笔记"],"title":"2024 的一些计划","uri":"/posts/my-first-post/#结语"}]