PHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)

藏色散人
リリース: 2023-02-17 11:58:01
転載
2517 人が閲覧しました

今回は「PHP8の基盤となるカーネルソースコードの解析 - 配列(2)」を紹介します。一定の参考値があるので、困っている友達が参考になれば幸いです。

おすすめ関連記事: 「PHP8の基礎となるカーネルソースコードの解析 - 配列(1) 」 「PHP8の基礎となるカーネルソースコードの解析 - 配列(3) 」 " PHP8 基盤となるカーネルソースコードの解析 - 配列 (4) >>

zend_array は PHP

1.packed array
2.hash で 2 種類に分かれていますarray

在上文中 补齐了zend_array的 所有值的 注释
ログイン後にコピー

実際、ソース コード内の順序は上記のものと少し異なります。上記の順序の方が理解しやすいと思います。

//源码里的代码
typedef struct _zend_array HashTable;
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar    flags,
zend_uchar    _unused,
zend_uchar    nIteratorsCount,
zend_uchar    _unused2)
} v;
uint32_t flags;
} u;
uint32_t          nTableMask;
Bucket           *arData;
uint32_t          nNumUsed;
uint32_t          nNumOfElements;
uint32_t          nTableSize;
uint32_t          nInternalPointer;
zend_long         nNextFreeElement;
dtor_func_t       pDestructor;
};
//我调换下顺序后的代码
struct _zend_array {
zend_refcounted_h gc; 
 ///  gc  占用8个字节 用于引用计数和  字符串类型的记录
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar    flags,
// flags   8位的无符号字符, 最大值为255   标记HashTable用 PHP8 中有6个值
zend_uchar    _unused,
zend_uchar    nIteratorsCount,
//迭代器计数。foreach语句会在全局变量EG中创建一个迭代器,
//迭代器包含正在遍历的HashTable和游标信息。
//nIteratorsCount记录了当前runtime正在迭代当前HashTable的迭代器的数量。
zend_uchar    _unused2)
} v;
  //这里有点不一样 看陈雷大佬书中 v结构体还包括 u.v.nApplyCount和u.v.consistency
uint32_t flags;
             //
} u;
// u是是一个联合体。占用4个字节。
//可以存储一个uint32_t类型的flags,也可以存储由4个unsigned char组成的结构体v,
//这里的宏ZEND_ENDIAN_LOHI_4是为了兼容不同操作系统的大小端,可以忽略。
Bucket           *arData;
//HashTable中存储数据的单元的指针。
//  用来存储key和value以及辅助信息的容器。
uint32_t          nTableSize;
//    HashTable的大小。表示arData指向的bucket数组的大小,即所有bucket的数量。
//该字段取值始终是2n,最小值是8,最大值在64位系统中是0x80000000(2的31次幂)。
uint32_t          nNumUsed;
//指所有已使用bucket的数量,包括有效bucket和无效bucket的数量
uint32_t          nNumOfElements;
//有效bucket的数量。该值总是小于或等于nNumUsed
uint32_t          nTableMask;
//索引大小。一般值为  -nTableSize。
uint32_t          nInternalPointer;
//全局默认游标。reset/key/current/next/prev等宏 和操作都会用到
zend_long         nNextFreeElement;
//下一个插入的元素的key的下标  
//比如  当$a[] = 1  nNextFreeElement =1  
dtor_func_t       pDestructor;
//指向一个函数   typedef void (*dtor_func_t)(zval *pDest);
//可以看出是pDest是zval结构指针二级指针,
//为什么会是二级指针,因为c语言函数传递都是值传递,要改变指针值只能将指针地址传入
//当bucket元素被更新或者被删除时,会对bucket的value调用该函数,
//如果value是引用计数的类型,那么会对value引用计数减1,进而引发可能的gc。
};
ログイン後にコピー

メンバー理解ツールによって生成された変数図は次のとおりです

PHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)

すべての展開後の結果は次のとおりです

PHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)

#zend_array 構造体のメンバー

コアは z_val zend_string zend_refcounted_h であることがわかります。バケットは複数の層を重ねています。

バケットには、配列のキー情報が格納されます。

typedef struct _Bucket {
zval              val;   //数组的值 ( 复习下 zval只有16个字节)
zend_ulong         h;     // key的 h  值
zend_string      *key;      //当数组为 hash_array时候 会用到 也就是 key的值  
} Bucket;
ログイン後にコピー

配列の種類に関係なく、 Packed_array または hash_array の場合、最終的には Bucket

キーがすべて数値キーであり、キーが挿入順序で増加する場合、配列タイプは次のようになります。

packed_array##パック配列の特徴

    配列にインデックスを付ける必要はありません
  1. キーは必要ありません
  2. キーのない配列の h 値は、0
  3. # から始まるバケット内のスペースのソート値と直接同じです。 ##キーと値のペアの配列 h 値は、キーの内容と等しくなります。
  4. ##ここで、3 番目と 4 番目の項目は、次のように理解できます。 PHP の配列はキーを書き込まないため、デフォルトのキーは 0

$a =array(1,2,3);  // packed array
$b =array(1=>'a',3=>'b',5=>'c'); //packed array
ログイン後にコピー
PHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)
バケット配列の前にインデックス配列があります。
パック配列の場合、インデックス配列のサイズは使用されないため、常に 2 になります。

$a に対応する zend_array の内容は、

##nTableSize です。 ; arData が指すバケット配列のサイズ、つまりすべてのバケットの数を表します。 =配列の合計サイズPHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)
nNum Used;有効なバケットと無効なバケットの数を含む、使用されているすべてのバケットの数を指します

nNumOfElements;有効なバケットの数。

So nNumOfElements nNumused =nTableSizenTableMask; インデックス サイズ。パック配列はインデックスを使用しないため、常に -2

nNextFreeElement; 次に挿入される要素のキーの添え字

パック配列はバケット配列の連続性特性を利用します。デジタル キーのみを使用するシナリオの一部の最適化。インデックス配列は不要になったため、(nTableSize-2)* sizeof(uint32_t) バイトがメモリ空間から節約されます。また、バケットにアクセスすることでバケット配列を直接操作できるため、パフォーマンスも向上します。

パック配列の条件が満たされない場合、配列は PHP では hash_array で表されます。

数値以外のすべてのキー値は hash_array で表されます

#そうだね

上記の $c は hash_array で表されます

バケットは次のとおりです

PHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)
$ cbucket

#zend_array は次のとおりです

#$c zend_arrayPHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)
nTableSize; arData が指すバケット配列のサイズ、つまりすべてのバケットの数を表します。 =8

nNumused; 有効なバケットと無効なバケットの数を含む、使用されているすべてのバケットの数を参照します =4

##nNumOfElements; 有効なバケットの数。 =4So nNumOfElements nNumused =nTableSize

nTableMask; インデックス サイズ。 -8

nNextFreeElement; 次に挿入される要素 hash_array のキーの添字は、使用されていない場合は常に 0 になります

▏この記事は、php 中国語 Web サイトで公開されました。元の著者 PHP Cui Xuefeng の同意、元のアドレス: https://zhuanlan.zhihu.com/p/358354087

以上がPHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:zhihu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート