Objets PHP vs tableaux - Comparaison des performances lors de l'itération

91

J'ai une énorme quantité d'objets PHP pour un réseau de neurones sur lequel je dois parcourir et effectuer des calculs. Je me demandais si je serais mieux d'utiliser un tableau associatif sur des instances de classes?

Je m'occupe d' 3640objets et d'itérations dans les 500temps (au mieux) en plus de cela, donc toute micro-optimisation aide beaucoup. Serait-il inévitablement plus rapide à faire $object['value']que $object->value?

Edit: Donc, ils sont tous les deux identiques. Mais je suppose qu'il y aurait un peu de frais généraux pour le constructeur? Quoi qu'il en soit, je ne pense pas que je veux échanger mes belles classes contre des tableaux sales: P

Louis
la source

Réponses:

65

Basé sur le code de Quazzle, j'ai exécuté le code suivant (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);  
?>

Et j'ai obtenu le résultat suivant:

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
)

Conclusion pour php 5.4

  1. La classe est plus rapide que les tableaux (mais marginalement).
  2. stdClass est le mal.
  3. La classe utilise moins de mémoire que les tableaux. (environ 30 à 40% de moins !!)

ps: pour mémoire, si la classe est définie mais les membres alors, l'utilisation de cette classe est plus lente. Il utilise également plus de mémoire. Apparemment, le secret est de définir les membres

Mise à jour

J'ai mis à jour de php 5.4 à 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
)

Conclusion pour php 5.5

  1. Pour les tableaux, PHP 5.5 est plus rapide que PHP 5.4, pour l'objet c'est à peu près la même chose
  2. La classe est plus lente que les tableaux grâce à l'optimisation de PHP 5.5 et des tableaux.
  3. stdClass est le mal.
  4. La classe utilise toujours moins de mémoire que les tableaux. (environ 30 à 40% de moins !!).
  5. SplFixedArray est similaire à l'utilisation d'une classe mais il utilise plus de mémoire.
magallanes
la source
Un bien fait à vous mon bon monsieur. Ce serait intéressant d'étendre cela aux tableaux imbriqués, etc. Sites intéressants pour d'autres performances PHP: phpbench.com php-benchmark-script.com mais j'aime bien que vous utilisiez aussi la mémoire.
Heath N
2
Avec PHP7, la différence entre les tableaux et les objets est devenue plus significative. Votre script montre une différence de 30% d'exécution et de 60% de mémoire. C'est juste ma machine, mais en règle générale: n'utilisez pas de tableaux comme structs. Utilisez des objets à la place :)
KingCrunch
Les objets sont-ils différents des classes dans ce cas?
Matt G
Mettre en signet ceci dans l'espoir d'une mise à jour PHP7. Et peut-être le prochain PHP8, le cas échéant. @magallanes
s3c
8

J'ai utilisé ce code pour le "profilage" (1000 instances, 1000.000 lectures / écritures):

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

Il sort dans mon LINUX hébergeant ce truc:

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

donc en conclusion: les objets sont plus lents même sur PHP 5.2. N'utilisez pas d'objets à moins que vous n'ayez vraiment besoin de leurs fonctionnalités oop.

Éblouir
la source
7
de l'utilisateur levans stackoverflow.com/users/1473035/levans : J'ai exécuté ceci avec 5.3.8 et les objets étaient plus lents, 0.51839280128479 pour les tableaux vs 0.85355806350708 pour les objets. Je l'ai également exécuté sur 5.4.13 et j'ai obtenu les résultats opposés, probablement en raison des optimisations de classe effectuées dans 5.4, 0.6256799697876 pour les tableaux contre 0.43650078773499. Ainsi, il semble que les tables ont changé et que les objets sont maintenant la voie à suivre.
Jean-Bernard Pellerin
1
Bonne réponse, je viens de tester sur XAMPP (Apache) et j'ai obtenu les résultats ci-dessous: arrays: 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
J'ai aussi couru sur la version 5.4.13, mais j'ai eu l'opposé de Jean-Bernard Pellerin: Tableaux: 0.5020840167999 Objets: 1.0378720760345 Donc je ne m'engagerais pas encore sur des objets.
simontemplar du
J'ai fait quelques changements dans le code et Class est rapide que Arrays pour php 5.4 (5.4.16 32bits Windows). J'ai mis une nouvelle réponse qui explique la raison.
magallanes
Résultats PHP 5.5.11: Tableaux: 0.17430, Objets: 0.24183
Lex
3

J'utilise le code de magallanes sous 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
)

Et l'utilisateur 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
la source
1
Et n'oubliez pas que stdClass peut utiliser une clé numérique de chaîne réelle. ['1' => 1] sera stocké sous [1 => 1], mais nous pouvons utiliser $a=new stdClass(); $a->{1} = 1; $b=(array)$a;get real ['1' => 1].
chariothy
2
Donc, en conclusion ... Les tableaux sont 18% plus rapides mais consomment 2,7 fois plus de mémoire.
jchook
3

script de magallanes @ PHP 7.3.5

  • SomeClass Object est le plus rapide et le plus léger.
  • Array Vitesse 1,32x . Mémoire 2.70x .
  • stdClass Object Vitesse 1,65x . Mémoire 2,94x .

Sortie brute:

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
la source
3

Pour tous ceux qui sont toujours intéressés par cette question :) J'ai exécuté du code Quazzle sur PHP 7.1 Ubuntu x64 et j'ai obtenu cette réponse:

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
)

Conclusion

Array prend 4 (!) La mémoire que l'objet de classe.
Objet de classe légèrement plus rapide.
stdClass toujours maléfique © magallanes :)

ShadowMad
la source
2

Vous ne nous avez pas montré le code du $object->valuefonctionnement, car il se pourrait que le backend soit un tableau auquel cas l' utilisation d'un tableau serait théoriquement plus rapide car elle implique un appel de fonction en moins. Le coût de la recherche sera probablement énorme par rapport à l'appel de fonction. S'il s'agit d'une variable, il y aura très peu de différence car les objets et les tableaux en PHP ont une implémentation très similaire.

Si vous recherchez des optimisations, vous devrez établir un profil pour vérifier où la majorité du temps est utilisée. Je soupçonne que changer des objets en tableaux ne fera aucune différence majeure.

Yacoby
la source
J'ai supposé que la valeur serait une variable publique, donc définitivement O (1) alors que la recherche de hachage pourrait ne pas l'être.
Filip Ekberg
2

Je vois que c'est une sorte de vieux message alors j'ai pensé que je vais le mettre à jour. voici mes codes et statistiques, réalisés sur Zend CE 5.3.21 J'ai essayé de tout tester, en stockant les informations et en les récupérant.

V1: prend 0,83 s

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

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

V2: prend 3,05 s

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: prend 1,98 sec (notez que le constructeur améliore les performances)

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
la source
1

Vous pouvez toujours vérifier le code source PHP pour des fonctionnalités de micro-performance comme celle-là.

Mais à première vue, ne pas faire ['value'] ne sera pas plus rapide car PHP doit faire une recherche pour savoir où trouver ['value'] même si une recherche de table de hachage devrait être O (1), ce n'est pas garanti. Il y a plus de surcharge lorsque vous utilisez Text-index.

Si l'objet ne contient qu'une seule variable à laquelle vous devez accéder et qui est value, l'utilisation d'un objet est plus lourde.

Filip Ekberg
la source
Et où pensez-vous que les propriétés sont recherchées? Ils sont également dans une table de hachage ... (bien que ce ne soit plus ou moins vrai dans le coffre).
Artefacto
1

Eh bien, je suis devenu curieux aujourd'hui en me basant sur le benchmark @magallanes, alors je l'ai un peu développé. J'ai augmenté certaines des boucles for pour vraiment mettre en évidence les écarts entre les choses. Cela fonctionne sur Apache 2.4, mod_php et PHP 7.2.

Voici un tableau récapitulatif pour faciliter les résultats:

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

Voici le script modifié. Je voulais tester les propriétés des paramètres avec des méthodes et des types de définition. J'ai été très surpris de constater que l'utilisation des méthodes de setter ajoute un succès significatif au code. Maintenant accordé, il s'agit d'un test de performances très très spécifique où de nombreuses applications n'atteindront même pas cela. Mais si vous avez un site qui gère 1000 / reqs / seconde avec 1000 classes qui sont utilisées avec des milliers d'objets, vous pouvez voir comment cela peut affecter les performances.

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

Et voici les résultats:

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
la source
0

Si les tableaux et les classes ont les mêmes performances, je pense que l'utilisation d'objets de classes prédéfinies pour stocker / transmettre des données métier rendrait notre programme plus logique et le code plus lisible.

Aujourd'hui, avec des idées modernes comme Eclipse, Netbean ... il est très pratique de savoir quelles informations un objet (de classe prédéfinie) porte mais les tableaux ne le sont pas

Par exemple: avec tableau

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

Avec objet

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
la source
Cela contredit les autres réponses prouvées et n'a aucune preuve. C'est donc plus une croyance idéologique qu'une réponse réelle.
scones