package allocations
import "testing"
func buildOrginalData() []int {
s := make([]int, 1024)
for i := range s {
s[i] = i
}
return s
}
func check(v int) bool {
return v%2 == 0
}
func FilterOneAllocation(data []int) []int {
var r = make([]int, 0, len(data))
for _, v := range data {
if check(v) {
r = append(r, v)
}
}
return r
}
func FilterNoAllocations(data []int) []int {
var k = 0
for i, v := range data {
if check(v) {
data[i] = data[k]
data[k] = v
k++
}
}
return data[:k]
}
func Benchmark_FilterOneAllocation(b *testing.B) {
data := buildOrginalData()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = FilterOneAllocation(data)
}
}
func Benchmark_FilterNoAllocations(b *testing.B) {
data := buildOrginalData()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = FilterNoAllocations(data)
}
}
// Benchmark_FilterOneAllocation-4 7263 ns/op 8192 B/op 1 allocs/op
// Benchmark_FilterNoAllocations-4 903.3 ns/op 0 B/op 0 allocs/op
go build -gcflags=-m copy/copy.go
# command-line-arguments
copy/copy.go:5:6: can inline arrayFibonacci
copy/copy.go:17:6: can inline sliceFibonacci
copy/copy.go:18:11: make([]int, capacity) escapes to heap
arrayFibonacci() 和 sliceFibonacci() 函数均可内联。
sliceFibonacci() 函数中定义的局部变量切片逃逸到了堆。
多大的变量才算是小变量呢?对 Go 编译器而言,超过一定大小的局部变量将逃逸到堆上,不同的 Go 版本的大小限制可能不一样。
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
hdr.Data = uintptr(unsafe.Pointer(p))
hdr.Len = n
文档里的说明:
the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing unsafe.
However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value.
func StringToSliceByte(s string) []byte {
var b []byte
l := len(s)
p := (*reflect.SliceHeader)(unsafe.Pointer(&b))
p.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
p.Len = l
p.Cap = l
return b
}
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
´
func StringToSliceByte(s string) []byte {
var b []byte
l := len(s)
p := (*SliceHeader)(unsafe.Pointer(&b))
p.Data = (*StringHeader)(unsafe.Pointer(&s)).Data
p.Len = l
p.Cap = l
return b
}
type StringHeader struct {
Data unsafe.Pointer
Len int
}
type SliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
func StringToSliceByte(s string) []byte {
var b []byte
l := len(s)
p := (*SliceHeader)(unsafe.Pointer(&b))
p.Data = (*StringHeader)(unsafe.Pointer(&s)).Data
p.Len = l
p.Cap = l
return b
}
参考资料
(usage::切片的性能和陷阱)
数组变量属于值类型,当一个数组变量被赋值和传递的时候,会复制整个数组。
Copy
Delete
Delete(GC)
Pop
陷阱
copy
替代re-slice
。for 和 range 的性能比较
(usage::计算结构体占用空间 )
unsafe
标准库提供了Alignof
方法,能返回一个类型的对齐值,也能叫做对齐系数或者对齐倍数unsafe.Alignof(x)
至少为 1。unsafe.Alignof(x.f)
,unsafe.Alignof(x)
等于其中的最大值。unsafe.Alignof(x)
等于构成数组的元素类型的对齐倍数。如何图形化看 pading
每个类型对齐要求
空 struct
对时间解析的优化
尽量避免分配
Save memory and reduce allocations by combining memory blocks
Use value cache pool to avoid some allocations
(usage::逃逸)
使用数组进行拷贝传递,比使用切片要好。
切片因为要返回到函数外部,所以发生了逃逸,需要在堆上申请内存空间
。(usage:: 将 string 改成[]byte)
reflect.SliceHeader 与 reflect.StringHeader
uintptr
,虽然有一个 ptr 后缀,但是它本质上还是一个整型
,并不是指针,也就是说,它并不会持有它指向的数据,所以数据可能会被 GC 回收验证刚才的想法
如果想验证是否存在特殊处理,能使用自定义的类型反向验证一下:
既然 uintptr 不是指针,那么改用 unsafe.Pointer,如此数据就不会被 GC 回收了:
更好的写法
type/golang #public