基准测试比time.now()更可靠:前者自动预热、多次采样、排除调度抖动,后者受gc、调度器切换和page cache干扰;真实磁盘性能需用go test -bench=.驱动,并清缓存、关预读、显式sync。

用基准测试代替 time.Now() 测 I/O 耗时
直接用 time.Now() 包裹 os.ReadFile 或 io.Copy 得到的耗时不可靠:Go 调度器切换、GC 暂停、page cache 干扰都会污染结果。真实磁盘性能必须用 go test -bench=. 驱动,它自动预热、多次采样、排除调度抖动。
- 写一个
BenchmarkReadLargeFile,用os.Open+bufio.NewReaderSize(f, 64*1024)读取 100MB 文件,别用os.ReadFile—— 后者内存分配会拖慢 benchmark - 测试前清系统缓存:
sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"(Linux),否则测的是内存带宽而非磁盘 - 关注
ns/op和B/op:前者反映吞吐稳定性,后者高说明缓冲区没复用或频繁 alloc,比如bufio.NewReader忘了设 size,退化成默认 4KB,B/op会飙升
区分场景选对读法,否则 benchmark 失真
benchmark 结果差,大概率不是磁盘慢,而是你测的方式和生产用法不一致。比如拿 os.ReadFile 测 500MB 日志文件,它会一次性申请 500MB 内存,触发 GC STW,测出来是 GC 时间,不是 I/O 时间。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
- 小文件(≤10MB):用
os.ReadFile,benchmark 里直接调,ns/op可信 - 大文件或流式处理:必须用
bufio.NewReaderSize(f, 64*1024)或io.CopyBuffer(dst, src, buf),buf 从sync.Pool获取,否则allocs/op会掩盖真实 I/O 开销 - 按行处理:禁用
bufio.Scanner(默认 64KB 行限制易 panic),改用reader.ReadBytes('\n'),并在 benchmark 中预设超长行测试边界
避免被 page cache 和内核预读带偏
Linux 默认开启 readahead,顺序读大文件时内核会偷偷多读几 MB 到 page cache,导致第一次读快、后续读更快——这测的是缓存性能,不是磁盘。
- 关掉预读干扰:
sudo blockdev --setra 0 /dev/sdX(替换为你的磁盘设备),再跑 benchmark -
os.Open返回的*os.File不带预读,但bufio.NewReader的缓冲行为会模拟类似效果;真正要测裸盘性能,得用f.ReadAt(buf, offset)配合固定偏移+随机跳读 - 如果 benchmark 在 SSD 上跑出 500MB/s,但在机械盘上只有 80MB/s,不是代码问题,是硬件差异——benchmark 只能横向比对同一环境下的不同实现
写入性能要看 Sync 时机,不是 Write 速度
file.Write 很快,因为它只进内核页缓存;真正卡住吞吐的是 file.Sync()。benchmark 若漏掉 Sync,测出来的是虚假高吞吐。
- 写 benchmark 必须包含
file.Sync(),且放在defer file.Close()之前,否则 close 会隐式 flush 但不 guarantee 落盘 - 对比
os.WriteFile和bufio.NewWriterSize(f, 64*1024)+w.Flush():前者每调用一次都 open+write+close+fsync,后者可批量 flush,ns/op差 5–10 倍很常见 - 高频小写场景(如日志),
os.O_APPEND | os.O_SYNC组合是性能杀手,benchmark 里要单独测它,避免误判为“Go 写文件慢”
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










