PHPでJudy整数配列をシリアル化する

ご存知のように、PHPの変数は貴重なデータであるだけでなく、zvalコンテナー全体でもあります。 そして、そのような変数の配列はメモリ内のスペースを取りすぎます。

PHPにはJudy配列が実装されており、高速で連想性があり、メモリ消費量が1桁少なくなっています。 しかし、残念ながら、それらのシリアル化/逆シリアル化の方法はありません。



整数配列のみをシリアル化することに興味がありました。 配列のインデックスのように、その値は整数のみです。 拡張ソースコードに必要なメソッドを追加して実装しました。 シリアル化の原理は簡単です。

配列(200 => 0、300 => -5000、-100 => 2000)は文字列になります

「+ c8 + 0 + 12c-1388-64 + 7d0」



ソースを取得できる場所は2つあります。



2番目が選択されました。 すべての変更はphp_judy.cで行われます

太線は追加された行を示します。



  1. シリアル化のサポートを宣言します。

    /* implements some interface to provide access to judy object as an array */

    zend_class_implements(judy_ce TSRMLS_CC, 1, zend_ce_arrayaccess, zend_ce_iterator);







    zend_class_implements(judy_ce TSRMLS_CC, 1, zend_ce_serializable);







    judy_ce->ce_flags |= ZEND_ACC_FINAL_CLASS;







  2. メソッドを宣言します:

    PHP_METHOD(judy, memoryUsage);





    PHP_METHOD(judy, serialize);





    PHP_METHOD(judy, unserialize);





    PHP_METHOD(judy, count);





  3. シリアル化解除の入力パラメーター:

     ZEND_BEGIN_ARG_INFO_EX(arginfo_judy_unserialize, 0, 0, 1) ZEND_ARG_INFO(0,str) ZEND_END_ARG_INFO()
          
          





  4. 目に見えるメソッドの説明:

    PHP_ME(judy, memoryUsage, NULL, ZEND_ACC_PUBLIC)





    PHP_ME(judy, serialize, NULL, ZEND_ACC_PUBLIC)





    PHP_ME(judy, unserialize, arginfo_judy_unserialize, ZEND_ACC_PUBLIC)





    PHP_ME(judy, count, arginfo_judy_count, ZEND_ACC_PUBLIC)





  5. メソッド自体と1つのヘルパー関数:

     PHP_METHOD(judy, serialize) { JUDY_METHOD_GET_OBJECT if ( intern->type == TYPE_INT_TO_INT ) { long int index = 0, i = 0, l; long int * PValue; char * s; Word_t idx1 = 0, idx2 = -1, Rc_word; JLC(Rc_word, intern->array, idx1, idx2); l = 64 + ( Rc_word << 3 ); s = emalloc ( l ); if ( s == NULL ) RETURN_NULL(); JLF ( PValue, intern->array, index ); while ( PValue != NULL && PValue != PJERR ) { JLG ( PValue, intern->array, index ); if ( index < 0 ) { if ( *PValue < 0 ) sprintf ( s+i, "-%x-%x", -index, -*PValue ); else sprintf ( s+i, "-%x+%x", -index, *PValue ); } else { if ( *PValue < 0 ) sprintf ( s+i, "+%x-%x", index, -*PValue ); else sprintf ( s+i, "+%x+%x", index, *PValue ); } i += strlen ( s+i ); if ( i > l - 50 ) { l <<= 1; s = erealloc ( s, l ); if ( s == NULL ) RETURN_NULL(); } JLN ( PValue, intern->array, index ); } *(s+i) = 0; RETURN_STRING ( s, 0 ); } RETURN_NULL(); } long int Hex2Int ( char * s, long int * j, long int l ) { long int v = 0; char sym; if ( s[*j] == '+' ) { (*j)++; sym = s[*j]; while ( sym != '+' && sym != '-' && *j < l ) { v <<= 4; if (( sym >= '0' ) && ( sym <= '9' )) v += ( sym - '0' ); else v += ( sym - 'a' + 10 ); (*j)++; sym = s[*j]; } } else { (*j)++; sym = s[*j]; while ( sym != '+' && sym != '-' && *j < l ) { v <<= 4; if (( sym >= '0' ) && ( sym <= '9' )) v -= ( sym - '0' ); else v -= ( sym - 'a' + 10 ); (*j)++; sym = s[*j]; } } return v; } PHP_METHOD(judy, unserialize) { JUDY_METHOD_GET_OBJECT intern->counter = 0; intern->type = TYPE_INT_TO_INT; intern->array = (Pvoid_t) NULL; char * s; long int l, j = 0; Word_t * PValue, index, value; if ( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &l) == FAILURE) return; if ( l < 4 ) return; while ( j < l ) { index = Hex2Int ( s, &j, l ); value = Hex2Int ( s, &j, l ); JLI ( PValue, intern->array, index ); if ( PValue == NULL || PValue == PJERR ) return; *PValue = value; } return; }
          
          





  6. コンパイルします:

     phpize ./configure make make install
          
          





  7. php.iniに登録することを忘れないでください

    extension=judy.so







小テスト\ベンチマーク


 <?php function InitArray ( &$ar, $init_srand, $max ) { srand ( $init_srand ); for ( $i = -$max; $i < $max; $i++ ) { $index = rand ( -$max, $max ); $value = rand ( -1000, 1000 ); $ar[$index] = $value; } } function ShowTime ( $st, $text ) { printf ( "$text in %.2f sec.\n", microtime ( true ) - $st ); } $init_srand = time (); $max = 500000; echo "srand = $init_srand, max = $max\n"; $init_mem = memory_get_usage(); $st = microtime ( true ); $a = array (); InitArray ( $a, $init_srand, $max ); ShowTime ( $st, "Initialized std. array" ); echo "Elements in std. array: " , count ( $a ) , "\n"; echo "Used memory: " , (memory_get_usage()-$init_mem) , " bytes\n\n"; $st = microtime ( true ); $j = new Judy(Judy::INT_TO_INT); InitArray ( $j, $init_srand, $max ); ShowTime ( $st, "Initialized Judy array" ); echo "Elements in Judy array: " , count ( $j ) , "\n"; echo "Used memory: " , $j->memoryUsage() , " bytes\n\n"; $st = microtime ( true ); $a_ser = serialize ( $a ); ShowTime ( $st, "Serialized std. array" ); $st = microtime ( true ); $j_ser = serialize ( $j ); ShowTime ( $st, "Serialized Judy array" ); echo "Serialized std. length: " , strlen ( $a_ser ) , "\n"; echo "Serialized Judy length: " , strlen ( $j_ser ) , "\n\n"; $st = microtime ( true ); $a_unser = unserialize ( $a_ser ); ShowTime ( $st, "Unserialized std. array" ); $st = microtime ( true ); $j_unser = unserialize ( $j_ser ); ShowTime ( $st, "Unserialized Judy array" ); if (count ( $j_unser ) != count ( $a )) die ( "ERROR: arrays size mismatch\n" ); foreach ( $a as $index => $value ) { if ( $j_unser[$index] != $value ) { die ( "ERROR: arrays data mismatch\n" ); } } echo "OK: std. array and unserialized Judy array are identical\n"; ?>
      
      







出力では

 srand = 1348822174, max = 500000 Initialized std. array in 0.76 sec. Elements in std. array: 632067 Used memory: 49703784 bytes Initialized Judy array in 0.98 sec. Elements in Judy array: 632067 Used memory: 3247108 bytes Serialized std. array in 0.33 sec. Serialized Judy array in 0.18 sec. Serialized std. length: 9904186 Serialized Judy length: 6061744 Unserialized std. array in 0.13 sec. Unserialized Judy array in 0.08 sec. OK: std. array and unserialized Judy array are identical
      
      





いくつかのコメント
  1. もちろん、シリアル化されたデータを持つ文字列にメモリを割り当てるアルゴリズムを変更できます。
  2. 現在、Judyのforeachイテレータの実装にバグがあります。

    配列にインデックス0と-1が同時に含まれている場合、foreachは無限ループに入ります

     <?php $j = new Judy(Judy::INT_TO_INT); $j[0] = 456; $j[-1] = 123; foreach ( $j as $index => $value ) { echo "$index $value\n"; } ?>
          
          





    クエリ「foreach [0] [-1]」が作成されました 。 必要に応じて、簡単に修正できます。





PSこの投稿は1年以上前-2012年9月に書かれたもので、このすべての時間はサンドボックスの中にありました。 私が今わかったように、彼らは3か月前に別のブランチのgithubでシリアル化を実装しました。

だから私は先に進み、Habrから実装をほとんどコピーしましたが、そのプロセスでネガティブキーのサポートを削除しました


github.com/orieg/php-judy/issues/13

github.com/tony2001/php-judy/tree/serialize



All Articles