gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

配列のぶん回しとメモリ

ちょうど、一部環境でPHP7を入れた事もあって色々よっしゃな感じなので実験。

<?php

function hoge($awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk[] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );


これが、PHP5環境(正確にはPHP5.5系)だと

int(15204352)
int(15204352)
int(25427968)
int(25427968)

になる一方で、PHP7だと

int(6295552)
int(6295552)
int(6295552)
int(6295552)

になる。


ちなみに参照渡しにすると…つまり

<?php

function hoge(&$awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk[] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

にすると、PHP5で

int(15204352)
int(15204352)
int(15204352)
int(15204352)

PHP7はまぁ予想通り

int(6295552)
int(6295552)
int(6295552)
int(6295552)

ってなる。


ただじゃぁPHP5にいったん焦点をあてて…foreachを「呼べばよぶほどメモリ食うのか」というとそうでもなくて。

<?php

function hoge($awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );

  foo($awk);
var_dump( memory_get_peak_usage(true) );
}

function foo($awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk[] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

でも

int(15204352)
int(15204352)
int(25427968)
int(25427968)
int(25427968)
int(25427968)

なので、「二回呼んだら二倍」ってわけではなさそう。
ちなみに「全部参照渡し」だと「int(15204352)」のままなんだけど、どこか一か所でも普通の引数にするとメモリが増えるぽい。


この辺が厭われる場合、PHP5系だと手っ取り早いのが、arrayObject。

<?php

function hoge($awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );

  foo($awk);
var_dump( memory_get_peak_usage(true) );
}

function foo($awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


//$awk = [];
$awk = new arrayObject();
for($i = 0; $i < 100000; $i ++) {
  $awk[] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

int(15204352)
int(15204352)
int(15204352)
int(15204352)
int(15204352)
int(15204352)

ってなる。
タイプヒンティングとか型宣言とかやってなければ、割と使える手かもしれない。実際に「どこまでメモリ効率がよくなるのか」はいささか疑問も残るけど。


PHP7のforeachが効率よくなるのは、 http://php.net/manual/ja/control-structures.foreach.php あたりにも書いてあって。

PHP 7 では、foreach は内部の配列ポインタを使わなくなりました。

この辺もまた、性能が色々とアレでナニなところなんだろうなぁ、っと。


ってなわけで、PHP7。
「内部的に、いわゆるvectorな配列とmapなhash配列を区別している」云々があるので、hash配列にしてみたらどうなるざましょ?

<?php

function hoge(&$awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk["key_{$i}"] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

int(11272192)
int(11272192)
int(11272192)
int(11272192)

あぁ本当だメモリ効率変わるわ。
「純粋にvectorのみなのかしらん?」って疑問もあるので、確認。

<?php

function hoge(&$awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk[$i * 2] = $i;
}
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

int(11272192)
int(11272192)
int(11272192)
int(11272192)

あぁうん連番だけなのね。
念の為、くどいほど確認。

<?php

function hoge(&$awk) {
  foreach($awk as $v) {
    ;
  }
var_dump( memory_get_peak_usage(true) );
}


$awk = [];
for($i = 0; $i < 100000; $i ++) {
  $awk[] = $i;
}
var_dump( memory_get_peak_usage(true) );
$awk[150000] = 150000;
var_dump( memory_get_peak_usage(true) );

//
foreach($awk as $v) {
  ;
}
var_dump( memory_get_peak_usage(true) );

//
hoge($awk);
var_dump( memory_get_peak_usage(true) );

int(6295552)
int(10489856)
int(10489856)
int(10489856)
int(10489856)

微妙…?


まぁとりあえず「普通にvectorな配列」を使うシーンはそれなりにあって。
そこのメモリ効率が良くなっているってのは大変にありがたいので、そこらへんだけ考えてもPHP7は便利なんじゃないかなぁ、っと。


個人的に「PHP5からPHP7への変換」は「ほぼ無修正」でいけるのですが、まぁその辺もおいおい。
端的にトマホークぶん投げると「綺麗に丁寧に書いておけば大概問題ないはずよ?」(笑