PHPオブジェクトと配列—反復中のパフォーマンスの比較


91

ニューラルネットワーク用のPHPオブジェクトが大量にあり、繰り返し処理していくつかの計算を実行する必要があります。クラスのインスタンスよりも連想配列を使用したほうがいいのではないかと思っていました。

私は3640オブジェクトの周りを扱っており500、その上で(せいぜい)何度も繰り返しているので、マイクロ最適化は大いに役立ちます。それは必然的に速く行うことであろう$object['value']よりも$object->value

編集:だから、それらは両方とも同じです。しかし、コンストラクターには少しオーバーヘッドがあると思いますか?いずれにせよ、私は自分の美しいクラスをダーティな配列と交換したくないと思います:P

回答:


65

Quazzleのコードに基づいて、次のコード(5.4.16ウィンドウ64ビット)を実行しました。

<?php
class SomeClass {
    public $aaa;
    public $bbb;
    public $ccc;
    }

function p($i) {
  echo '<pre>';
  print_r($i);
  echo '</pre>';
}


$t0 = microtime(true);
$arraysOf=array();
$inicio=memory_get_usage(); 
for ($i=0; $i<1000; $i++) {
    $z = array();
    for ($j=0; $j<1000; $j++) {
        $z['aaa'] = 'aaa';
        $z['bbb'] = 'bbb';
        $z['ccc'] = $z['aaa'].$z['bbb'];            
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>arrays: '.(microtime(true) - $t0)."</p>";
echo '<p>memory: '.($fin-$inicio)."</p>";
p($z);

$t0 = microtime(true);
$arraysOf=array();
$inicio=memory_get_usage(); 
for ($i=0; $i<1000; $i++) {
    $z = new SomeClass();
    for ($j=0; $j<1000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>arrays: '.(microtime(true) - $t0)."</p>";
echo '<p>memory: '.($fin-$inicio)."</p>";
p($z);

$t0 = microtime(true);
$arraysOf=array();
$inicio=memory_get_usage(); 
for ($i=0; $i<1000; $i++) {
    $z = new stdClass();
    for ($j=0; $j<1000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>arrays: '.(microtime(true) - $t0)."</p>";
echo '<p>memory: '.($fin-$inicio)."</p>";
p($z);  
?>

そして、私は次の結果を得ました:

arrays: 1.8451430797577

memory: 460416

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 1.8294548988342

memory: 275696

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 2.2577090263367

memory: 483648

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

php5.4の結論

  1. クラスは配列よりも高速です(ただし、わずかです)。
  2. stdClassは悪です。
  3. クラスは配列よりも少ないメモリを使用します。(約30〜40%少ない!!)

ps:注として、クラスが定義されているがメンバーが定義されている場合、このクラスの使用は遅くなります。また、より多くのメモリを使用します。 どうやら秘密はメンバーを定義することです

更新

php5.4からphp5.5(5.5.12 x86ウィンドウ)に更新しました。

arrays: 1.6465699672699

memory: 460400

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 1.8687851428986

memory: 363704

SplFixedArray Object
(
    [0] => aaa
    [1] => bbb
    [2] => aaabbb
)

arrays: 1.8554251194

memory: 275568

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 2.0101680755615

memory: 483656

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

php5.5の結論

  1. 配列の場合、PHP5.5はPHP5.4よりも高速ですが、オブジェクトの場合はほとんど同じです。
  2. PHP 5.5と配列の最適化のおかげで、クラスは配列よりも遅くなります。
  3. stdClassは悪です。
  4. クラスは依然として配列よりも少ないメモリを使用します。(約30〜40%少ない!!)。
  5. SplFixedArrayはクラスの使用に似ていますが、より多くのメモリを使用します。

よくやったね。これをネストされた配列などに拡張するのは興味深いでしょう。他のPHPパフォーマンスの興味深いサイト:phpbench.com php-benchmark-script.comですが、メモリも使用しているのが好きです。
ヒースN

2
PHP7では、配列とオブジェクトの違いがより重要になりました。スクリプトは、30%のランタイムと60%のメモリの違いを示しています。これは私のマシンですが、経験則として、配列を構造体として使用しないでください。代わりにオブジェクトを使用してください:)
KingCrunch 2016年

この場合、オブジェクトはクラスとは異なりますか?
マットG

PHP7のアップデートを期待してこれをブックマークします。そして、該当する場合は、おそらく今後のPHP8です。@magallanes
s3c

8

私はこのコードを「プロファイリング」に使用しました(1000インスタンス、1000.000読み取り/書き込み):

function p($i) {
  echo '<pre>';
  print_r($i);
  echo '</pre>';
}


$t0 = microtime(true);
for ($i=0; $i<1000; $i++) {
    $z = array();
    for ($j=0; $j<1000; $j++) {
        $z['aaa'] = 'aaa';
        $z['bbb'] = 'bbb';
        $z['ccc'] = $z['aaa'].$z['bbb'];
    }
}
echo '<p>arrays: '.(microtime(true) - $t0);
p($z);

$t0 = microtime(true);
for ($i=0; $i<1000; $i++) {
    $z = (object) null;
    for ($j=0; $j<1000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;
    }
}
echo '<p>obj: '.(microtime(true) - $t0);
p($z);

echo '<p> phpversion '.phpversion();

これは、このようなものをホストしている私のLINUXに出力されます。

arrays: 1.1085488796234

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
obj: 1.2824709415436

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
phpversion 5.2.17

したがって、結論として、PHP5.2でもオブジェクトは遅くなります。oop機能が本当に必要でない限り、オブジェクトを使用しないでください。


7
ユーザーlevansからstackoverflow.com/users/1473035/levans:これを5.3.8で実行したところ、オブジェクトの速度が遅くなりました。配列の場合は0.51839280128479、オブジェクトの場合は0.85355806350708でした。また、5.4.13で実行したところ、逆の結果が得られました。これは、5.4で行われたクラスの最適化、配列の0.6256799697876と0.43650078773499が原因である可能性があります。したがって、テーブルが回転し、オブジェクトが移動する方法になっているように見えます。
Jean-Bernard Pellerin 2013

1
良い答えです。XAMPP(Apache)でテストしたところ、次の結果が得られました。配列:0.5174868106842配列([aaa] => aaa [bbb] => bbb [ccc] => aaabbb)obj:0.72189617156982 stdClass Object([aaa] => aaa [bbb] => bbb [ccc] => aaabbb)phpversion 5.4.19
ilhnctn

1
私も5.4.13で実行しましたが、Jean-Bernard Pellerinの反対を取得しました。配列:0.5020840167999オブジェクト:1.0378720760345したがって、まだオブジェクトにコミットしません。
simontemplar 2013年

コードにいくつかの変更を加えましたが、クラスはphp 5.4(5.4.16 32ビットWindows)の配列よりも高速です。その理由を説明する新しい答えを出しました。
magallanes 2014年

PHP 5.5.11結果:配列:0.17430、オブジェクト:0.24183
Lex

3

私はphp7.0.9でmagallanesのコードを使用しています:

arrays: 0.19802498817444

memory: 324672

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.18602299690247

memory: 132376

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.1950249671936

memory: 348296

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

そしてユーザーphp7.1.3:

arrays: 0.59932994842529
memory: 444920
Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 0.72895789146423
memory: 164512

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

arrays: 0.61777496337891
memory: 484416
stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)                      

1
また、stdClassは実際の文字列の数字キーを使用できることを忘れないでください。['1' => 1]は[1 => 1]として保存されますが、$a=new stdClass(); $a->{1} = 1; $b=(array)$a;get real ['1' => 1]を使用できます。
チャリオシー2017年

2
したがって、結論として、配列は18%高速ですが、2.7倍のメモリを消費します。
jchook

3

magallanesのスクリプト@PHP 7.3.5

  • SomeClass Object 最速かつ最軽量です。
  • Array 1.32倍の速度。2.70xメモリ。
  • stdClass Object 1.65倍の速度。2.94xメモリ。

生の出力:

arrays: 0.064794063568115
memory: 444920
Array (
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.048975944519043
memory: 164512
SomeClass Object (
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.081161022186279
memory: 484416
stdClass Object (
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

3

この質問にまだ興味がある人のために:)私はPHP7.1 Ubuntu x64でQuazzleコードを実行し、この答えを得ました:

arrays: 0.24848890304565

memory: 444920

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.23238587379456

memory: 164512

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)
arrays: 0.24422693252563

memory: 484416

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

結論

配列はクラスオブジェクトよりも4(!)メモリを取ります。
クラスオブジェクトはわずかに高速です。
stdClassはまだ邪悪©magallanes :)


2

$object->valueバックエンドが配列である可能性があるため、動作のコードを示していません。その場合、関数呼び出しが1つ少なくなるため、理論的には配列を使用する方が高速になります。ルックアップを実行するコストは、関数呼び出しと比較しておそらく莫大なものになります。変数の場合、PHPのオブジェクトと配列の実装は非常に似ているため、違いはほとんどありません。

最適化を検討している場合は、プロファイルを作成して、ほとんどの時間が使用されている場所を確認する必要があります。オブジェクトを配列に変更しても大きな違いはないと思います。


値はパブリック変数であると想定したため、ハッシュルックアップはそうではない可能性がありますが、間違いなくO(1)です。
フィリップエクバーグ2010

2

これは古い投稿のようなものだと思うので、更新しようと思いました。これが私のコードと統計です。ZendCE5.3.21で実行しました。すべてをテストし、情報を保存して元に戻しました。

V1:0.83秒かかります

for ($i=1; $i<1000000; $i++) {
  $a = get_one();
  $b = $a[0];
  $b = $a[1];
}

function get_one() {
  return array(1,1);
}

V2:3.05秒かかります

for ($i=1; $i<1000000; $i++) {
  $a = get_one();
  $b = $a->v;
  $b = $a->k;
}

function get_one() {
  $ret = new test();
  $ret->v = 1;
  $reb->k = 1;
  return $ret;
}

class test {
  public $v;
  public $k;
}

V3:1.98秒かかります(コンストラクターによってパフォーマンスが向上することに注意してください)

for ($i=1; $i<1000000; $i++) {
  $a = get_one();
  $b = $a->v;
  $b = $a->k;
}

function get_one() {
  return new test(1,1);
}

class test {
  public $v;
  public $k;
  public function __construct($v, $k) {
    $this->v = $v;
    $this->k = $k;
  }
}

1

そのようなマイクロパフォーマンス機能については、PHPソースコードをいつでも確認できます。

しかし、一見したところ、ハッシュテーブルルックアップがO(1)である必要がある場合でも、PHPは['value']の場所をルックアップする必要があるため、['value']を実行しない方が速くなりません。これは保証されません。Text-indexを使用すると、より多くのオーバーヘッドが発生します。

アクセスする必要のある変数が1つだけオブジェクトに含まれている場合、それは値です。オブジェクトを使用すると、オーバーヘッドが大きくなります。


そして、プロパティはどこで検索されると思いますか?それらもハッシュテーブルにあります...(これはトランクでは多かれ少なかれ真実ですが)。
Artefacto 2010

1

今日は@magallanesベンチマークに基づいて興味を持ったので、少し拡張しました。forループのいくつかをアップして、物事間のギャップを実際に強調しました。これは、Apache 2.4、mod_php、およびPHP7.2で実行されています。

結果を簡単にするための要約表を次に示します。

+---------------------------+---------+-----------------+
|           Test            | Memory  |      Time       |
+---------------------------+---------+-----------------+
| Array                     | 2305848 | 9.5637300014496 |
| stdClass                  | 2505824 | 11.212271928787 |
| SomeClass                 |  164920 | 3.9636149406433 | <-- *
| AnotherClass              | 2563136 | 10.872401237488 |
| SetterClass               |  905848 | 59.879059791565 |
| SetterClassDefineReturn   |  905792 | 60.484427213669 |
| SetterClassSetFromParam   |  745792 | 62.783381223679 |
| SetterClassSetKeyAndParam |  745824 | 72.155715942383 |
+---------------------------+---------+-----------------+
* - Winner winner chicken dinner

以下は変更されたスクリプトです。メソッドを使用してプロパティを設定し、型を定義することをテストしたかったのです。セッターメソッドを使用すると、コードに大きなヒットが追加されることに非常に驚きました。これは非常に特殊なパフォーマンステストであり、多くのアプリがこれにヒットすることはありません。ただし、1000個のオブジェクトで使用される1000個のクラスで1000 / reqs / secondを処理するサイトがある場合、これがパフォーマンスにどのように影響するかを確認できます。

<?php

set_time_limit(500);

class SomeClass {
    public $aaa;
    public $bbb;
    public $ccc;
}
    
class AnotherClass {
}

class SetterClass {
    public $aaa;
    public $bbb;
    public $ccc;

    public function setAAA() {
        $this->aaa = 'aaa';
    }

    public function setBBB() {
        $this->bbb = 'bbb';
    }

    public function setCCC() {
        $this->ccc = $this->aaa.$this->bbb;
    }
}

class SetterClassDefineReturn {
    public $aaa;
    public $bbb;
    public $ccc;

    public function setAAA():void {
        $this->aaa = 'aaa';
    }

    public function setBBB():void {
        $this->bbb = 'bbb';
    }

    public function setCCC():void {
        $this->ccc = $this->aaa.$this->bbb;
    }
}

class SetterClassSetFromParam {
    public $aaa;
    public $bbb;
    public $ccc;

    public function setAAA(string $val): void {
        $this->aaa = $val;
    }

    public function setBBB(string $val): void {
        $this->bbb = $val;
    }

    public function setCCC(string $val): void {
        $this->ccc = $val;
    }
}

class SetterClassSetKeyAndParam {
    public $aaa;
    public $bbb;
    public $ccc;

    public function set(string $key, string $val): void {
        $this->{$key} = $val;
    }
}

function p($i) {
  echo '<pre>';
  print_r($i);
  echo '</pre>';
  echo '<hr>';
}

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<1000; $i++) {
    $z = new SomeClass();
    for ($j=0; $j<10000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new AnotherClass();
    for ($j=0; $j<5000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new SetterClass();
    for ($j=0; $j<5000; $j++) {
        $z->setAAA();
        $z->setBBB();
        $z->setCCC();          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new SetterClassDefineReturn();
    for ($j=0; $j<5000; $j++) {
        $z->setAAA();
        $z->setBBB();
        $z->setCCC();          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new SetterClassSetFromParam();
    for ($j=0; $j<5000; $j++) {
        $z->setAAA('aaa');
        $z->setBBB('bbb');
        $z->setCCC('aaabbb');          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';

p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new SetterClassSetKeyAndParam();
    for ($j=0; $j<5000; $j++) {
        $z->set('aaa', 'aaa');
        $z->set('bbb', 'bbb');  
        $z->set('ccc', 'aaabbb');        
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = new stdClass();
    for ($j=0; $j<5000; $j++) {
        $z->aaa = 'aaa';
        $z->bbb = 'bbb';
        $z->ccc = $z->aaa.$z->bbb;          
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z); 


$t0 = microtime(true);
$arraysOf=[];
$inicio=memory_get_usage(); 
for ($i=0; $i<5000; $i++) {
    $z = [];
    for ($j=0; $j<5000; $j++) {
        $z['aaa'] = 'aaa';
        $z['bbb'] = 'bbb';
        $z['ccc'] = $z['aaa'].$z['bbb'];            
    }
    $arraysOf[]=$z;
}
$fin=memory_get_usage();    
echo '<p>Time Taken (seconds): '.(microtime(true) - $t0).'</p>';
echo '<p>Memory: '.($fin-$inicio).'</p>';
p($z);

そして、これが結果です:

Time Taken (seconds): 3.9636149406433

Memory: 164920

SomeClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

-----

Time Taken (seconds): 10.872401237488

Memory: 2563136

AnotherClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 59.879059791565

Memory: 905848

SetterClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 60.484427213669

Memory: 905792

SetterClassDefineReturn Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 62.783381223679

Memory: 745792

SetterClassSetFromParam Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 72.155715942383

Memory: 745824

SetterClassSetKeyAndParam Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 11.212271928787

Memory: 2505824

stdClass Object
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)

----

Time Taken (seconds): 9.5637300014496

Memory: 2305848

Array
(
    [aaa] => aaa
    [bbb] => bbb
    [ccc] => aaabbb
)


0

配列とクラスのパフォーマンスが同じである場合、ビジネスデータの格納/受け渡しに事前定義されたクラスのオブジェクトを使用すると、プログラムのロジックが増え、コードが読みやすくなると思います。

今日、Eclipse、Netbeanなどの最新のIDEでは、(事前定義されたクラスの)オブジェクトがどのような情報を保持しているかを知ることは非常に便利ですが、配列はそうではありません。

例:アレイあり

function registerCourse(array $student) {
    // Right here I don't know how a $student look like unless doing a print_r() or var_dump()
 ....
}

オブジェクト付き

class Studen {
    private $_name, $_age;
    public function getAge() {}
    public function getName() {}
    ..
}

function registerCourse(Studen $student) {
    // Right here I just Ctrl+Space $student or click "Student" and I know I can get name or age from it
    ...
}

これは他の証明された答えと矛盾し、証拠がありません。したがって、これは実際の答えというよりも、イデオロギー的な信念です。
スコーン2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.