doushuichong2589 2019-08-09 22:55
浏览 77
已采纳

为什么在测试程序中添加string时,strings.Builder比fmt.Sprint慢?

I'm trying to optimize speed of a String() function of struct FmtC. Based on following benchmark.

  • If I pre-allocate 1024 bytes with strings.Builder, it is slower (624 ns) than fmt.Sprint (437 ns).
  • If I pre-allocate 32 bytes, strings.Builder is faster (74.8 ns) but it is not useful if FmtC contains more member fields.
  • If I pre-allocate 1024 bytes by slice append method it is slowest (2337 ns).

go version: go1.12.7 linux/amd64.

go test -v -bench=. -benchmem

BenchmarkFmtSprint_32-2                  3000000           435 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_32-2            20000000            74.8 ns/op        32 B/op          1 allocs/op
BenchmarkSliceAppendString_32-2         10000000           213 ns/op         160 B/op          3 allocs/op
BenchmarkSliceAppendBytes_32-2          10000000           143 ns/op          96 B/op          2 allocs/op
BenchmarkFmtSprint_128-2                 3000000           437 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_128-2           10000000           125 ns/op         128 B/op          1 allocs/op
BenchmarkSliceAppendString_128-2         3000000           478 ns/op         544 B/op          3 allocs/op
BenchmarkSliceAppendBytes_128-2          5000000           312 ns/op         384 B/op          2 allocs/op
BenchmarkFmtSprint_1024-2                3000000           437 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_1024-2           2000000           624 ns/op        1024 B/op          1 allocs/op
BenchmarkSliceAppendString_1024-2         500000          2337 ns/op        3456 B/op          3 allocs/op
BenchmarkSliceAppendBytes_1024-2         5000000           310 ns/op         384 B/op          2 allocs/op

main.go

package main

import (
        "fmt"
        "strconv"
        "strings"
)

type FmtC struct {
        Field1 uint32
        Field2 [5]byte
}

var preAllocatedSize = 1024

func (c FmtC) FmtSprint() string {
        return fmt.Sprint("{Field1:", c.Field1, " Field2:",
                string(c.Field2[:]), "}")
}
func (c FmtC) StringsBuilder() string {
        var s strings.Builder
        s.Grow(preAllocatedSize) // output length width less than 1024 bytes
        s.WriteString("{Field1:")
        s.WriteString(strconv.FormatUint(uint64(c.Field1), 10))
        s.WriteString(" Field2:")
        s.Write(c.Field2[:])
        s.WriteString("}")
        return s.String()
}
func (c FmtC) SliceAppendString() string {
        s := make([]byte, preAllocatedSize)
        s = append(s, "{Field1:"...)
        s = strconv.AppendUint(s, uint64(c.Field1), 10)
        s = append(s, " Field2:"...)
        s = append(s, c.Field2[:]...)
        s = append(s, "}"...)
        return string(s)
}
func (c FmtC) SliceAppendBytes() []byte {
        s := make([]byte, preAllocatedSize)
        s = append(s, "{Field1:"...)
        s = strconv.AppendUint(s, uint64(c.Field1), 10)
        s = append(s, " Field2:"...)
        s = append(s, c.Field2[:]...)
        s = append(s, "}"...)
        return s
}
func main() {
}

main_test.go

package main

import (
        "testing"
)

var c = FmtC{5, [5]byte{'h', 'e', 'l', 'l', 'o'}}

func BenchmarkFmtSprint_32(b *testing.B) {
        preAllocatedSize = 32
        for n := 0; n < b.N; n++ {
                c.FmtSprint()
        }
        b.StopTimer()
}
func BenchmarkStringsBuilder_32(b *testing.B) {
        preAllocatedSize = 32
        for n := 0; n < b.N; n++ {
                c.StringsBuilder()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendString_32(b *testing.B) {
        preAllocatedSize = 32
        for n := 0; n < b.N; n++ {
                c.SliceAppendString()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendBytes_32(b *testing.B) {
        preAllocatedSize = 32
        for n := 0; n < b.N; n++ {
                c.SliceAppendBytes()
        }
        b.StopTimer()
}
func BenchmarkFmtSprint_128(b *testing.B) {
        preAllocatedSize = 128
        for n := 0; n < b.N; n++ {
                c.FmtSprint()
        }
        b.StopTimer()
}
func BenchmarkStringsBuilder_128(b *testing.B) {
        preAllocatedSize = 128
        for n := 0; n < b.N; n++ {
                c.StringsBuilder()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendString_128(b *testing.B) {
        preAllocatedSize = 128
        for n := 0; n < b.N; n++ {
                c.SliceAppendString()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendBytes_128(b *testing.B) {
        preAllocatedSize = 128
        for n := 0; n < b.N; n++ {
                c.SliceAppendBytes()
        }
        b.StopTimer()
}
func BenchmarkFmtSprint_1024(b *testing.B) {
        preAllocatedSize = 1024
        for n := 0; n < b.N; n++ {
                c.FmtSprint()
        }
        b.StopTimer()
}
func BenchmarkStringsBuilder_1024(b *testing.B) {
        preAllocatedSize = 1024
        for n := 0; n < b.N; n++ {
                c.StringsBuilder()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendString_1024(b *testing.B) {
        preAllocatedSize = 1024
        for n := 0; n < b.N; n++ {
                c.SliceAppendString()
        }
        b.StopTimer()
}
func BenchmarkSliceAppendBytes_1024(b *testing.B) {
        preAllocatedSize = 128
        for n := 0; n < b.N; n++ {
                c.SliceAppendBytes()
        }
        b.StopTimer()
}

(Edit:) After changing the mistake of s := make([]byte, preAllocatedSize) to s := make([]byte, 0, preAllocatedSize), the result is: only pre-allocated 1024 bytes version is slower than fmt.Sprint in this simple test.

BenchmarkFmtSprint_32-2                  3000000           432 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_32-2            20000000            75.2 ns/op        32 B/op          1 allocs/op
BenchmarkSliceAppendString_32-2         20000000           112 ns/op          64 B/op          2 allocs/op
BenchmarkSliceAppendBytes_32-2          20000000            64.2 ns/op        32 B/op          1 allocs/op
BenchmarkFmtSprint_128-2                 3000000           437 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_128-2           10000000           123 ns/op         128 B/op          1 allocs/op
BenchmarkSliceAppendString_128-2        10000000           162 ns/op         160 B/op          2 allocs/op
BenchmarkSliceAppendBytes_128-2         20000000           110 ns/op         128 B/op          1 allocs/op
BenchmarkFmtSprint_1024-2                3000000           429 ns/op          64 B/op          4 allocs/op
BenchmarkStringsBuilder_1024-2           2000000           626 ns/op        1024 B/op          1 allocs/op
BenchmarkSliceAppendString_1024-2        2000000           653 ns/op        1056 B/op          2 allocs/op
BenchmarkSliceAppendBytes_1024-2        10000000           110 ns/op         128 B/op          1 allocs/op
  • 写回答

1条回答

      报告相同问题?

      相关推荐 更多相似问题

      悬赏问题

      • ¥30 AndroidBench&eMMC内存测试速度&Android
      • ¥15 W10 文件共享失败 怎么解决
      • ¥20 b站私信完整导出的方法
      • ¥15 边界图中的x、y轴解释
      • ¥30 如何用matlab求解复杂方程
      • ¥15 VUE TS中怎么调用第三方js
      • ¥15 Activiti如何将值传入textValue
      • ¥20 在linux里安装lummps时 在make mpi里找不到rpc
      • ¥25 python先到先得调度和轮询调度
      • ¥15 SDK Manager闪退