package main
// A ...
type A struct {
A1 int32
A2 int32
}
// B ...
type B struct {
B1 int
B2 int
}
func conv(sliceA []*A) []*B {
var (
tempSliceB = make([]B, len(sliceA))
sliceB = make([]*B, len(sliceA))
)
for i := 0; i < len(sliceA); i++ {
tempSliceB[i].B1 = int(sliceA[i].A1)
tempSliceB[i].B2 = int(sliceA[i].A2)
sliceB[i] = &tempSliceB[i]
}
return sliceB
}
func main() {
var sliceA = []*A{{A1: 0}, {A1: 1}, {A1: 2}}
conv(sliceA)
}
内存对齐
结构体字段合理排序
package main
import (
"fmt"
"unsafe"
)
type A struct {
a int32
b int32
c int64
}
type B struct {
a int32
c int64
b int32
}
func main() {
fmt.Printf("size of A is %d\n", unsafe.Sizeof(A{})) // size of A is 16
fmt.Printf("size of B is %d\n", unsafe.Sizeof(B{})) // size of B is 24
}
// https://github.com/golang/go/blob/e9e0d1ef704c4bba3927522be86937164a61100c/src/runtime/map.go#L150-L150
// A bucket for a Go map.
type bmap struct {
// tophash generally contains the top byte of the hash value
// for each key in this bucket. If tophash[0] < minTopHash,
// tophash[0] is a bucket evacuation state instead.
tophash [bucketCnt]uint8
// Followed by bucketCnt keys and then bucketCnt elems.
// NOTE: packing all the keys together and then all the elems together makes the
// code a bit more complicated than alternating key/elem/key/elem/... but it allows
// us to eliminate padding which would be needed for, e.g., map[int64]int8.
// Followed by an overflow pointer.
}
通过显式填充避免 false sharing:
// https://github.com/golang/go/blob/e9e0d1ef704c4bba3927522be86937164a61100c/src/sync/pool.go#L70-L70
type poolLocal struct {
poolLocalInternal
// Prevents false sharing on widespread platforms with
// 128 mod (cache line size) = 0 .
pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
Go 编译器会在编译期自动把适合条件的函数内联到调用函数中,以减少函数调用返回时参数传递入栈出栈等性能耗损。
当被调用的函数很长时,可以进行拆分,以使部分比较常命中的逻辑分支内联到调用函数中。
比如 sync.Once 里面的的这段代码
// https://github.com/golang/go/blob/e9e0d1ef704c4bba3927522be86937164a61100c/src/sync/once.go#L58-L58
func (o *Once) Do(f func ()) {
// Note: Here is an incorrect implementation of Do:
//
// if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
// f()
// }
//
// Do guarantees that when it returns, f has finished.
// This implementation would not implement that guarantee:
// given two simultaneous calls, the winner of the cas would
// call f, and the second would return immediately, without
// waiting for the first's call to f to complete.
// This is why the slow path falls back to a mutex, and why
// the atomic.StoreUint32 must be delayed until after f returns.
if atomic.LoadUint32(&o.done) == 0 {
// Outlined slow-path to allow inlining of the fast-path.Carlo Alberto Ferraris, 3 years ago: • sync: allow inlining the Once.Do fast path
o.doSlow(f)
}
}
func (o *Once) doSlow(f func ()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}