Objetos PHP vs matrices: comparación de rendimiento durante la iteración

91

Tengo una gran cantidad de objetos PHP para una red neuronal para la que tengo que iterar y realizar algunas operaciones matemáticas. Me preguntaba si estaría mejor usando una matriz asociativa sobre instancias de clases.

Estoy lidiando con 3640objetos e iterando alrededor de los 500tiempos (en el mejor de los casos) además de eso, por lo que cualquier microoptimización ayuda mucho. Sería inevitablemente ser más rápido que hacer $object['value']que $object->value?

Editar: Entonces ambos son iguales. ¿Pero supongo que habría un poco de sobrecarga para el constructor? De cualquier manera, no creo que quiera cambiar mis hermosas clases por matrices sucias: P

Luis
fuente

Respuestas:

65

Basado en el código de Quazzle, ejecuté el siguiente código (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);  
?>

Y obtuve el siguiente resultado:

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
)

Conclusión para php 5.4

  1. La clase es más rápida que las matrices (pero marginalmente).
  2. stdClass es malvado.
  3. La clase usa menos memoria que las matrices. (¡alrededor del 30-40% menos!)

ps: como nota, si la clase está definida pero los miembros entonces, el uso de esta clase es más lento. También usa más memoria. Al parecer el secreto es definir a los miembros

Actualizar

Actualicé de 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
)

Conclusión para php 5.5

  1. Para matrices, PHP 5.5 es más rápido que PHP 5.4, para objetos es más o menos lo mismo
  2. La clase es más lenta que las matrices gracias a la optimización de PHP 5.5 y las matrices.
  3. stdClass es malvado.
  4. La clase todavía usa menos memoria que las matrices. (¡¡alrededor del 30-40% menos !!).
  5. SplFixedArray es similar a usar una clase pero usa más memoria.
magallanes
fuente
Enhorabuena, buen señor. Sería interesante expandir esto a matrices anidadas, etc. Sitios interesantes para otro rendimiento de PHP: phpbench.com php-benchmark-script.com pero me gusta que también utilizaste memoria.
Heath N
2
Con PHP7, la diferencia entre matrices y objetos se volvió más significativa. Su script muestra una diferencia de 30% en tiempo de ejecución y 60% de memoria. Esa es solo mi máquina, pero como regla general: no use matrices como estructuras. Use objetos en su lugar :)
KingCrunch
¿Los objetos son diferentes a las clases en este caso?
Matt G
Marcando esto con la esperanza de cualquier actualización de PHP7. Y quizás el próximo PHP8, cuando corresponda. @magallanes
s3c
8

Usé este código para "crear perfiles" (1000 instancias, 1000.000 lecturas / escrituras):

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

Aparece en mi LINUX que aloja estas cosas:

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

así que en conclusión: los objetos son más lentos incluso en PHP 5.2. No uses objetos a menos que realmente necesites sus funciones de oop.

Temblor
fuente
7
del usuario levans stackoverflow.com/users/1473035/levans : ejecuté esto con 5.3.8 y los objetos eran más lentos, 0.51839280128479 para matrices frente a 0.85355806350708 para objetos. También lo ejecuté en 5.4.13 y obtuve los resultados opuestos, probablemente debido a las optimizaciones de clase realizadas en 5.4, 0.6256799697876 para matrices frente a 0.43650078773499. Por lo tanto, parece que las tornas han cambiado y los objetos son ahora el camino a seguir.
Jean-Bernard Pellerin
1
Buena respuesta, acabo de probar en XAMPP (Apache) y obtuve los resultados a continuación: matrices: 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
También ejecuté 5.4.13, pero obtuve lo opuesto a Jean-Bernard Pellerin: Arrays: 0.5020840167999 Objects: 1.0378720760345 Así que no me comprometería con objetos todavía.
simontemplar
Hice algunos cambios en el código y Class es más rápido que Arrays para php 5.4 (5.4.16 32bits Windows). Pongo una nueva respuesta que explica el motivo.
magallanes
Resultados de PHP 5.5.11: Matrices: 0.17430, Objetos: 0.24183
Lex
3

Yo uso el código de magallanes bajo 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
)

Y el usuario 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
)                      
chariothy
fuente
1
Y no olvide que stdClass puede usar una clave numérica de cadena real. ['1' => 1] se almacenará como [1 => 1], pero podemos usar $a=new stdClass(); $a->{1} = 1; $b=(array)$a;get real ['1' => 1].
chariothy
2
Entonces, en conclusión ... Las matrices son un 18% más rápidas pero consumen 2,7 veces más memoria.
jchook
3

script de magallanes @ PHP 7.3.5

  • SomeClass Object es más rápido y ligero.
  • Array Velocidad de 1,32x . 2.70x de memoria.
  • stdClass Object Velocidad de 1,65x . 2.94x ​​de memoria.

Salida sin procesar:

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
)
Qcho
fuente
3

Para cualquiera que todavía esté interesado en esta pregunta :) Ejecuté el código Quazzle en PHP 7.1 Ubuntu x64 y obtuve esta respuesta:

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
)

Conclusión

Array toma 4 (!) La memoria que el objeto de clase.
Objeto de clase ligeramente más rápido.
stdClass sigue siendo malvado © magallanes :)

ShadowMad
fuente
2

No nos ha mostrado el código de cómo $object->valuefunciona, ya que podría ser que el backend sea una matriz, en cuyo caso, teóricamente, usar una matriz sería más rápido ya que implica una llamada de función menos. El costo de realizar la búsqueda probablemente sea enorme en comparación con la llamada a la función. Si es una variable, habrá muy poca diferencia ya que los objetos y las matrices en PHP tienen una implementación muy similar.

Si está buscando optimizaciones, deberá crear un perfil para verificar dónde se está utilizando la mayor parte del tiempo. Sospecho que cambiar objetos por matrices no supondrá una gran diferencia.

Yacoby
fuente
Supuse que el valor sería una variable pública, por lo tanto, definitivamente O (1), mientras que la búsqueda de hash podría no serlo.
Filip Ekberg
2

Veo que esta es una publicación antigua, así que pensé en actualizarla. aquí están mis códigos y estadísticas, hechos en Zend CE 5.3.21 Traté de probar todo, almacenando información y retirándola.

V1: tarda 0,83 segundos

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

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

V2: tarda 3,05 segundos

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: tarda 1,98 segundos (tenga en cuenta que el constructor mejora el rendimiento)

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;
  }
}
Nir
fuente
1

Siempre puede consultar el código fuente de PHP para obtener características de micro rendimiento como esa.

Pero a primera vista, no hacer ['valor'] no será más rápido porque PHP necesita hacer una búsqueda sobre dónde encontrar ['valor'] incluso aunque una búsqueda de tabla hash debería ser O (1), eso no está garantizado. Hay más sobrecarga cuando usa Text-index.

Si el objeto solo contiene 1 variable a la que necesita acceder, que es el valor, hay más gastos generales al usar un objeto.

Filip Ekberg
fuente
¿Y dónde crees que se buscan propiedades? También están en una tabla hash ... (aunque esto solo es más o menos cierto en el tronco).
Artefacto
1

Bueno, hoy sentí curiosidad por el punto de referencia de @magallanes, así que lo expandí un poco. Subí algunos de los bucles for para resaltar realmente los espacios entre las cosas. Esto se ejecuta en Apache 2.4, mod_php y PHP 7.2.

Aquí hay una tabla de resumen para facilitar los resultados:

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

A continuación se muestra el script modificado. Quería probar las propiedades de configuración con métodos y definir tipos. Me sorprendió mucho descubrir que el uso de métodos de establecimiento agrega un impacto significativo al código. Ahora bien, esta es una prueba de rendimiento muy muy específica en la que muchas aplicaciones ni siquiera lo alcanzarán. Pero si tiene un sitio que maneja 1000 / reqs / second con 1000 clases que se usan con miles de objetos, puede ver cómo esto puede afectar el rendimiento.

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

Y aquí están los resultados:

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
)

n0nag0n
fuente
0

Si las matrices y las clases tienen el mismo rendimiento, creo que usar objetos de clases predefinidas para almacenar / pasar datos comerciales haría que nuestro programa fuera más lógico y el código más legible.

Hoy en día, con ide moderno como Eclipse, Netbean ... es muy conveniente saber qué información lleva un objeto (de clase predefinida) pero las matrices no lo son

Por ejemplo: con matriz

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

Con objeto

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
    ...
}
trungnnh
fuente
Esto contradice las otras respuestas probadas y no tiene pruebas. Así que esto es más una creencia ideológica que una respuesta real.
bollos