go 高性能数据
字符串高效拼接
常见方式:使用+ 使用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
string 和byte 转换
标准转换
go 中 string 与 []byte 的标准转换。
s1 := "hello"
b := []byte(s1)
// []byte to string
s2 := string(b)string2[]byte
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
    var b []byte
    if buf != nil && len(s) <= len(buf) {
        *buf = tmpBuf{}
        b = buf[:len(s)]
    } else {
        b = rawbyteslice(len(s))
    }
    copy(b, s)
    return b
}[]byte2string:
// Buf is a fixed-size buffer for the result,
// it is not nil if the result does not escape.
func slicebytetostring(buf *tmpBuf, b []byte) (str string) {
    l := len(b)
    if l == 0 {
        // Turns out to be a relatively common case.
        // Consider that you want to parse out data between parens in "foo()bar",
        // you find the indices and convert the subslice to string.
        return ""
    }
  // 如果开启了竞态检测 -race
    if raceenabled {
        racereadrangepc(unsafe.Pointer(&b[0]),
            uintptr(l),
            getcallerpc(),
            funcPC(slicebytetostring))
    }
  // 如果开启了 memory sanitizer -msan
    if msanenabled {
        msanread(unsafe.Pointer(&b[0]), uintptr(l))
    }
    if l == 1 {
        stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]])
        stringStructOf(&str).len = 1
        return
    }
    var p unsafe.Pointer
    if buf != nil && len(b) <= len(buf) {
        p = unsafe.Pointer(buf)
    } else {
        p = mallocgc(uintptr(len(b)), nil, false)
    }
    stringStructOf(&str).str = p
    stringStructOf(&str).len = len(b)
  // 拷贝字节数组至字符串
    memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))
    return
}
// 实例 stringStruct 对象
func stringStructOf(sp *string) *stringStruct {
    return (*stringStruct)(unsafe.Pointer(sp))
}强转换
通过 unsafe 和 reflect 包,
func String2Bytes(s string) []byte {
    sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
    bh := reflect.SliceHeader{
        Data: sh.Data,
        Len:  sh.Len,
        Cap:  sh.Len,
    }
    return *(*[]byte)(unsafe.Pointer(&bh))
}
func Bytes2String(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}
  对于标准转换,无论是从 []byte 转 string 还是 string 转 []byte 都会涉及底层数组的拷贝。而强转换是直接替换指针的指向,从而使得 string 和 []byte 指向同一个底层数组。
所以强制转换性能更优!强转换的方式,会给我们的程序带来极大的安全隐患。
a := "hello"
b := String2Bytes(a)
b[0] = 'H'  a 是 string 类型,前面我们讲到它的值是不可修改的。通过强转换将 a 的底层数组赋给 b,而 b 是一个 []byte 类型,它的值是可以修改的,
所以这时对底层数组的值进行修改,将会造成严重的错误(通过 defer + recover 也不能捕获)。
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号