類型和Sizeof
Go的類型系統(tǒng)比較簡(jiǎn)單枫慷,從reflect包可以窺得一二:
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
針對(duì)每一種類型,了解每種類型所占的空間對(duì)于編寫以及優(yōu)化Go程序有較大的幫助浪规。以下測(cè)試均在GOARCH=amd64環(huán)境下
type | Sizeof(字節(jié)數(shù)) |
---|---|
Bool | 1 |
Int | 8 |
Uint | 8 |
Uintptr | 8 |
Array | Sizeof(type) * len |
Chan | 8 |
Func | 8 |
Interface | 16 |
Map | 8 |
Ptr(Go指針) | 8 |
Slice | 8 |
String | 16 |
Struct | 需考慮字節(jié)對(duì)齊和填充 |
UnsafePointer | 8 |
Alignment and padding
Go是一個(gè)C家族語(yǔ)言或听,Go的結(jié)構(gòu)體類型基于C語(yǔ)言的結(jié)構(gòu)體演化而來,因此關(guān)于字節(jié)對(duì)齊等概念也是通用的笋婿。通過調(diào)整結(jié)構(gòu)體字段的聲明順序有時(shí)可以優(yōu)化程序性能誉裆,減少內(nèi)存消耗,在一些內(nèi)存受限的嵌入式系統(tǒng)或者操作系統(tǒng)內(nèi)核缸濒,或者你的程序達(dá)到了內(nèi)存上限的場(chǎng)景足丢,只要內(nèi)存是有限的,該項(xiàng)技術(shù)就仍然有用庇配。
關(guān)于內(nèi)存對(duì)齊斩跌,在Go語(yǔ)言規(guī)范中可以找到如下描述:
Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable's type's alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable x:
uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
關(guān)于更詳盡的Go內(nèi)存布局可參考Go語(yǔ)言規(guī)范和go101 Memory Layouts。
關(guān)于以C語(yǔ)言結(jié)構(gòu)體為基礎(chǔ)的字節(jié)對(duì)齊和填充可參考這篇介紹詳盡的文章:
關(guān)于結(jié)構(gòu)體字段的字節(jié)偏移和大小可以使用下面的工具進(jìn)行顯示和字段調(diào)整參考:
另外捞慌,這是一個(gè)優(yōu)化的小例子:
從Go1.5開始耀鸦,有一點(diǎn)需要注意。在一個(gè)結(jié)構(gòu)體結(jié)尾的一個(gè)零長(zhǎng)度的字段(一個(gè)零長(zhǎng)度的數(shù)組或者空結(jié)構(gòu)體)要占一個(gè)字節(jié)啸澡。在padding-is-hard這篇文章可以找到詳細(xì)的討論袖订。下面是一個(gè)簡(jiǎn)單的測(cè)試:
type EmptyEndStruct struct {
Field bool
_ struct{}
}
testT := EmptyEndStruct{true, struct{}{}}
fmt.Println("struct type Sizeof:", unsafe.Sizeof(testT)) // struct type Sizeof: 2
unsafe、reflect和sync/atomic
unsafe包在編譯時(shí)進(jìn)行計(jì)算嗅虏,而reflect在運(yùn)行時(shí)計(jì)算對(duì)齊的長(zhǎng)度
- unsafe.Alignof(t)
- unsafe.Alignof(x.t)
- reflect.TypeOf(t).Align()
- reflect.TypeOf(t).FieldAlign()
另外洛姑,在sync/atomic的文檔底部,詳細(xì)說明了atomic包的64位原子函數(shù)由于字節(jié)對(duì)齊導(dǎo)致的在32位芯片上的使用限制皮服。
Sizeof完整的測(cè)試代碼
boolTemp := true
intTemp := 99
var uintTemp uint = 99
uintptrTemp := (uintptr)(unsafe.Pointer(&intTemp))
arrayTemp := [3]int8{1, 2}
// arrayTemp2 := [3]string{}
chanTemp := make(<-chan string, 100)
ll := func(a int, b string) (int, error) {
return a + 1, errors.New("a error")
}
type interfaceTest interface {
test(int, int) int
}
var interfaceTemp interfaceTest
mapTemp := make(map[string]string)
sliceTemp := []int{1, 2, 3}
stringTemp := "hongyi"
// Alignment and padding
type StructTemp struct {
Field3 bool // 1
Field2 int //8
Field4 uint64 // 8
Field1 string //16
}
structTemp1 := StructTemp{true, 1, 89, "hongyi"}
unsafePointerTemp := unsafe.Pointer(&intTemp)
fmt.Println("bool type Sizeof:", unsafe.Sizeof(boolTemp))
fmt.Println("int type Sizeof:", unsafe.Sizeof(intTemp))
fmt.Println("uint type Sizeof:", unsafe.Sizeof(uintTemp))
fmt.Println("uintptr type Sizeof:", unsafe.Sizeof(uintptrTemp))
fmt.Println("array type Sizeof:", unsafe.Sizeof(arrayTemp))
fmt.Println("chan type Sizeof:", unsafe.Sizeof(chanTemp))
fmt.Println("func type Sizeof:", unsafe.Sizeof(ll))
fmt.Println("interface type Sizeof:", unsafe.Sizeof(interfaceTemp))
fmt.Println("map type Sizeof:", unsafe.Sizeof(mapTemp))
if reflect.TypeOf(&intTemp).Kind() == reflect.Ptr {
fmt.Println("ptr type Sizeof:", unsafe.Sizeof(&intTemp))
}
fmt.Println("slice type Sizeof:", unsafe.Sizeof(&sliceTemp))
fmt.Println("string type Sizeof:", unsafe.Sizeof(stringTemp))
fmt.Println("struct type Sizeof:", unsafe.Sizeof(structTemp1))
fmt.Println("unsafePointer type Sizeof:", unsafe.Sizeof(unsafePointerTemp))
Go結(jié)構(gòu)體內(nèi)存對(duì)齊舉例
type T1 struct {
a int8
// On 64-bit architectures, to make field b
// 8-byte aligned, 7 bytes need to be padded
// here. On 32-bit architectures, to make
// field b 4-byte aligned, 3 bytes need to be
// padded here.
b int64
c int16
// To make the size of type T1 be a multiple
// of the alignment guarantee of T1, on 64-bit
// architectures, 6 bytes need to be padded
// here, and on 32-bit architectures, 2 bytes
// need to be padded here.
}
// The size of T1 is 24 (= 1 + 7 + 8 + 2 + 6)
// bytes on 64-bit architectures and is 16
// (= 1 + 3 + 8 + 2 + 2) on 32-bit architectures.
type T2 struct {
a int8
// To make field c 2-byte aligned, one byte
// needs to be padded here on both 64-bit
// and 32-bit architectures.
c int16
// On 64-bit architectures, to make field b
// 8-byte aligned, 4 bytes need to be padded
// here. On 32-bit architectures, field b is
// already 4-byte aligned, so no bytes need
// to be padded here.
b int64
}
// The size of T2 is 16 (= 1 + 1 + 2 + 4 + 8)
// bytes on 64-bit architectures, and is 12
// (= 1 + 1 + 2 + 8) on 32-bit architectures.