做者:李德php
在PHP源碼中,有一段對small內存規格的計算,具體在Zend/zend_alloc.c的zend_mm_small_size_to_bin函數中,其目的是傳入一個size,計算對應的規格。見代碼:函數
if (size <= 64) { /* we need to support size == 0 ... */ return (size - !!size) >> 3; } else { t1 = size - 1; t2 = zend_mm_small_size_to_bit(t1) - 3; t1 = t1 >> t2; t2 = t2 - 3; t2 = t2 << 2; return (int)(t1 + t2); }
能夠看出,這段代碼中分爲兩種狀況進行討論:code
下面咱們對這兩種狀況詳細分析下。內存
ZEND_MM_BINS_INFO
這個宏知道當size小於等於64的狀況是一個等差數列,遞增8,因此使用size除以8就行(源碼中是右移3位)size >> 3
(size - 1) >> 3
-1
的處理是!!size
,當size爲0的狀況!!0 = 0
。因此當size爲0的狀況就把-1
轉換成了-0
,最終有了源碼中的表達式 (size - !!size) >> 3
t1 = size - 1; t2 = zend_mm_small_size_to_bit(t1) - 3; t1 = t1 >> t2; t2 = t2 - 3; t2 = t2 << 2; return (int)(t1 + t2);
/* num, size, count, pages */ #define ZEND_MM_BINS_INFO(_, x, y) \ _( 0, 8, 512, 1, x, y) \ _( 1, 16, 256, 1, x, y) \ _( 2, 24, 170, 1, x, y) \ _( 3, 32, 128, 1, x, y) \ _( 4, 40, 102, 1, x, y) \ _( 5, 48, 85, 1, x, y) \ _( 6, 56, 73, 1, x, y) \ _( 7, 64, 64, 1, x, y) \ _( 8, 80, 51, 1, x, y) \ _( 9, 96, 42, 1, x, y) \ _(10, 112, 36, 1, x, y) \ _(11, 128, 32, 1, x, y) \ _(12, 160, 25, 1, x, y) \ _(13, 192, 21, 1, x, y) \ _(14, 224, 18, 1, x, y) \ _(15, 256, 16, 1, x, y) \ _(16, 320, 64, 5, x, y) \ _(17, 384, 32, 3, x, y) \ _(18, 448, 9, 1, x, y) \ _(19, 512, 8, 1, x, y) \ _(20, 640, 32, 5, x, y) \ _(21, 768, 16, 3, x, y) \ _(22, 896, 9, 2, x, y) \ _(23, 1024, 8, 2, x, y) \ _(24, 1280, 16, 5, x, y) \ _(25, 1536, 8, 3, x, y) \ _(26, 1792, 16, 7, x, y) \ _(27, 2048, 8, 4, x, y) \ _(28, 2560, 8, 5, x, y) \ _(29, 3072, 4, 3, x, y) #endif /* ZEND_ALLOC_SIZES_H */
size = size - 1;
這個是邊界狀況,跟前面同樣,後面出現的size暫且都認爲已近減一了ZEND_MM_BINS_INFO
中找到對應的bin_numZEND_MM_BINS_INFO
得知後續的增長4個爲一組,分別爲2^4, 2^5, 2^6...
有了這個分組信息的話,咱們要找siez對應的bin_num源碼
64 | 100 0000 80 | 101 0000 96 | 110 0000 112 | 111 0000 128 | 1000 0000 160 | 1010 0000 192 | 1100 0000 224 | 1110 0000 256 | 1 0000 0000 320 | 1 0100 0000 384 | 1 1000 0000 448 | 1 1100 0000 .....
1
的位數1
的位數1
的位數int n = 16; if (size <= 0x00ff) {n -= 8; size = size << 8;} if (size <= 0x0fff) {n -= 4; size = size << 4;} if (size <= 0x3fff) {n -= 2; size = size << 2;} if (size <= 0x7fff) {n -= 1;} return n;
(size-64)/16 (size-128)/32 (size-256)/64
七、八、9...
,那麼咱們如今來看看這樣的數據怎麼計算組內的偏移量(size - 2^4 * 4) / 16 = size / 2^4 - 4 (size - 2^5 * 4) / 32 = size / 2^5 - 4 (size - 2^6 * 4) / 64 = szie / 2^6 - 4
七、八、9
減去3
獲得四、五、6
,這樣咱們就能夠根據它在哪一組的信息獲得當前組的差值(1六、3二、64...)(65-64) / 2^4 = 0
1
的位數減去6
就能夠獲得分組信息了八、十二、16...
它也是一個等差數列,就是4n+4
咱們在看看size=65的那個例子數學
4*1 + 4 = 8
8 + 0 = 8
咱們再看一個size=129的例子it
偏移量是class
1
的位數爲8(129 - 1 - 32 * 4) / 64 = 0
4 * 2 + 4 = 12
12 + 0 = 0
size=193二進制
偏移量是方法
1
的位數爲8(193 - 1 - 32 * 4) / 64 = 2
4 * 2 + 4 = 12
12 + 2 = 14
size=1793
偏移量是
1
的位數爲11(1793 - 1 - 256 * 4) / 256 = 3
4 * 5 + 4 = 24
24 + 3 = 27
1 t1 = size - 1; 2 t2 = zend_mm_small_size_to_bit(t1) - 3; 3 t1 = t1 >> t2; 4 t2 = t2 - 3; 5 t2 = t2 << 2; 6 return (int)(t1 + t2);
t1 = size - 1;
t2 = zend_mm_small_size_to_bit(t1) - 3;
zend_mm_small_size_to_bit
這個函數,咱們看看這個函數/* higher set bit number (0->N/A, 1->1, 2->2, 4->3, 8->4, 127->7, 128->8 etc) */ int n = 16; if (size <= 0x00ff) {n -= 8; size = size << 8;} if (size <= 0x0fff) {n -= 4; size = size << 4;} if (size <= 0x3fff) {n -= 2; size = size << 2;} if (size <= 0x7fff) {n -= 1;} return n;
咱們經過zend_mm_small_size_to_bit
這個函數獲取了size二進制中最高位1的位數,那麼這個 -3
是什麼神奇的操做呢
(size - 2^4 * 4) / 16 = size / 2^4 - 4 (size - 2^5 * 4) / 32 = size / 2^5 - 4 (size - 2^6 * 4) / 64 = szie / 2^6 - 4
-3
的操做來獲取相應的 四、五、6... t1 = t1 >> t2;
binnum = (4n + 4) + (size / 2^n - 4) binnum = 4n + size / 2^n
t2 = t2 - 3;
t2 = t2 << 2;
binnum = (4n + 4) + (size / 2^n - 4) binnum = 4n + size / 2^n
4n
對吧,左移兩位就是乘以4return (int)(t1 + t2);