【PHPソース分析】smallメモリ仕様の計算
7812 ワード
作者:李徳
smallメモリ割当て計算bin_num
PHPソースには、Zend/zend_でsmallメモリ仕様の計算があります.alloc.cのzend_mm_small_size_to_bin関数では,sizeを入力し,対応する仕様を計算することを目的とする.コードを参照:
このコードでは、次の2つのケースに分けて説明されています.1、sizeが64以下の場合. 2、sizeが64より大きい場合.
以下、この2つの状況を詳しく分析します.
sizeが64以下の場合 が使用される.であるがsizeが8,16等であることを考慮すると である.は、次に0の場合を考慮しなければならないので、ソースコードにおける がある.
sizeが64より大きい場合
最初は愚かだった初めてこのコードを見て、顔がぼんやりしていて、これらのt 1 t 2はすべて何です でも恐れる必要はありません.私たちは少しずつ を分析します.
ステップ分析 に近づいたと考えられています.このソースコードを見ないと仮定し、 とした.このパケット情報があればsiez対応bin_を探しますnum このsizeがどのグループに属しているかを見つけます そしてsizeのグループ内のオフセットはどのくらい ですか.計算グループの開始位置 それは今問題が上の3つの小さな問題に変換されて、私たちは1つずつ を解決します.
sizeがどのグループに属しているかを見つけます一番簡単な方法は大きさを比べることですね.ifを使うことができます.elseは比べてみますが、phpソースコードはそうではありません.他に何か方法がありますか. 十進法では何も見えないので、これらの値をバイナリに変えてみましょう. 上のバイナリを見てみると、各グループのバイナリの長さは等しく、後ろの各ビットは前の より多いことがわかります.それは私たちがバイナリの長さを計算してそのパケットを決定することができるということです.では、バイナリの長さは何ですか.実は現在のバイナリの最上位の です.それでは問題はまたバイナリの中で最高位の に変換された.は、phpソースコードの解法を以下に示す.ここでは、バイナリの中で最も高い を返すことを知っている限り、その解析は一時的に行われない.我々が申請したsizeが65であると仮定すると、ここのnは7 を返す.
グループ内のsizeのオフセット量の計算これは簡単で、sizeで各グループの開始siezサイズを直接減算し、現在のグループ内の差(16、32、64...)で除算します.すなわち である.前のステップの戻り値を見てみましょう.各グループはそれぞれ をどのように計算するかを見てみましょう.それは sizeが65の場合、オフセット量は であるか否かである.
計算グループの開始位置オフセット量の情報が得られ、パケットが1、2、3 であると仮定するそれは最上位の ではないか.パケット情報を取得した後、どのようにして各グループの開始位置を知っていますか 開始位置はそれぞれ です. size=65の例を見てみましょう で算出するオフセット量は0 である.計算の開始位置は である.だからsize=65のbin_numは、開始位置にオフセット量 である.
size=129の例を見てみましょう オフセット量Yes バイナリにおける最上位の である.そして8から3を引く5 を得る.
計算開始位置は である.両者を加算と となる.
size=193 オフセット量Yes バイナリにおける最上位の である.
計算開始位置は である.両者を加算と となる.
size=1793 オフセット量Yes バイナリにおける最上位の である.
計算開始位置は である.両者を加算と となる.
コード解析
php実装コード
最初の行 はsizeが64、128であることを考慮するため...これらの境界状況 2行目 ここでは を見てみましょう.注釈を見ると、この関数は現在のsizeバイナリの中で最も高い1のビット数を返すために使用されていることがわかります.具体的には、二分法 です.私たちは で質問された解析では,sizeのグループ内のオフセット量を計算する式 について述べた.ここで取得したバイナリのビット数は7、8、9です.
3行目 t 1をt 2ビット右に移動すると、これは何の不思議な操作ですか? 最後にbinを計算しますnumの数学式は、各グループの開始位置にグループ内のオフセット量 を加えたものに等しい.ですので3行目の意味は分かりますが、size右シフト2^n次は です.
4行目 この理解は、上記の各グループの開始位置を得る方法 を参照することができる.
5行目 binを見てみましょうnumの計算式 それではこの行はよく理解して、各グループの開始位置 を乗じることです.
6行目 この行は何も言わないで、intタイプのbinを返しました.num
smallメモリ割当て計算bin_num
PHPソースには、Zend/zend_でsmallメモリ仕様の計算があります.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);
}
このコードでは、次の2つのケースに分けて説明されています.
以下、この2つの状況を詳しく分析します.
sizeが64以下の場合
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
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);
最初は愚かだった
ステップ分析
/* 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つを1組とし,それぞれ2^4, 2^5, 2^6...
sizeがどのグループに属しているかを見つけます
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のオフセット量の計算
(size-64)/16 (size-128)/32 (size-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) / 2^4 = 0
計算グループの開始位置
1
の桁数から6
を減算パケット情報が得られる8、12、16...
であり、等差数列でもあることを知っています.4n+4
4*1 + 4 = 8
8 + 0 = 8
を加える1
のビット数は8 (129 - 1 - 32 * 4) / 64 = 0
4 * 2 + 4 = 12
12 + 0 = 0
1
のビット数は8 (193 - 1 - 32 * 4) / 64 = 2
4 * 2 + 4 = 12
12 + 2 = 14
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;
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
の操作により、対応する4、5、6を取得する.3行目
t1 = t1 >> t2;
binnum = (4n + 4) + (size / 2^n - 4)
binnum = 4n + size / 2^n
4行目
t2 = t2 - 3;
5行目
t2 = t2 << 2;
binnum = (4n + 4) + (size / 2^n - 4)
binnum = 4n + size / 2^n
4n
を計算するのではないでしょうか.左に2桁移動するのは4 6行目
return (int)(t1 + t2);