1.平臺原因(移植原因): 不是所有的硬件平臺都能訪問任意地址上的任意數據的;某些硬件平臺只能
創新互聯是專業的宣恩網站建設公司,宣恩接單;提供成都網站設計、做網站,網頁設計,網站設計,建網站,PHP網站建設等專業做網站服務;采用PHP框架,可快速的進行宣恩網站開發網頁制作和功能擴展;專業做搜索引擎喜愛的網站,專業的做網站團隊,希望更多企業前來合作!
在某些地址處取某些特定類型的數據,否則拋出硬件異常。
2.性能原因: 數據結構應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。(如果是對齊的,那么CPU不需要跨越兩個操作字,不是對齊的則需要訪問兩個操作字才能拼接出需要的內存地址)
指針的大小一般是一個機器字的大小
通過Go語言的structlayout工具,可以得出下圖
這些類型在之前的 slice 、 map 、 interface 已經介紹過了,也特意強調過,makehmap函數返回的是一個指針,因此map的對齊為一個機器字.
回頭看看 sync.pool的防止copy的空結構體字段,也是放在第一位,破案了。
計算機結構可能會要求內存地址 進行對齊;也就是說,一個變量的地址是一個因子的倍數,也就是該變量的類型是對齊值。
函數Alignof接受一個表示任何類型變量的表達式作為參數,并以字節為單位返回變量(類型)的對齊值。對于變量x:
這是因為int64在bool之后未對齊。
它是32位對齊的,但不是64位對齊的,因為我們使用的是32位系統,因此實際上只是兩個32位值并排在一起。
● 內存對齊是為了cpu更高效訪問內存中數據
● 結構體對齊依賴類型的大小保證和對齊保證
● 地址對齊保證是:如果類型 t 的對齊保證是 n,那么類型 t 的每個值的地址在運行時必須是 n 的倍數。
● struct內字段如果填充過多,可以嘗試重排,使字段排列更緊密,減少內存浪費
● 零大小字段要避免作為struct最后一個字段,會有內存浪費
● 32位系統上對64位字的原子訪問要保證其是8bytes對齊的;當然如果不必要的 話,還是用加鎖(mutex)的方式更清晰簡單
圖解go-內存對齊
doc-pdf
int大小不論是在64位機還是32位機上都是4字節大小
這個和golang是不一樣的
另c/c++默認是最大字節對齊的,sizeof(A) == 16,但是sizeof(int) == 4,可見是8字節對齊的。
使用 #param pack(1)單字節對齊,sizeof(A) == 12,可見虛表指針大小是8字節。
在開始之前,希望你計算一下 Part1 共占用的大小是多少呢?
輸出結果:
這么一算, Part1 這一個結構體的占用內存大小為 1+4+1+8+1 = 15 個字節。相信有的小伙伴是這么算的,看上去也沒什么毛病
真實情況是怎么樣的呢?我們實際調用看看,如下:
輸出結果:
最終輸出為占用 32 個字節。這與前面所預期的結果完全不一樣。這充分地說明了先前的計算方式是錯誤的。為什么呢?
在這里要提到 “內存對齊” 這一概念,才能夠用正確的姿勢去計算,接下來我們詳細的講講它是什么
有的小伙伴可能會認為內存讀取,就是一個簡單的字節數組擺放
上圖表示一個坑一個蘿卜的內存讀取方式。但實際上 CPU 并不會以一個一個字節去讀取和寫入內存。相反 CPU 讀取內存是 一塊一塊讀取 的,塊的大小可以為 2、4、6、8、16 字節等大小。塊大小我們稱其為 內存訪問粒度 。如下圖:
在樣例中,假設訪問粒度為 4。 CPU 是以每 4 個字節大小的訪問粒度去讀取和寫入內存的。這才是正確的姿勢
另外作為一個工程師,你也很有必要學習這塊知識點哦 :)
在上圖中,假設從 Index 1 開始讀取,將會出現很崩潰的問題。因為它的內存訪問邊界是不對齊的。因此 CPU 會做一些額外的處理工作。如下:
從上述流程可得出,不做 “內存對齊” 是一件有點 "麻煩" 的事。因為它會增加許多耗費時間的動作
而假設做了內存對齊,從 Index 0 開始讀取 4 個字節,只需要讀取一次,也不需要額外的運算。這顯然高效很多,是標準的 空間換時間 做法
在不同平臺上的編譯器都有自己默認的 “對齊系數”,可通過預編譯命令 #pragma pack(n) 進行變更,n 就是代指 “對齊系數”。一般來講,我們常用的平臺的系數如下:
另外要注意,不同硬件平臺占用的大小和對齊值都可能是不一樣的。因此本文的值不是唯一的,調試的時候需按本機的實際情況考慮
輸出結果:
在 Go 中可以調用 unsafe.Alignof 來返回相應類型的對齊系數。通過觀察輸出結果,可得知基本都是 2^n ,最大也不會超過 8。這是因為我手提(64 位)編譯器默認對齊系數是 8,因此最大值不會超過這個數
在上小節中,提到了結構體中的成員變量要做字節對齊。那么想當然身為最終結果的結構體,也是需要做字節對齊的
接下來我們一起分析一下,“它” 到底經歷了些什么,影響了 “預期” 結果
在每個成員變量進行對齊后,根據規則 2,整個結構體本身也要進行字節對齊,因為可發現它可能并不是 2^n ,不是偶數倍。顯然不符合對齊的規則
根據規則 2,可得出對齊值為 8。現在的偏移量為 25,不是 8 的整倍數。因此確定偏移量為 32。對結構體進行對齊
Part1 內存布局:axxx|bbbb|cxxx|xxxx|dddd|dddd|exxx|xxxx
通過本節的分析,可得知先前的 “推算” 為什么錯誤?
是因為實際內存管理并非 “一個蘿卜一個坑” 的思想。而是一塊一塊。通過空間換時間(效率)的思想來完成這塊讀取、寫入。另外也需要兼顧不同平臺的內存操作情況
在上一小節,可得知根據成員變量的類型不同,其結構體的內存會產生對齊等動作。那假設字段順序不同,會不會有什么變化呢?我們一起來試試吧 :-)
輸出結果:
通過結果可以驚喜的發現,只是 “簡單” 對成員變量的字段順序進行改變,就改變了結構體占用大小
接下來我們一起剖析一下 Part2 ,看看它的內部到底和上一位之間有什么區別,才導致了這樣的結果?
符合規則 2,不需要額外對齊
Part2 內存布局:ecax|bbbb|dddd|dddd
通過對比 Part1 和 Part2 的內存布局,你會發現兩者有很大的不同。如下:
仔細一看, Part1 存在許多 Padding。顯然它占據了不少空間,那么 Padding 是怎么出現的呢?
通過本文的介紹,可得知是由于不同類型導致需要進行字節對齊,以此保證內存的訪問邊界
那么也不難理解,為什么 調整結構體內成員變量的字段順序 就能達到縮小結構體占用大小的疑問了,是因為巧妙地減少了 Padding 的存在。讓它們更 “緊湊” 了。這一點對于加深 Go 的內存布局印象和大對象的優化非常有幫
用golang解析二進制協議時,其實沒必要管結構體的字段的對齊規則,何況語言規范也沒有規定如何對齊,也就是沒有規則。用encoding/binary.Read函數直接讀入struct里就行,struct就像c那樣寫
type Data struct {
Size, MsgType uint16
Sequence uint32
// ...
}
golang編譯器加不加padding,Read都能正常工作,runtime知道Data的布局的,不像C直接做cast所以要知道怎樣對齊。
用unsafe.Alignof可以知道每個field的對齊長度,但沒必要用到。
package main
/*
#include stdint.h
#pragma pack(push, 1)
typedef struct {
uint16_t size;
uint16_t msgtype;
uint32_t sequnce;
uint8_t data1;
uint32_t data2;
uint16_t data3;
} mydata;
#pragma pack(pop)
mydata foo = {
1, 2, 3, 4, 5, 6,
};
int size() {
return sizeof(mydata);
}
*/
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"unsafe"
)
func main() {
bs := C.GoBytes(unsafe.Pointer(C.foo), C.size())
fmt.Printf("len %d data %v\n", len(bs), bs)
var data struct {
Size, Msytype uint16
Sequence uint32
Data1 uint8
Data2 uint32
Data3 uint16
}
err := binary.Read(bytes.NewReader(bs), binary.LittleEndian, data)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%v\n", data) // {1 2 3 4 5 6}
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, data)
fmt.Printf("%d %v\n", buf.Len(), buf.Bytes()) // 15 [0 1 0 2 0 0 0 3 4 0 0 0 5 0 6]
}
網頁標題:go語言1字節對齊 go語言int默認幾字節
分享URL:http://vcdvsql.cn/article40/doooiho.html
成都網站建設公司_創新互聯,為您提供網站制作、品牌網站設計、網站收錄、網站內鏈、虛擬主機、動態網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯