Rust 中內存對齊

Rust 中內存對齊

如果你曾讀 CSAPP 一書,那你一定記得 CSAPP 中關於 C 語言內結構體內存對齊的描述。

內存對齊的背景和概念

當 CPU 對數據進行訪問時,每次都是取指定的字節數。例如每次取 4 字節,若當前數據類型爲 int, 且地址位爲 0x00-0x03, 則一次性可以取出該變量;若該變量的地址爲 0x02-0x05, 則會分別取 0x00-0x03, 0x04-0x07 兩個地址,因此會存在一定的性能消耗。

對此,可以採用內存對齊策略來優化存儲,以 C 語言結構體爲例:

struct ss {
 int i;
 char c;
 int j;
 char d;
} test = {
 1,
 'a',
 2,
 'b',
};

int main() {
 printf("%lu", sizeof(test)); // 16
}

首先思考,結構體 ss 中,兩個 int 類型,兩個 char 類型,一共佔據 10 字節。

但是,由於內存對齊策略,實際存儲到內存中,char 類型需要對齊 int 類型的 4 字節,因此結構體 ss 一共佔據 16 字節。

其示例圖如下:

0x00 - 0x03 i0 i1 i2 i3 // 一個 int 變量佔據四字節
0x04 - 0x07 c0          // 一個 char 變量佔據一字節
0x08 - 0x0B j0 j1 j2 j3 // 但由於內存對齊,會對齊 int 類型的 4 字節
0x0C - 0x0F d0

以上,就是內存對齊的背景和概念。

優化 C 語言中結構體

上例中,爲了優化運行耗時,內存對齊策略將原本只有 10 字節的結構體擴展到了 16 字節,此時,可以修改結構體內變量的排序來優化內存:

struct ss2 {
 int i;
 char c;
 char d;
 int j;
} test2 = {
 1,
 'a',
 'b',
 2,
};

int main() {
 printf("%lu", sizeof(test2)); // 12
}

結構體 ss2 生成的變量內存佔據 12 字節,這是因爲,char 類型的變量 d 使用了變量 c 中未使用的部分,其示例如下:

0x00 - 0x03 i0 i1 i2 i3
0x04 - 0x07 c0 d0      
0x08 - 0x0B j0 j1 j2 j3

Rust 中的內存對齊

在 Rust 中,也有結構體的概念,但是在內存對齊的處理上與 C 語言有所不同,例如:

struct A {
 i: i32,
 u1: u8,
 j: i32,
 u2: u8,
}

fn main() {
 println!("{:?}", std::mem::size_of::<A>()); // 12 
}

在 Rust 中, char 類型佔據 4 個字符,因此使用 u8 代替 char.

上述代碼中,i32 類型佔據 4 個字節,u8 類型佔據 1 個字節,兩個 i32 和兩個 u8 一共佔據 10 個字節。

#[repr(C)]
struct B {
 i: i32,
 u1: u8,
 j: i32,
 u2: u8, 
}

fn main() {
 println!("{:?}", std::mem::size_of::<B>()); // 16
}

感悟

這個示例並無太大難度,但是可以留下一些思考:在實際開發中,得益於高度完善的編譯器、解釋器等工具,開發者可以將更多的精力放到對工程代碼的可讀性、正確性上,而沒有必要_過多_擔心某語句、某變量、某聲明是否會對運行時間帶來影響。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7kjiBDkruPWgqmDfg7T--g