ラベル 配列 の投稿を表示しています。 すべての投稿を表示
ラベル 配列 の投稿を表示しています。 すべての投稿を表示

2013年5月6日月曜日

配列のメモリの話 その2

今回は制御構造とメモリ使用量の関係について調べてみます。

サンプルコード 実行結果
memory 1:160
memory 2:128
memory 3:0
個人的な予想に反してforよりforeachの方が省メモリであるという結果が出ました。
コードの書き方を何か間違えたかな…と思ったのですが原因についてはよく分かりませんでした。
listとeachを使ったループが最も省メモリであるのは予想通りです。
とても大きな配列をループさせる時はlistとeachで回すのも一つの方法かもしれませんね。

配列のメモリの話 その1

ArrayAccessと配列の比較を続ける前に
PHPの配列とメモリの関係について確認しておきましょう。

簡単なサンプルコードを書いてみます。

サンプルコード 実行結果
1
memory 1:56
memory 2:5324488
と、このようにtest1とtest2ではメモリの使用量に大きな差が出ています。

これは、関数に配列が引数として与えられた場合、
PHPはその時点ではスタック領域のポインタアドレスを渡していますが、
配列に変更があった時点でその配列のヒープ領域がコピーされ
別の変数として新しいスタック領域に別のポインタアドレスが保存されることを指します。

ArrayAccessと配列の比較 その1

ArrayAccessと配列の比較をしてみます。
まずは配列への値の格納からです。

今回はメモリの比較もしたかったのでいつものベンチマーク用の関数は使いません。

ArrayAccessのサンプルコード 実行結果
time:0.10538291931152
memory:8653392
配列のサンプルコード 実行結果
time:0.01983904838562
memory:8647760
と、速度もメモリ使用量も配列のほうがパフォーマンスが高いという結果になりました。

2013年5月5日日曜日

ArrayAccessの挙動

今回はArrayAccessインタフェースについての確認です。
ArrayAccessを実装したクラスは配列と同様の挙動をします。

サンプルコード 実行結果
string(21) "arr_object::offsetSet"
string(21) "arr_object::offsetSet"
string(21) "arr_object::offsetSet"
string(24) "arr_object::offsetExists"
bool(false)
string(21) "arr_object::offsetGet"
string(6) "array2"
string(23) "arr_object::offsetUnset"
arr_object Object
(
    [container:arr_object:private] => Array
        (
            [0] => array1
            [1] => array2
        )

)

2013年3月17日日曜日

関数の返り値を直接配列として扱うと…

以前の記事でPHP5.4から「関数やメソッドの返す結果を直接配列として扱える」ように
なったという事について例を示しました。
ですが、そのベンチマークを取り忘れていたので調査してみます。

サンプルコード 実行結果
BenchMark 1     average:0.012288689613342
BenchMark 2     average:0.0055228233337402
というわけで、普通の配列の倍以上の時間がかかることが分かりました。
これは、foreachのループごとに関数getArray()が実行されていることによるパフォーマンス低下と見なせます。
関数の返り値の利用が一度きりならまだしも、複数回参照するのでしたら一度変数に代入したほうが良さそうですね。

ループ内での要素の変更

配列のマニュアルを読んでいたら良さそうな例を見かけたので
メモ&ベンチマークです。

サンプルコード 実行結果
BenchMark 1     average:0.032666206359863
BenchMark 2     average:0.032941651344299
これからはパターン1で行こうと思います。

配列について考える

今回は配列について考えます。
まずは配列についての基本的な知識から。

keyは整数 または 文字列です。 valueには任意の型を指定できます。

さらに、keyは指定される文字列によっては型キャストが発生します。
  • integer として妥当な形式の文字列は integer 型にキャストされます。
    つまり、キーに "8" を指定すると、実際には 8 として格納されるということです。
    一方 "08" はキャストされません。これは十進数として妥当な形式ではないからです。
  • floats もまた integer にキャストされます。
    つまり、 小数部分は切り捨てられるということです。
    たとえばキーに 8.7 を指定すると、実際には 8 として格納されます。
  • boolinteger にキャストされます。
    つまり、 キーに true を指定すると実際には 1 に格納され、
    同様にキーを false とすると実際には 0 となります。
  • Null は空文字列にキャストされます。
    つまり、キーに null を指定すると、実際には "" として格納されます。
  • arrayobject は、キーとして使えません。 キーとして使おうとすると Illegal offset type という警告が発生します。
サンプルコード 実行結果
array(1) {
  [1]=>
  string(1) "d"
}
上の例は、keyが全て1に型キャストされる例です。
keyが1にキャストされた結果、valueが上書かれて最終的にdになります。

では次に、PHP5.4以降で可能となった「関数やメソッドの返す結果を直接配列として扱える」という例をご紹介します。

サンプルコード 実行結果
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
}
array(5) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
  [3]=>
  int(4)
  [4]=>
  int(5)
}
と、このように関数の結果を一次変数に代入しなくても次の関数の引数に設定することができるようになりました。
今まで関数の結果を代入するだけだった変数が要らなくなったことは大きな進歩だと思います。

そしてPHP5.5以降では、配列リテラルをデリファレンスできるようになりました。
以下、マニュアルより抜粋したコードです。

サンプルコード 今回はここまでです。

2013年3月15日金曜日

配列の添字の変更

先日ふと疑問に思ったことです。
配列の添字を変えたい場合はどうするのがスマートなのでしょうか?

とりあえず自分で考えた実装 なんのひねりもない上に2行で処理しているのがとても気持ち悪いですね。
特定の場合に限りarray_popなどを使用する書き方もありますが汎用的ではありませんし…。

以上、気になった出来事でした。

2013年3月14日木曜日

配列の結合について

配列の結合にはarray_mergeを使う方法と+演算子を使う方法があると思いますが、違いについて確認してみました。

サンプルコード
実行結果
Array
(
    [0] => a0
    [1] => a1
    [2] => a2
    [a] => ba
    [b] => bb
    [c] => bc
    [3] => b0
    [4] => b1
    [5] => b2
)
Array
(
    [0] => a0
    [1] => a1
    [2] => a2
    [a] => aa
    [b] => ab
    [c] => ac
)
ということで、表にまとめるとこのような感じになります。
配列の結合と重複した添字の関係
数値文字列
array_merge後の配列は後ろに追加後の配列の値で上書き
前の配列の値で上書き前の配列の値で上書き

ちなみに、お決まりのベンチマークです

サンプルコード 実行結果
BenchMark 1     average:0.029446578025818
BenchMark 2     average:0.011990690231323
やっぱりarray_mergeを使わない方が速いですね。
ですがこの二種類は使い所が違うので、違いをしっかり把握しておくことが重要だと思います。

2013年3月13日水曜日

変数と配列

今日も今日とて趣味のベンチマークです。
小さな積み重ねがいつか実ると信じています。

今回は変数と配列の扱いについてです。

サンプルコード 実行結果
BenchMark 1     average:0.0046348333358765
BenchMark 2     average:0.01211142539978
BenchMark 3     average:0.012383699417114
変数と比べて配列は3倍くらい遅いですね。
配列の利点の一つとして同じ属性の変数をまとめて分り易いコードを書ける点だと思います。

また、変数と配列の出力についても調査してみました。

サンプルコード 実行結果
BenchMark 1     average:0.2620055437088
BenchMark 2     average:0.26266303062439
BenchMark 3     average:0.26184134483337
こんな感じでした。
一概に変数と配列のどちらが優れているとは言えませんが、
使いどころについては考えたほうが良さそうです。

rangeを使おう

簡単なテストコードを書く際に配列を生成する場合があるかと思いますが、
そのような時はrange関数を使うと楽です。

サンプルコード 実行結果
array(5) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(2)
  [3]=>
  int(3)
  [4]=>
  int(4)
}
array(5) {
  [0]=>
  int(0)
  [1]=>
  int(1)
  [2]=>
  int(2)
  [3]=>
  int(3)
  [4]=>
  int(4)
}
速度調査もしてみます

サンプルコード 実行結果
BenchMark 1     average:1.6927280902863
BenchMark 2     average:1.0643670082092
速度的にも後者のほうが良いですね。

2013年3月12日火曜日

Validation その2

今回は配列の判定です。これも簡単ですね。

取得したい値と弾きたい値の仕様について考えましょう。

取得したい値

  • array
弾きたい値
  • NULL
  • integer
  • float
  • string
  • boolean
  • object
  • resouce

サンプルコード 実行結果
NULL : array(0) {
}
"" : array(0) {
}
"a" : array(0) {
}
0 : array(0) {
}
1 : array(0) {
}
+1 : array(0) {
}
-1 : array(0) {
}
0x0A : array(0) {
}
012 : array(0) {
}
0.1 : array(0) {
}
1e3 : array(0) {
}
1e-3 : array(0) {
}
array("") : array(0) {
}
array("a") : array(1) {
  [0]=>
  string(1) "a"
}
array(array("a")) : array(1) {
  [0]=>
  array(1) {
    [0]=>
    string(1) "a"
  }
}
TRUE : array(0) {
}
FALSE : array(0) {
}
object : array(0) {
}
fopen : array(0) {
}
ちなみに、実際に使用するときは以下のように判別して使用します。