Oggetti PHP vs array: confronto delle prestazioni durante l'iterazione


91

Ho una quantità enorme di oggetti PHP per una rete neurale per la quale devo iterare ed eseguire alcuni calcoli. Mi chiedevo se sarebbe stato meglio usare un array associativo su istanze di classi?

Ho a che fare con 3640oggetti intorno e iterando intorno ai 500tempi (nella migliore delle ipotesi) in aggiunta a questo, quindi qualsiasi microottimizzazione aiuta molto. Sarebbe inevitabilmente più veloce da fare $object['value']di $object->value?

Modifica: quindi sono entrambi uguali. Ma immagino che ci sarebbe un piccolo sovraccarico per il costruttore? Ad ogni modo, non penso di voler scambiare le mie bellissime classi con array sporchi: P.

Risposte:


65

Basato nel codice di Quazzle, ho eseguito il codice successivo (5.4.16 windows 64bits):

<?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);  
?>

E ho ottenuto il risultato successivo:

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
)

Conclusione per php 5.4

  1. La classe è veloce rispetto agli array (ma marginalmente).
  2. stdClass è il male.
  3. La classe utilizza meno memoria rispetto agli array. (circa il 30-40% in meno !!)

ps: come nota, se la classe è definita ma i membri quindi, l'uso di questa classe è più lento. Utilizza anche più memoria. Apparentemente il segreto è definire i membri

Aggiornare

Ho aggiornato da php 5.4 a php 5.5 (5.5.12 x86 windows).

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
)

Conclusione per php 5.5

  1. Per gli array, PHP 5.5 è più veloce di PHP 5.4, per gli oggetti è praticamente lo stesso
  2. La classe è più lenta degli array grazie all'ottimizzazione di PHP 5.5 e degli array.
  3. stdClass è il male.
  4. La classe utilizza ancora meno memoria rispetto agli array. (circa il 30-40% in meno !!).
  5. SplFixedArray è simile all'uso di una classe ma utilizza più memoria.

Un complimento a lei, buon signore. Sarebbe interessante estenderlo ad array annidati, ecc. Siti interessanti per altre prestazioni PHP: phpbench.com php-benchmark-script.com ma mi piace che tu abbia usato anche la memoria.
Heath N

2
Con PHP7 la differenza tra array e oggetti è diventata più significativa. Lo script mostra una differenza del 30% di runtime e del 60% di memoria. Questo è solo il mio computer, ma come regola generale: non utilizzare array come strutture. Usa invece gli oggetti :)
KingCrunch

Gli oggetti sono diversi dalle classi in questo caso?
Matt G

Aggiungendo questo segnalibro nella speranza di qualsiasi aggiornamento PHP7. E forse il prossimo PHP8, se applicabile. @magallanes
s3c

8

Ho usato questo codice per la "profilazione" (1000 istanze, 1000.000 letture / scritture):

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();

Emette nel mio LINUX che ospita questa roba:

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

quindi in conclusione: gli oggetti sono più lenti anche su PHP 5.2. Non usare oggetti a meno che tu non abbia davvero bisogno delle loro funzionalità oop.


7
da levans utente stackoverflow.com/users/1473035/levans : ho corso questo con 5.3.8 e gli oggetti erano più lenti, ,51839280128479 per gli array vs ,85355806350708 per gli oggetti. L'ho eseguito anche su 5.4.13 e ho ottenuto i risultati opposti, probabilmente a causa delle ottimizzazioni di classe eseguite in 5.4, 0.6256799697876 per gli array rispetto a 0.43650078773499. Quindi sembra che i tavoli siano cambiati e gli oggetti siano ora la strada da percorrere.
Jean-Bernard Pellerin

1
Buona risposta, ho appena provato su XAMPP (Apache) e ho ottenuto i seguenti risultati: array: 0.5174868106842 Array ([aaa] => aaa [bbb] => bbb [ccc] => aaabbb) obj: 0.72189617156982 stdClass Object ([aaa] => aaa [bbb] => bbb [ccc] => aaabbb) phpversion 5.4.19
ilhnctn

1
Ho anche eseguito il 5.4.13, ma ho ottenuto l'opposto di Jean-Bernard Pellerin: Arrays: 0.5020840167999 Oggetti: 1.0378720760345 Quindi non mi impegnerei ancora con gli oggetti.
simontemplar

Ho apportato alcune modifiche al codice e Class è veloce di Arrays per php 5.4 (5.4.16 32bits Windows). Ho messo una nuova risposta che spiega il motivo.
magallanes

Risultati PHP 5.5.11: array: 0.17430, oggetti: 0.24183
Lex

3

Uso il codice di magallanes sotto php 7.0.9:

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
)

E utente php 7.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
E non dimenticare che stdClass può usare la chiave numerica della stringa reale. ['1' => 1] verrà memorizzato come [1 => 1], ma possiamo usare $a=new stdClass(); $a->{1} = 1; $b=(array)$a;get real ['1' => 1].
chariothy

2
Quindi, in conclusione .. Gli array sono più veloci del 18% ma consumano 2,7 volte più memoria.
jchook

3

script di magallanes @ PHP 7.3.5

  • SomeClass Object è più veloce e leggero.
  • Array Velocità 1.32x . 2.70x di memoria.
  • stdClass Object Velocità 1,65 volte . 2.94x ​​di memoria.

Uscita grezza:

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

Per chiunque sia ancora interessato a questa domanda :) Ho eseguito il codice Quazzle su PHP 7.1 Ubuntu x64 e ho ottenuto questa risposta:

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
)

Conclusione

L'array prende 4 (!) La memoria rispetto all'oggetto classe.
Oggetto classe leggermente più veloce.
stdClass ancora malvagio © magallanes :)


2

Non ci hai mostrato il codice per come $object->valuefunziona, in quanto potrebbe essere che il backend sia un array, nel qual caso teoricamente l' utilizzo di un array sarebbe più veloce in quanto implica una chiamata di funzione in meno. Il costo per eseguire la ricerca sarà probabilmente enorme rispetto alla chiamata di funzione. Se si tratta di una variabile, ci sarà pochissima differenza poiché gli oggetti e gli array in PHP hanno un'implementazione molto simile.

Se stai esaminando le ottimizzazioni, dovrai creare un profilo per verificare dove viene utilizzata la maggior parte del tempo. Sospetto che cambiare gli oggetti in array non farà alcuna differenza.


Ho assunto che il valore sarebbe una variabile pubblica, quindi sicuramente O (1) mentre la ricerca hash potrebbe non esserlo.
Filip Ekberg

2

Vedo che questo è una specie di vecchio post, quindi ho pensato di aggiornarlo. ecco i miei codici e le mie statistiche, fatto su Zend CE 5.3.21 Ho provato a testare il tutto, archiviando informazioni e recuperandole.

V1: richiede 0,83 sec

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

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

V2: richiede 3.05 sec

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: impiega 1,98 secondi (si noti che il costruttore migliora le prestazioni)

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

Puoi sempre controllare il codice sorgente PHP per micro-prestazioni come questa.

Ma a prima vista, non fare ['valore'] non sarà più veloce perché PHP deve fare una ricerca su dove trovare ['valore'] anche se una ricerca in tabella hash dovrebbe essere O (1), non è garantito. C'è più overhead quando usi Text-index.

Se l'oggetto contiene solo 1 variabile a cui devi accedere, ovvero value, c'è più overhead nell'usare un oggetto.


E dove pensi che vengano cercate le proprietà? Sono anche in una tabella hash ... (anche se questo è più o meno vero solo nel trunk).
Artefacto

1

Bene, oggi mi sono incuriosito sulla base del benchmark @magallanes, quindi l'ho ampliato un po '. Ho aumentato alcuni dei cicli for per evidenziare davvero gli spazi tra le cose. Funziona su Apache 2.4, mod_php e PHP 7.2.

Ecco una tabella di riepilogo per semplificare i risultati:

+---------------------------+---------+-----------------+
|           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

Di seguito è riportato lo script modificato. Volevo testare l'impostazione delle proprietà con metodi e la definizione dei tipi. Sono rimasto molto sorpreso di scoprire che l'uso dei metodi setter aggiunge un successo significativo al codice. Ora, questo è un test delle prestazioni molto molto specifico in cui molte app non lo faranno nemmeno. Ma se hai un sito che gestisce 1000 / req / secondo con 1000 classi che vengono utilizzate con migliaia di oggetti, puoi vedere come ciò potrebbe influire sulle prestazioni.

<?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);

Ed ecco i risultati:

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

Se gli array e le classi hanno le stesse prestazioni, penso che l'uso di oggetti di classi predefinite per archiviare / passare dati aziendali renderebbe il nostro programma più logico e il codice più leggibile.

Oggi, con ide moderni come Eclipse, Netbean ... è molto comodo sapere quali informazioni trasporta un oggetto (di classe predefinita) ma gli array non lo sono

Ad esempio: con array

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

Con oggetto

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
    ...
}

Ciò contraddice le altre risposte provate e non ha prove. Quindi questa è più una convinzione ideologica, che una risposta reale.
focaccine
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.