Skip to content

性能

govalid 使用纯反射——没有代码生成,没有需要预热的缓存。下面的 数据来自 Apple M1 Pro 上的 Go 1.26(go test -bench=. -benchmem):

text
BenchmarkCheck_Simple_Valid-10      745966     1655 ns/op    1136 B/op    29 allocs/op
BenchmarkCheck_Simple_Invalid-10    713349     1644 ns/op    1160 B/op    31 allocs/op
BenchmarkCheck_Rich_Valid-10        262418     4748 ns/op    3216 B/op    73 allocs/op
BenchmarkParseRules-10             2852902      412 ns/op     520 B/op    14 allocs/op

对一个 10 字段的 "rich" 结构体(含手机号、邮箱、长度与范围检查), 单核约 21 万次校验/秒,无需任何额外优化。

各项耗时

操作成本
每个标签的 parseRules~400ns / 14 次分配
每个校验器调用各自数 ns,正则驱动的略高
反射遍历字段主导内存分配
本地化消息渲染每条非平凡模板一次 strings.NewReplacer

热点路径与建议

  • 正则只编译一次。 内置模式都是包级 regexp.MustCompile。 自定义校验器也应遵循同样的模式。
  • 复用结构体类型。 parseRules 在单次 Check 调用内会缓存 按标签的规则切片,但跨调用并不缓存。
  • 请求级别校验。 govalid 足够快,可以在 HTTP handler 里同步调用, 无需"校验缓存"。
  • 避免巨型 list: 规则。 每个候选值都用 fmt.Sprintf("%v", value) 比较。几十个值没问题;上千个就改用 自定义校验器,内部用 map[string]struct{}

并发

Check 可以被并发调用。热点路径上的包级状态(Checkers、各 locale 模板 map)支持并发写——请在程序启动、goroutine 开始命中 Check 之前就把自定义校验器与模板注册好。

go test -race 已经覆盖了共享 struct 与独立 struct 两类并发负载, 都是干净的。

Fuzz 覆盖

仓库提供两个 fuzz 目标:

sh
go test -run=^$ -fuzz=FuzzCheck     -fuzztime=10s ./...
go test -run=^$ -fuzz=FuzzParseRules -fuzztime=10s ./...

两者累计百万级执行,没有产生任何 panic。如果你发现一个会触发 崩溃的输入,请提交 issue 并附上种子。

基于 MIT 协议发布。