go 高性能数据

坐看云起时 / 2023-08-02 / 原文

字符串高效拼接

常见方式:使用+  使用fmt.Sprintf 

效率比较高的:

  • strings.Builder

func builderConcat(n int, str string) string {
    var builder strings.Builder
    for i := 0; i < n; i++ {
        builder.WriteString(str)
    }
    return builder.String()
}
  • 使用 bytes.Buffer
func bufferConcat(n int, s string) string {
    buf := new(bytes.Buffer)
    for i := 0; i < n; i++ {
        buf.WriteString(s)
    }
    return buf.String()
}
  • 使用 []byte
func byteConcat(n int, str string) string {
    buf := make([]byte, 0)
    for i := 0; i < n; i++ {
        buf = append(buf, str...)
    }
    return string(buf)
}

go官方

// A Builder is used to efficiently build a string using Write methods.

 

数组:

Go 语言中,数组变量属于值类型(value type),因此当一个数组变量被赋值或者传递时,实际上会复制整个数组。例如,将 a 赋值给 b,修改 a 中的元素并不会改变 b 中的元素:

a := [...]int{1, 2, 3} // ... 会自动计算数组长度
b := a
a[0] = 100
fmt.Println(a, b) // [100 2 3] [1 2 3]

对于slice 注意引用导致大内存不能删除

for range

  range 在迭代过程中返回的是迭代值的拷贝,如果每次迭代的元素的内存占用很低,那么 for 和 range 的性能几乎是一样,例如 []int。但是如果迭代的元素内存占用较高,例如一个包含很多属性的 struct 结构体,那么 for 的性能将显著地高于 range,

有时候甚至会有上千倍的性能差异。对于这种场景,建议使用 for,如果使用 range,建议只迭代下标,通过下标访问迭代值,这种使用方式和 for 就没有区别了。

如果想使用 range 同时迭代下标和值,则需要将切片/数组的元素改为指针,才能不影响性能。

func BenchmarkForStruct(b *testing.B) {
    var items [1024]Item
    for i := 0; i < b.N; i++ {
        length := len(items)
        var tmp int
        for k := 0; k < length; k++ {
            tmp = items[k].id
        }
        _ = tmp
    }
}
type Item struct {
    id  int
    val [4096]byte
}

func BenchmarkForStruct(b *testing.B) {
    var items [1024]Item
    for i := 0; i < b.N; i++ {
        length := len(items)
        var tmp int
        for k := 0; k < length; k++ { // len for 方式
            tmp = items[k].id
        }
        _ = tmp
    }
}

func BenchmarkRangeIndexStruct(b *testing.B) {
    var items [1024]Item
    for i := 0; i < b.N; i++ {
        var tmp int
        for k := range items {. // 之rang key index
            tmp = items[k].id
        }
        _ = tmp
    }
}

func BenchmarkRangeStruct(b *testing.B) {
    var items [1024]Item
    for i := 0; i < b.N; i++ {
        var tmp int
        for _, item := range items {
            tmp = item.id
        }
        _ = tmp
    }
}

 Benchmark 的结果:

$ go test -bench=Struct$ .
goos: darwin
goarch: amd64
pkg: example/hpg-range
BenchmarkForStruct-8             3769580               324 ns/op
BenchmarkRangeIndexStruct-8      3597555               330 ns/op
BenchmarkRangeStruct-8              2194            467411 ns/op