低いメモリ使用率と弱い型の PHP 配列の例

黄舟
リリース: 2023-03-14 20:22:01
オリジナル
1033 人が閲覧しました

この記事では主に、PHP 配列の低メモリ使用率と弱い型の詳細な解釈を紹介します。一定の参考値があるので、興味のある方は参考にしてください。

この 2 日間のタスクは予定より早く完了しました。落ち着いて PHP を深く学ぶために一息入れることができます。実は、もともとPHPのパフォーマンス最適化について学びたかったのですが、ネット上の「PHPの配列メモリ使用率は低い。C言語で100MBのメモリ配列はPHPで1G必要。」という文章に衝撃を受けました。 PHP は本当に多くのメモリを消費しますか?そこで私はこの機会を利用して、PHP のデータ型の実装について学びました。

まずテストをしてみましょう:


<?php 
  echo memory_get_usage() , &#39;<br>&#39;; 
  $start = memory_get_usage(); 
  $a = Array(); 
  for ($i=0; $i<1000; $i++) { 
   $a[$i] = $i + $i; 
  } 
  $end = memory_get_usage(); 
  echo memory_get_usage() , &#39;<br>&#39;; 
  echo &#39;argv:&#39;, ($end - $start)/1000 ,&#39;bytes&#39; , &#39;<br>&#39;;
ログイン後にコピー

結果:

353352
437848
argv: 84.416bytes

1000 要素の整数配列はメモリを消費します (4 37848 - 353352) バイト、約 82KB 、つまり各要素は 84 バイトのメモリを占有します。 C 言語では、int は 4 バイトを占有し、全体で 20 倍の違いがあります。

しかし、インターネットでは、memory_get_usage() によって返される結果はすべての配列占有ではなく、PHP 自体の構造も含まれていると言われています。そのため、別の方法を試し、PHP 組み込み関数を使用して配列を生成してください。


<?php 
  $start = memory_get_usage(); 
  $a = array_fill(0, 10000, 1); 
  $end = memory_get_usage(); //10k elements array; 
  echo &#39;argv:&#39;, ($end - $start )/10000,&#39;byte&#39; , &#39;<br>&#39;;
ログイン後にコピー

出力は次のとおりです:

argv:54.5792byte


は以前よりわずかに改善されましたが、それでも 54 バイトであり、確かに約 10 倍悪くなります。


その理由は、基礎となる PHP の実装から始まる必要があります。 PHP は型付けが弱い言語ですが、int、double、string などに関係なく、統一された '$' ですべての問題を解決できます。 PHP の最下層は C 言語で実装されており、各変数は zval 構造体に対応しており、その詳細な定義は次のとおりです。


typedef struct _zval_struct zval; 
struct _zval_struct { 
  /* Variable information */ 
  zvalue_value value;   /* The value 1 12字节(32位机是12,64位机需要8+4+4=16) */ 
  zend_uint refcount__gc; /* The number of references to this value (for GC) 4字节 */ 
  zend_uchar type;    /* The active type 1字节*/ 
  zend_uchar is_ref__gc; /* Whether this value is a reference (&) 1字节*/ 
};
ログイン後にコピー

PHP は、zvalue_value 型の value 変数を使用して変数の値を格納します。 zval は共用体であり、次のように定義されます:


typedef union _zvalue_value { 
  long lval;         /* long value */ 
  double dval;        /* double value */ 
  struct {          /* string value */ 
    char *val; 
    int len; 
  } str;  
  HashTable *ht;       /* hash table value */ 
  zend_object_value obj;   /*object value */ 
} zvalue_value;
ログイン後にコピー

共用体型によって占有されるメモリのサイズは、その最大のメンバーによって占有されるデータ領域によって決まります。 zvalue_value では、str 構造体の int が 4 バイト、char ポインタが 4 バイトを占めるため、zvalue_value 全体が占めるメモリは 8 バイトになります。


zvalのサイズは8 + 4 + 1 + 1 = 14バイトです。


zvalue_value にも HashTable があることに注意してください。これは何をするのでしょうか? zval では、配列、文​​字列、オブジェクトにも追加のストレージ構造が必要です。配列のストレージ構造は HashTable です。


HashTable の定義は次のとおりです:


typedef struct _hashtable { 
   uint nTableSize; //表长度,并非元素个数 
   uint nTableMask;//表的掩码,始终等于nTableSize-1 
   uint nNumOfElements;//存储的元素个数 
   ulong nNextFreeElement;//指向下一个空的元素位置 
   Bucket *pInternalPointer;//foreach循环时,用来记录当前遍历到的元素位置 
   Bucket *pListHead; 
   Bucket *pListTail; 
   Bucket **arBuckets;//存储的元素数组 
   dtor_func_t pDestructor;//析构函数 
   zend_bool persistent;//是否持久保存。从这可以发现,PHP数组是可以实现持久保存在内存中的,而无需每次请求都重新加载。 
   unsigned char nApplyCount; 
   zend_bool bApplyProtection; 
} HashTable;
ログイン後にコピー

テーブルのサイズとそれに含まれる要素の数を記録するいくつかの属性変数に加えて、Bucket の定義方法:


typedef struct bucket { 
   ulong h; //数组索引 
   uint nKeyLength; //字符串索引的长度 
   void *pData; //实际数据的存储地址 
   void *pDataPtr; //引入的数据存储地址 
   struct bucket *pListNext; 
   struct bucket *pListLast; 
   struct bucket *pNext; //双向链表的下一个元素的地址 
   struct bucket *pLast;//双向链表的下一个元素地址 
   char arKey[1]; /* Must be last element */ 
} Bucket;
ログイン後にコピー
リンク リストと少し似ています。Bucket は特定のデータとポインターを含むリンク リスト ノードのようなもので、HashTable は Bucket 要素の文字列を格納する配列です。 PHP での多次元配列の実装は、バケットに格納される別の HashTable にすぎません。


HashTable が 39 バイト、Bucket が 33 バイトを占めることを計算します。空の配列は 14 + 39 + 33 = 86 バイトを占めます。 Bucket 構造体には 33 バイトが必要で、キー長が 4 バイトを超える部分は Bucket の末尾に追加され、要素の値は zval 構造体になる可能性が高く、さらに各配列には Bucket ポインターが割り当てられます。 arBuckets が指す配列。ただし、追加要素ごとにポインタが必要であるとは言えませんが、状況はさらに悪化する可能性があります。これは、1 つの配列要素が 54 バイトを占有することを計算し、これは上記の見積もりとほぼ同じです。


スペースの観点から見ると、小さな配列は平均してコストが高くなります。もちろん、スクリプトが多数の小さな配列で埋め尽くされることはなく、より少ないスペース コストでプログラミング速度を向上させることができます。しかし、配列をコンテナとして使用する場合は別の話になります。実際のアプリケーションでは、多くの要素を含む多次元配列が頻繁に発生します。たとえば、10k 要素の 1 次元配列は約 540k のメモリを消費しますが、10k の 2 次元配列は実際には 23M を消費します。小さな配列には実際には価値がありません。

以上が低いメモリ使用率と弱い型の PHP 配列の例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
php
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!