64位與Tagged Pointer

yecat_10 11年前發布 | 7K 次閱讀 IOS MacOS iOS開發 移動開發

在Mac OS X 10.6(Snow Leopard)中開始支持64位,如今最新版本iPhone 5s也開始采用了Arm64架構。在64位化的過程中,其中一個比較關鍵的改進就是,Mac OS 10.7(Lion)和iOS 7的64位環境先后引入了Tagged Pointer。下面就簡單地來介紹一下Tagged Pointer,在介紹Tagged Pointer之前有必要介紹一下指針地址對齊概念和64位環境的一些變化。

指針地址對齊

在32位環境下,如果要讀取一個32位整數,如果這個32位整數在內存地址為0x00000002-0x00000006(僅作舉例,這個地址一般是被系統保留的)的內存上,讀取這個整數會消耗2個CPU周期,而如果這個數在0x00000004-0x00000008的內存上只需要一個CPU周期。為了加快內存的CPU訪問,程序都使用了指針地址對齊概念。指針地址對齊就是指在分配堆中的內存時往往采用偶數倍或以2為指數倍的內存地址作為地址邊界。幾乎所有系統架構,包括Mac OS和iOS,都使用了地址對齊概念。

void *a = malloc(1);
void *b = malloc(3);
NSLog(@"a: %p",a);
NSLog(@"b: %p",b);

運行這段代碼后,我得到了如下結果:

a: 0x8c11e20
b: 0x8c11e30

可以看到,a和b指針的最后4位都是0,雖然a只占用1個字節,但是a和b的地址卻相差16個字節。因為iOS中是以16個字節為內存分配邊界的,或者說iOS的指針地址對齊是以16個字節為對齊邊界的。進一步說,iOS中分配的內存地址最后4位永遠都是0。

64位地址

在不久前發布iPhone5s中采用了Arm64的CPU,同時也支持了64位的App。64位App中指針大小也擴大到64位,就是理論上可以支持最大2 64 字節(達千萬T字節)的內存地址空間。而對于大多數應用來說,這么大的地址空間完全是浪費的。也就是說64位環境下,內存地址的前面很多位一般都是0。

Tagged Pointer

由于指針地址對齊概念和64位超大地址的出現,指針地址僅僅作為內存的地址是比較浪費的,我們可以在指針地址中保存或附加更多的信息。這就引入了Tagged Pointer概念。Tagged Pointer是指那些指針中包含特殊屬性或信息的指針。其中指針對齊概念可以讓我們來標識一個指針是否是Tagged Pointer以及相關類型,64位的地址指針又為我們提供保存額外信息的足夠空間。如今,iOS 7的64位環境和Mac OS 10.7(Lion)中開始引入了Tagged Pointer。

NSNumber的優化

Tagged Pointer一個比較典型的應用就是NSNumber,在64位環境下,對于一般的數字,NSNumber不用再分配內存了。我們看看NSNumber是如何運用Tagged Pointer的:

NSNumber *number3 = @3;
NSNumber *number4 = @4;
NSNumber *number9 = @9;
NSLog(@"number3 pointer is %p", number3);
NSLog(@"number4 pointer is %p", number4);
NSLog(@"number9 pointer is %p", number9);

在64位模擬器中運行后,我得到了如下結果:

number3 pointer is 0xb000000000000032
number4 pointer is 0xb000000000000042
number9 pointer is 0xb000000000000092

可以看出 number3 、 number4 和 number9 的值前4位都是0xb,后4位都是0x2(指針的Tag),中間就是實際的取值,因此,這些NSNumber已經不需要再分配內存(指堆中內存)了,直接可以把實際的值保存到指針中,而無需再去訪問堆中的數據。這無疑提高的內存訪問速度和整體運算速度。

也就是說Tagged Pointer本身就可以表示一個NSNumber了,在64位環境下運行這段代碼:

NSLog(@"0xb000000000000052's class is %@",[(NSNumber*)0xb000000000000052 class]); 
0xb000000000000052's class is __NSCFNumber 

那么如果一個數超過了Tagged Pointer所能表示的范圍,系統會怎么處理?看看這段代碼:

NSNumber *numberBig = @(0x1234567890ABCDEF); NSLog(@"numberBig pointer is %p", numberBig); 

在64位模擬器中運行后,我得到了如下結果:

numberBig pointer is 0x1094026a0 

可以看出 numberBig 指針最后4位都是0,應該是分配在堆中的對象。因此,如果NSNumber超出了Tagged Pointer所能表示的范圍,系統會自動采用分配成對象,可以根據指針的最后4位是否為0來區分。

isa指針優化

查看NSObject類的頭文件,你會發現這段定義:

@interface NSObject <NSObject> { Class isa; } 

所有類都繼承自NSObject,因此每個對象都有一個isa 1 指針指向它所屬的類。在《 ARM64 and You 》文章中指出:

在32位環境下,對象的引用計數都保存在一個外部的表中,而對引用計數的增減操作都要先鎖定這個表,操作完成后才解鎖。這個效率是非常慢的。

而在64位環境下,isa也是64位,實際作為指針部分只用到的其中33位,剩余的部分會運用到Tagged Pointer的概念,其中19位將保存對象的引用計數,這樣對引用計數的操作只需要原子的修改這個指針即可,如果引用計數超出19位,才會將引用計數保存到外部表,而這種情況往往是很少的,因此效率將會大大提高。

參考文獻

 

來自: http://blog.xcodev.com/blog/2013/10/21/tagged-pointer-and-64-bit/

 

 本文由用戶 yecat_10 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!