ホームページ  >  記事  >  バックエンド開発  >  PHPでの小規模メモリ仕様の計算(コード例)

PHPでの小規模メモリ仕様の計算(コード例)

不言
不言転載
2019-02-25 10:01:162170ブラウズ

この記事の内容は、PHP での小さいメモリの仕様の計算 (コード例) に関するもので、一定の参考値があります。必要な友人が参考にすることができます。お役に立てれば幸いです。

小規模なメモリ割り当ての計算 bin_num

PHP ソース コードには、特に Zend/zend_alloc.c の zend_mm_small_size_to_bin 関数内に、小規模なメモリ仕様の計算があります。その目的は、サイズ、対応する仕様を計算します。コードを参照してください:

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

このコードは、議論のために 2 つの状況に分割されていることがわかります:

  • 1. サイズが以下の場合64;

  • 2. サイズが 64 より大きい場合;

これら 2 つの状況を以下で詳しく分析してみましょう。

サイズが 64 以下の場合については、

  • ZEND_MM_BINS_INFO」を参照してください。このマクロは、サイズが 64 以下であることを認識します。 64、これは 8 ずつ増加する算術シーケンスであるため、size を 8 で割った値を使用します (ソース コードでは 3 ビット右シフト) size >> 3</pre> <li> <p>ただし、サイズは 8、16 などに等しいことを考慮する必要があるため、<code>(size - 1) >> 3

  • 次に、 0の場合を考慮する必要があるので、ソースコードの -1の処理は !!size となり、size が 0 の場合 !!0 = 0## #。したがって、サイズが 0 の場合、-1-0 に変換され、最終的にソース コードには式 (size - !!size) >> が存在します。 3

  • サイズが 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;

      これは境界ケースであり、前のものと同様に、後で現れるサイズは次のようにみなされます。ほぼ 1 つ減少しました

    • このソース コードを見ないと仮定すると、
    • ZEND_MM_BINS_INFO

      ## で対応する bin_num を見つける必要があります。

    • ZEND_MM_BINS_INFO
    • から学習され、その後、4 がそれぞれグループとして追加されます

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

    このグループ化情報を使用して、次のことが必要になります。 siez に対応する bin_num を見つけます
    • このサイズがどのグループに属しているかを見つけます
      • そして、その中のサイズのオフセットは何ですかグループ
      • グループの開始位置を計算します
      • 問題は上記の 3 つの小さな問題に変換されますので、解いてみましょう1 つずつ
    • # サイズがどのグループに属しているかを見つける最も簡単な方法は、サイズを比較することですよね? if...else を使用して 1 つずつ比較することもできますが、明らかに、PHP ソース コードはこのようには機能しません。では、他にどのような選択肢があるでしょうか?
    10 進数では興味深いものが何も表示されないので、これらの値を 2 進数に変換して見てみましょう
    • 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
      
      .....

    • 上記の 2 進数を見ると、各グループの 2 進数の長さが等しく、次のそれぞれの桁が前のグループより 1 つ多いことがわかります。

    つまり、2 進数の長さを計算できるということです。そのグループ分けを決定すると、2 進数の長さはどれくらいになりますか?実際には、現在の 2 進数の最上位ビットが # である桁数になります。 ##1
    • 次に、質問は「2 進数の最上位ビット
    • 1

      の桁数を見つける」に変換されます。 PHP ソース コードの解決策は次のとおりです。ここでは解析しませんが、2 進数の最大桁数

      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;

      # 適用するサイズが 65 であると仮定すると、ここでの n は 7 を返します
    • #計算グループ内のサイズのオフセット

    • これは簡単です。サイズから各グループの開始サイズを直接減算し、それを現在のグループで割ります。差 (16、32、64) ...) で十分です。つまり、
    (サイズ-64)/16 (サイズ-128)/32 (サイズ-256)/64
    • 今前のステップで返された値を見てみましょう。各グループは
    7, 8, 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

      #7、8、9

      から
    • 3
    • を引いたものを使用できますか?

      4、5、6# を取得します##、どのグループに属しているかという情報に基づいて、現在のグループの差 (16、32、64...) を取得できるようにします。

    • サイズが 65 の場合、
      #
      (64-64) / 2^4 = 0
    • に等しいオフセットです。

      ##<pre class="brush:php;toolbar:false">1 t1 = size - 1; 2 t2 = zend_mm_small_size_to_bit(t1) - 3; 3 t1 = t1 &gt;&gt; t2; 4 t2 = t2 - 3; 5 t2 = t2 &lt;&lt; 2; 6 return (int)(t1 + t2);</pre>グループの開始位置を計算します#これで、グループ化が 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></h3> <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での小規模メモリ仕様の計算(コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。