点击上方蓝色“飞雪无情”关注我,设个星标,第一时间看文章
看Go Web框架Gin的Release,发现它1.7版本的Release有个增强型的更新:
chore: improve render string performance #2365
好奇心驱使打开了这个PR看了下,发现代码改动非常简单,只有一行:
// 旧代码
_, err = io.WriteString(w, format)
// 新代码
_, err = w.Write([]byte(format))
真的就是这么简单的修改,把原来通过 io.WriteString 的实现换成了通过w.Write直接写入。更改文件可以参考这里 https://github.com/gin-gonic/gin/pull/2365/files。
这里看起来也是挺疑惑的,这样的改动会有提升吗?但是通过作者给的性能测试,是有些许提升的。我们来看看作者的性能测试结果(可以横向滚动)。
before
script:
$ go get github.com/gin-gonic/gin@v1.6.3
$ go test -bench=. -benchtime=10s
result:
BenchmarkGinStatic-8 23306 50344 ns/op 8283 B/op 157 allocs/op
BenchmarkGinGitHubAPI-8 16506 71542 ns/op 10801 B/op 203 allocs/op
BenchmarkGinGplusAPI-8 284379 4061 ns/op 685 B/op 13 allocs/op
BenchmarkGinParseAPI-8 153843 7657 ns/op 1361 B/op 26 allocs/op
after
script:
$ go get github.com/gin-gonic/gin@1bd5a8f
$ go test -bench=. -benchtime=10s
result:
BenchmarkGinStatic-8 246488 46740 ns/op 9915 B/op 314 allocs/op
BenchmarkGinGitHubAPI-8 176192 65348 ns/op 12940 B/op 406 allocs/op
BenchmarkGinGplusAPI-8 3298646 3747 ns/op 811 B/op 26 allocs/op
BenchmarkGinParseAPI-8 1683153 7003 ns/op 1620 B/op 52 allocs/op
每次操作花费的时间没有提升太多,反而每次操作分配的对象翻倍了。
我感觉是不划算,即使想用内存空间换时间,这个换算方式也不划算。
现在我们对比下为什么会出现这种性能测试结果,首先看下这两个写入的实现代码:
// io.WriteString
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(StringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}
旧的使用的是io.WriteString 方法,相比新的改动,多了StringWriter接口的断言,其实我们知道ResponseWriter是一个StringWriter,所以走的是sw.WriteString代码逻辑,不会走下面的w.Write方法。
如果你逐步的跟下去sw.WriteString的逻辑,会发现最终走的是bufio.Writer的WriteString方法,不会增加内存分配,但是这整条调用链很复杂,所以每次操作耗时会久一些。
但是w.Write([]byte(s)) 这个强制转换,是会多一次内存分配的。其实后来我看到了这段代码的再次优化,也验证了我的猜测。
_, err = w.Write(bytesconv.StringToBytes(format))
https://github.com/gin-gonic/gin/pull/2508/files
换成了这样,bytesconv.StringToBytes这是一个零内存分配的转换函数。
func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(
&struct {
string
Cap int
}{s, len(s)},
))
}
利用了unsafe.Pointer实现。
Gin的字符串优化过程是一个非常的经验示例,它也踩过坑,但是最终使用了正确的方法。
在Go SDK标准库中,有很多Write String,或者字符串拼接的函数方法,一定要选最适合自己代码那个。我以前写了几篇关于字符串拼接的文章,可以参考。
—— 精彩推荐 ——
扫码关注
分享、点赞、在看就是最大的支持