首頁 >後端開發 >php教程 >php中small記憶體規格的計算(程式碼範例)

php中small記憶體規格的計算(程式碼範例)

不言
不言轉載
2019-02-25 10:01:162199瀏覽

這篇文章帶給大家的內容是關於php中small記憶體規格的計算(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

small記憶體分配計算bin_num

在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);
}

可以看出,這段程式碼中分為兩種情況來討論:

  • 1、size小於等於64的情況;

  • 2、size大於64的情況;

在下面我們對這兩種情況詳細分析下。

對於size小於等於64的情況

  • ZEND_MM_BINS_INFO這個巨集知道當size小於等於64的情況是等差數列,遞增8,所以使用size除以8就行(源碼中是右移3位)size >> 3</pre> <li><p>但是要考慮到size等於8、 16等的情況,所以為<code>(size - 1) >> 3

  • 然後要考慮到為0的情況,所以原始碼中對於-1的處理是!!size,當size為0的情況!!0 = 0。所以當size為0的情況就把-1轉換成了-0,最後有了原始碼中的表達式(size - !!size) >&gt ; 3

  • 對於size大於64的情況

    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);

    初始懵逼

    • ##初看這個程式碼,容易一臉懵逼,這些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_num

      ##由
    • ZEND_MM_BINS_INFO
    • 得知後續的增加4個為一組,分別為

      2^4, 2^5, 2^6...

    #有了這個分組資訊的話,我們要找siez對應的bin_num
    • 找到這個size屬於哪一組
      • 並且size在群組內的偏移量是多少
      • 計算組的起始位置
      • 那現在問題轉換成了上面3個小問題,我們一個一個來解決
    • 找到size屬於哪一組
    最簡單的辦法就是比大小是吧,可以用if...else 來一個一個比,但是顯然php源碼不是這樣幹的,那我們還有什麼其它的辦法呢?
    • 我們看十進位看不出來什麼名堂,就把這些值轉成二進位看看吧
    • 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
    • 的位元數
    • 下面給出php原始碼的解法,這裡暫時不對其解析,只要知道它傳回的是二進位中最高位的

      1
    • 的位數
    • <pre class="brush:php;toolbar:false">int n = 16; if (size &lt;= 0x00ff) {n -= 8; size = size &lt;&lt; 8;} if (size &lt;= 0x0fff) {n -= 4; size = size &lt;&lt; 4;} if (size &lt;= 0x3fff) {n -= 2; size = size &lt;&lt; 2;} if (size &lt;= 0x7fff) {n -= 1;} return n;</pre>

    • #假設我們申請的size為65,那麼這裡的n回傳7
    • 計算size在群組內的偏移量

    這個簡單,直接用size減去每組的起始siez大小然後除以目前群組內的差值(16、32、64...)即可,也就是
    (size-64)/16 (size-128)/32 (size-256)/64
    • #現在來看看上一個步驟中的回傳的值,每個組分別是

      7、8、9...
    • ,那我們現在來看看這樣的資料怎麼計算組內的偏移量
    • <pre class="brush:php;toolbar:false">(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</pre>

    • 那是不是可以用
    7、8、9
      減去
    • 3

      得到4、5、6,這樣我們就可以根據它在哪一組的資訊來得到目前組的差值(16、32、64...) 當size為65時,偏移量是不是就是

    • (64-64) / 2^4 = 0

      計算群組的起始位置

    現在我們有了偏移量的信息,假定我們分組是1、2、3
    • 那是不是就是用最高位的

      1
    • 的位數減去
    • 6

      就可以得到分組資訊了得到分組資訊之後,怎麼知道每組的起始位置呢

    • ##我們知道起始位置分別是

      8、12、16...

      它也是一個等差數列,就是
    • 4n 4
    • 我們在看看size=65的那個例子

      • 计算的偏移量是0

      • 计算的起始位置是4*1 + 4 = 8

      • 所以当size=65的bin_num就是起始位置加上偏移量 8 + 0 = 8

    • 我们再看一个size=129的例子

      • 二进制中最高位的1的位数为8

      • 然后用8减去3得到5

      • (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

    代码分析

    php实现代码

    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;

    • 是为了考虑size为64、128...这些边界情况

    第二行

    • 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;
    • 看注释我们就知道这个函数是用来返回当前size二进制中最高位1的位数,具体的做法呢其实就是二分法

    • 我们通过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
      • 这里获取二进制的位数是7、8、9...通过 -3 的操作来获取相应的 4、5、6...

      • 上问的分析中提到,我们计算size在组内的偏移量的公式

    第三行

    • t1 = t1 >> t2;</pre> <li><p>把t1右移t2位,这又是什么神奇的操作?</p></li> <li><p>这里我们把最后计算bin_num的数学公式给写出来,它是等于每组的起始位置加上组内的偏移量</p></li> <pre class="brush:php;toolbar:false">binnum = (4n + 4) + (size / 2^n - 4) binnum = 4n + size / 2^n</pre> <ul class=" list-paddingleft-2"><li><p>所以第三行的意思我们就知道了,就是size右移2^n次方为</p></li></ul> <h4>第四行</h4> <ul class=" list-paddingleft-2"> <li><p><code>t2 = t2 - 3;

    • 这个好理解,可以参照上文得到每组的起始位置的方法

    第五行

    • t2 = t2 << 2;

    • 我们再看看bin_num的计算公式

    binnum = (4n + 4) + (size / 2^n - 4)
    
    binnum = 4n + size / 2^n
    • 那么这行就好理解了,就是计算每组的起始位置4n对吧,左移两位就是乘以4

    第六行

    • return (int)(t1 + t2);

    • 这行没啥说的,就是返回了一个int类型的bin_num

    以上是php中small記憶體規格的計算(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除