我遇到了需要预先添加 10,000 个字符串的情况。这与预置 10 个字符串的最快方法有点不同,但这里还没有回答。
TL;DR 将 10,000 个字符串存储在 []string 中,然后使用 strings.StringBuilder 以相反的顺序组装它。
var strs []string
for i := 0; i < 10000; i++ {
strs = append(strs, "Put your string here")
}
var b strings.Builder
for i := len(strs) - 1; i >= 0; i -= 1 {
b.WriteString(strs[j])
}
+ 的幼稚方法使用了 10 GB 内存 + 1 秒。将大量时间投入到从后面填充缓冲区的自定义前置代码中,将相同的测试用例减少到 10mb + 2 毫秒。但事实证明,只需存储每个字符串,然后使用字符串生成器就可以用更少的代码实现几乎相同的结果。 (14mb 2.4 毫秒)。
以下是基准测试结果:
go test -benchtime=50x -benchmem -bench .
BenchmarkPrepend10kAddition-20 50 971398162 ns/op 9666950277 B/op 20325 allocs/op
BenchmarkPrepend10kPrepender-20 50 1924394 ns/op 10165544 B/op 29913 allocs/op
BenchmarkPrepend10kReorderThenStringBuilder-20 50 2436740 ns/op 14518100 B/op 19952 allocs/op
这是代码(没有 prepender 类,因为这需要更多的工作才能公开):
package main
import (
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
var teststrings []string = []string{
"Test test tses test test sets etsetsetsetsettesestsetet\n",
"TEST TEST TEST TEST TEST EST SET ET SET EST EST SET E TSE TES T STE ET SETT\n",
"ashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n", "asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
"asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
"asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
"asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
"asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
"asdhakjsdhaksjdhdskajhsdajkshdkajshdkjasdkjdhasfhjadshfashdasgfkjashdgfjhsadgfjashgdfkjhasgdfkjhgasdfkjghasdfkjhgasdkjfhgasdkjghfasdkjghfasdhjfgasjkghfsadhjgfaashkajshfksjhdfksjdhfklhjasdfkljhasdfljashdfkljahsdfkjshafdkjashdflkjhsdgkljhsdfklgjhsklfjghksdfjgkldsjfghldksjhfgkdsfjghkdsjhfgkldjsghkldsjfghdklsfgasdjhaksdhjaksdjhasdkjhfksdjhfkjashfdjldhjasfkljashdfkljsahdflkjashdfldhjasfkjsdhf\n",
}
func BenchmarkPrepend10kAddition(b *testing.B) {
for i := 0; i < b.N; i++ {
str := ""
for j := 0; j < 10000; j++ {
str = (strconv.Itoa(j) + teststrings[j%4]) + str
}
_ = string(str)
}
}
func BenchmarkPrepend10kPrepender(b *testing.B) {
for i := 0; i < b.N; i++ {
// Example using custom prepender code not included in this post
prepender := NewStringPrepender()
for j := 0; j < 10000; j++ {
prepender.Prepend(strconv.Itoa(j) + teststrings[j%4])
}
_ = string(prepender.String())
}
}
func BenchmarkPrepend10kReorderThenStringBuilder(b *testing.B) {
for i := 0; i < b.N; i++ {
// What if we have 10,000 strings and assemble them at the end?
var strs []string
for j := 0; j < 10000; j++ {
strs = append(strs, strconv.Itoa(j)+teststrings[j%4])
}
var b strings.Builder
for j := len(strs) - 1; j >= 0; j -= 1 {
b.WriteString(strs[j])
}
_ = string(b.String())
}
}
为了在基准测试中生成字符串,我使用 Itoa + 测试字符串列表。这可能是不必要的,但我添加了它们以确保 []string 中的值不指向相同的内存位置。随机测试字符串会改变连接字符串的长度。