性能优化
Created|Updated|系统架构
|Word Count:30|Reading Time:1mins|Post Views:
每个人都应该知道的操作时间
Author: magicliang
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Related Articles

2026-10-09
Go 31:benchmark 为何不能只测准备工作
上篇用有界 fuzz 寻找任务接收与状态序列的反例。这一次要回答另一个容易混淆的问题:数字下降了,究竟是计数函数更快,还是测量时少做了一部分工作?沿用同一个任务服务;累计工程包、SHA-256及独立复跑步骤保留完整源码与原始命令。 准备一次任务,不等于执行一次任务 原先的 CountWords 用 len(strings.Fields(text)) 计算词数。如果基准把 strings.Repeat 生成输入也放进计时循环,数字同时包含造字符串、分配和扫描。服务的 HTTP 查询又是另一条路径:已完成任务的 GET 会读取存储、编码 JSON,不会调用 CountWords。把这两种耗时混在一个基准里,优化统计函数后看到的请求耗时,既不能归因,也不能检验 HTTP 是否受益。 冻结版 Go 1.27.1 的 testing.B.Loop 会在第一次调用时重置计时器,循环结束停止计时。因此两个基准都在循环外造输入和服务实例,在循环内执行要测的操作及最小业务断言: 12345678text := strings.Repeat("Go one two 你好 ", 6...

2026-10-01
容器 34:冷启动时间花在哪一段
拿一条从 docker run 到终端提示的总耗时,无法推断是拉取慢、快照准备慢、进程启动慢还是应用就绪慢。先定义测量的边界与可关联的对象,然后谈数值。 同一只 Pod 在两台节点的“启动耗时”可能相差很大:一台已有内容存储和快照,另一台要从 registry 取层;应用的探针又可能固定等待十秒。只看 kubectl wait 结束时刻,连缓存条件都无法还原,更谈不上给 OCI create 分配多少毫秒。本篇把一次启动拆成控制面可见事件、节点运行时原始事件、应用首次就绪三条时间轴;先标记哪些区间目前还不可观测,再决定能不能比较冷、热启动。 先写实验设计,后写结论 同一 probe-app 固定 image digest、节点 CPU 架构、内核与运行时版本,在专用 VM 上分冷缓存与热缓存两组。时间戳分别落在开始拉取、manifest/layer 验证结束、本机解包与 snapshot 准备结束、OCI create/start、进程首条日志、/ready=200。对同一 ID 记录事件时钟来源,跨组件不能把不同机器的墙钟未经校准就做相减。预先确定预热次数、重复次数、并发、输入...
2026-05-24
回到工程:Linux VM 如何改变性能诊断
前面 16 篇从地址空间到 OOM,逐层拆解了 Linux 虚拟内存子系统的结构和机制。这些知识的价值不只在于读源码——更在于它提供了一种分层诊断习惯:遇到内存相关的性能问题时,先定位层次,再找对象,再看状态转移,最后用指标验证。 核心问题可以压成一句话: Linux VM 的价值不只在源码知识,而在一种分层诊断习惯:先定位层次,再找对象,再看状态转移,最后用指标验证。 系列概念地图 整个系列覆盖的层次和对象: 1234567891011121314151617181920212223用户空间视角 内核视角───────────── ──────────malloc / mmap VMA (vm_area_struct) ↓ 虚拟地址 ↓page fault 页表 (PGD→P4D→PUD→PMD→PTE) ↓ 物理页分配 ↓RSS 增长 ...

2026-10-09
Go 33:GC 预算怎样影响任务服务的堆与延迟
上篇用编译信息和基准报告解释了一次分配变化。可任务服务在处理多个请求时,分配了多少、最后留下多少,以及用了多久,不能靠一个函数的 B/op 推断。本篇在同一个任务服务增加固定 HTTP handler 负载,保留 完整工程包、SHA-256和独立复跑步骤。 分配的字节不是存活堆 TestTaskGCBudget 用两个 worker、容量 1,025 的队列,提交 1,024 个相同请求。每个请求 JSON 体是 1,803 字节,文本里有 512 个单词;HTTP 适配层必须返回 202。这里使用 httptest.NewRequest 和当前服务的原 handler,没有走 TCP。收齐应答之后调用 service.Close(),等待服务拥有的 worker 退出,再逐个按 ID 核对 1,024 条记录均为 SUCCEEDED、Words=512。如果漏了任务,只有速度的数字就不能拿来讨论 GC。202 是入队应答,逐 ID 的终态检查才是这次业务完成的证据。 负载开始前后,测试从 runtime/metrics 读累计分配字节和自动 GC 轮次;分配字节除以从开始提交到...
2026-05-24
Huge Page:TLB 压力和页表开销
上一篇讲了 NUMA 如何让内存访问不再均匀。但即使在单 node 系统上,当工作集足够大时,另一类开销浮现:TLB miss 和页表自身的内存消耗。Huge page 通过增大映射粒度来同时缓解这两个问题。 核心问题可以压成一句话: huge page 用更大的映射粒度减少 TLB 和页表开销,但引入碎片、分配时机和回收复杂度作为代价。 问题从哪里来 标准页大小 4KB。一个 64GB 内存的机器有 1600 万个物理页。如果一个进程映射了 8GB 工作集,对应 200 万个 PTE。 TLB(Translation Lookaside Buffer)是 MMU 的地址翻译缓存。现代 CPU 的 L1 dTLB 通常只有 64-128 个条目,L2 sTLB 有 1024-2048 个条目。200 万个活跃页面对 2048 个 TLB 条目意味着 TLB 覆盖率不足 0.1%——绝大多数访问要走 page table walk。 page table walk 不是免费的。它需要 4-5 次内存访问(每级页表一次),即使有 page walk cache 辅助,在 TLB...

2026-10-01
容器 35:性能差异来自隔离层还是实验条件
“容器性能损耗多少”缺少测量对象和实验条件。应用计算、内存申请、文件系统 copy-up、网络封装都在不同路径上。34只评估启动,不把 steady-state 性能一起平均。本篇先设定可以证伪的对照,再决定哪些数字能归因于容器边界。 把某个程序在宿主上执行十次、在容器里执行十次,得到两列数字后相除,并不能得出“容器比宿主慢 X%”:运行方式可能顺带改变了 CPU 配额、可用核心、进程 UID、文件路径、网络路由与镜像依赖。先用一种不碰磁盘、也不调用网络的短时计算任务建立最小对照;再单独讨论内存、OverlayFS 与网络。每次只改变一个前提,才能知道数字是哪个路径造成的。本篇没有专用 Docker/双 VM,数字一律留空,不用外部基准报告代替自己的实验。 逐项只改变一个条件 同一内核、架构、节点、二进制和输入,先在普通进程跑 bounded_load 的有界计算/内存路径,再在专用容器内跑同样命令;分别记录用户态时间、CPU/内存控制器、可运行 CPU 集与真实的 cgroup 限制。文件系统实验区分宿主相同盘的普通写入、容器可写层首次写入与同一镜像的重复写入;网络实验区分 l...
Contents

