go 运行时,命令/编译:字符串拼接的性能专用程序集,

yrdbyhpb  于 3个月前  发布在  Go
关注(0)|答案(4)|浏览(58)

CL123256 发起了一场关于我们应该选择哪种字符串拼接特化(如果有的话)的讨论。
如果我们专门针对带有转义结果和参数个数小于等于5(N)的拼接进行优化,那么字符串拼接的速度会更快。转义结果意味着我们可以避免传递始终为 nilbuf ,并使用 rawstring 代替 rawstringtmp 。已知的 N 意味着我们可以展开循环。
有几种方法(这些并不是全部):

  1. 为 N=2 进行特化 x+y 。代码较少,似乎最常见,提升最大,但不会提高其他任何拼接(如使用 concatstring3x+y+z )。
  2. 为所有 N 进行特化,但由于性能提升可能不值得。
  3. 为 N={2,3,4,5} 进行特化。代码较多。
  4. 为 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)
	}
}

我想了解:

  1. 这种优化是否受欢迎。
  2. 如果需要,我们需要选择采取哪种方法。
    CC @martisch
ebdffaop

ebdffaop1#

СС @TocarIP@josharian@mvdan

eagi6jfj

eagi6jfj2#

仅添加来自CL的数据:

For go executable, there are:

	501 concatstring2
	194 concatstring3
	55  concatstring4
2ledvvac

2ledvvac3#

Kindly paging @randall77@ianlancetaylor in case of some more ideas too

kulphzqa

kulphzqa4#

我并不特别担心额外的代码。如果这意味着为逃逸和非逃逸情况复制 concatstringsconcatstring[2345],那并不是什么大问题。
你是否还计划在 concatstrings 中展开循环?此外,在这里我不担心二进制代码大小,但我担心大量复制粘贴的内部循环体的可维护性。

相关问题