CL123256 发起了一场关于我们应该选择哪种字符串拼接特化(如果有的话)的讨论。
如果我们专门针对带有转义结果和参数个数小于等于5(N)的拼接进行优化,那么字符串拼接的速度会更快。转义结果意味着我们可以避免传递始终为 nil
的 buf
,并使用 rawstring
代替 rawstringtmp
。已知的 N 意味着我们可以展开循环。
有几种方法(这些并不是全部):
- 为 N=2 进行特化
x+y
。代码较少,似乎最常见,提升最大,但不会提高其他任何拼接(如使用concatstring3
的x+y+z
)。 - 为所有 N 进行特化,但由于性能提升可能不值得。
- 为 N={2,3,4,5} 进行特化。代码较多。
- 为 N={2,3} 进行特化,覆盖了大部分拼接。显著提高了 concat2 和 concat3 的速度。
我从 (1) 开始,因为这是最容易更改的更改,需要较少的更改并带来显著的性能提升。但是为了做出决策,还需要额外的反馈。
以下是 (1) 与未优化的拼接进行比较的结果:
name old time/op new time/op delta
Concat2-8 74.2ns ± 0% 53.5ns ± 1% -27.95% (p=0.000 n=9+15)
Concat3-8 94.9ns ± 0% 94.8ns ± 0% ~ (p=0.082 n=14+15)
Concat2Empty-8 21.4ns ± 1% 14.2ns ± 0% -33.75% (p=0.000 n=15+14)
Concat3Empty-8 23.9ns ± 1% 23.9ns ± 1% ~ (p=0.756 n=15+15)
[Geo mean] 43.6ns 36.2ns -16.88%
对于 (4) 实现与 go tip 的比较:
name old time/op new time/op delta
Concat2-8 74.2ns ± 0% 66.1ns ± 1% -10.95% (p=0.000 n=9+15)
Concat3-8 94.9ns ± 0% 71.9ns ± 1% -24.22% (p=0.000 n=14+15)
Concat2Empty-8 21.4ns ± 1% 21.1ns ± 0% -1.56% (p=0.000 n=15+15)
Concat3Empty-8 23.9ns ± 1% 16.6ns ± 1% -30.63% (p=0.000 n=15+12)
[Geo mean] 43.6ns 35.9ns -17.61%
请注意,这些数字表示开销节省,而不是一般情况中字符串拼接性能提升,因为字符串长度对这些计时有很大影响。
基准测试:
package foo
import "testing"
//go:noinline
func concat2(x, y string) string {
return x + y
}
//go:noinline
func concat3(x, y, z string) string {
return x + y + z
}
var x = "foor"
var y = "2ews"
var z = ""
func BenchmarkConcat2(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = concat2(x, "abc")
}
}
func BenchmarkConcat3(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = concat3(x, "abc", y)
}
}
func BenchmarkConcat2Empty(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = concat2(x, "")
}
}
func BenchmarkConcat3Empty(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = concat3("", x, z)
}
}
我想了解:
- 这种优化是否受欢迎。
- 如果需要,我们需要选择采取哪种方法。
CC @martisch
4条答案
按热度按时间ebdffaop1#
СС @TocarIP@josharian@mvdan
eagi6jfj2#
仅添加来自CL的数据:
2ledvvac3#
Kindly paging @randall77@ianlancetaylor in case of some more ideas too
kulphzqa4#
我并不特别担心额外的代码。如果这意味着为逃逸和非逃逸情况复制
concatstrings
和concatstring[2345]
,那并不是什么大问题。你是否还计划在
concatstrings
中展开循环?此外,在这里我不担心二进制代码大小,但我担心大量复制粘贴的内部循环体的可维护性。