亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

此代碼如何生成內存對齊的切片?

此代碼如何生成內存對齊的切片?

Go
森欄 2023-02-06 14:46:17
我正在嘗試在 Linux 上進行直接 I/O,所以我需要創建內存對齊緩沖區。我復制了一些代碼來做到這一點,但我不明白它是如何工作的:package mainimport (    "fmt"    "golang.org/x/sys/unix"    "unsafe"    "yottaStore/yottaStore-go/src/yfs/test/utils")const (    AlignSize = 4096    BlockSize = 4096)// Looks like dark magicfunc Alignment(block []byte, AlignSize int) int {    return int(uintptr(unsafe.Pointer(&block[0])) & uintptr(AlignSize-1))}func main() {    path := "/path/to/file.txt"    fd, err := unix.Open(path, unix.O_RDONLY|unix.O_DIRECT, 0666)    defer unix.Close(fd)    if err != nil {        panic(err)    }    file := make([]byte, 4096*2)    a := Alignment(file, AlignSize)    offset := 0    if a != 0 {        offset = AlignSize - a    }    file = file[offset : offset+BlockSize]    n, readErr := unix.Pread(fd, file, 0)        if readErr != nil {        panic(readErr)    }    fmt.Println(a, offset, offset+utils.BlockSize, len(file))    fmt.Println("Content is: ", string(file))}我知道我正在生成一個比我需要的大兩倍的切片,然后從中提取一個內存對齊的塊,但是這個Alignment函數對我來說沒有意義。該功能如何Alignment運作?如果我嘗試執行fmt.Println該函數的中間步驟,我會得到不同的結果,為什么?我猜是因為觀察它會改變它的內存對齊方式(就像在量子物理學中一樣:D)
查看完整描述

1 回答

?
滄海一幻覺

TA貢獻1824條經驗 獲得超5個贊

你AlignSize的值為 2 的冪。在二進制表示中,它包含一個1位,后跟全零:


fmt.Printf("%b", AlignSize) // 1000000000000

由 分配的切片make()可能具有或多或少隨機的內存地址,由二進制隨機跟隨的 1 和 0 組成;或者更準確地說是其后備數組的起始地址。


由于您分配了所需大小的兩倍,因此可以保證后備數組將覆蓋一個地址空間,該地址空間的中間某處的地址以與AlignSize的二進制表示形式一樣多的零結尾,并且BlockSize數組中的空間以此開頭. 我們要找到這個地址。


這就是Alignment()函數的作用。它使用 獲取后備數組的起始地址&block[0]。在 Go 中沒有指針運算,所以為了做類似的事情,我們必須將指針轉換為整數(當然有整數運算)。為了做到這一點,我們必須將指針轉換為unsafe.Pointer:所有指針都可以轉換為這種類型,并且unsafe.Pointer可以轉換為uintptr(這是一個足夠大的無符號整數來存儲指針值的未解釋位),在其上-是一個整數——我們可以進行整數運算。


我們對值使用按位與uintptr(AlignSize-1)。由于AlignSize是 2 的冪(包含1一位后跟零),因此減一的數字是二進制表示中全是 1 的數字,與尾隨零的數量一樣多AlignSize??催@個例子:


x := 0b1010101110101010101

fmt.Printf("AlignSize   : %22b\n", AlignSize)

fmt.Printf("AlignSize-1 : %22b\n", AlignSize-1)

fmt.Printf("x           : %22b\n", x)

fmt.Printf("result of & : %22b\n", x&(AlignSize-1))

輸出:


AlignSize   :          1000000000000

AlignSize-1 :           111111111111

x           :    1010101110101010101

result of & :           110101010101

因此,的結果&是偏移量,如果您從中減去該偏移量AlignSize,您將得到一個地址,其尾隨零的數量與AlignSize它本身一樣多:結果與 的倍數“對齊” AlignSize。


所以我們將使用file從 開始的切片部分offset,我們只需要BlockSize:


file = file[offset : offset+BlockSize]

編輯:


查看您嘗試打印步驟的修改代碼:我得到如下輸出:


Pointer:  0xc0000b6000

Unsafe pointer:  0xc0000b6000

Unsafe pointer, uintptr:  824634466304

Unpersand:  0

Cast to int:  0

Return is:  0

Content is: 

注意這里沒有任何改變。簡單地,fmt包使用十六進制表示形式打印指針值,前綴為0x. uintptr值打印為整數,使用十進制表示。這些值是相等的:


fmt.Println(0xc0000b6000, 824634466304) // output: 824634466304 824634466304

另請注意,其余部分是0因為在我的情況下0xc0000b6000已經是 的倍數4096,在二進制中是1100000000000000000100001110000000000000。


編輯#2:


當您用于fmt.Println()調試部分計算時,這可能會改變逃逸分析并可能會改變切片的分配(從堆棧到堆)。這也取決于使用的 Go 版本。不要依賴于你的切片被分配在一個(已經)對齊到AlignSize.


查看完整回答
反對 回復 2023-02-06
  • 1 回答
  • 0 關注
  • 123 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號