Об'єкти PHP проти масивів - Порівняння продуктивності під час ітерації


91

У мене є величезна кількість PHP-об'єктів для нейронної мережі, для яких я повинен переглядати і виконувати деякі математичні завдання. Мені було цікаво, чи краще мені буде використовувати асоціативний масив над екземплярами класів?

Я маю справу з навколишніми 3640об’єктами і, крім того, повторюю 500часом (в кращому випадку), тому будь-яка мікрооптимізація дуже допомагає. Це було б неминуче швидше зробити, $object['value']ніж $object->value?

Редагувати: Отже, вони обидва однакові. Але, мабуть, було б трохи накладних витрат на конструктор? У будь-якому випадку я не думаю, що хочу продавати в своїх прекрасних класах брудні масиви: P

Відповіді:


65

Виходячи з коду Quazzle, я запустив наступний код (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);  
?>

І я отримав наступний результат:

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
)

Висновок щодо php 5.4

  1. Клас - це швидше, ніж масиви (але незначно).
  2. stdClass - це зло.
  3. Клас використовує менше пам'яті, ніж масиви. (приблизно на 30-40% менше !!)

ps: як примітка, якщо клас визначений, але члени тоді, використання цього класу повільніше. Він також використовує більше пам'яті. Мабуть, секрет полягає у визначенні членів

Оновлення

Я оновився з 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
)

Висновок щодо php 5.5

  1. Для масивів PHP 5.5 швидший, ніж PHP 5.4, для об'єкта - майже однаковий
  2. Клас повільніший за масиви завдяки оптимізації PHP 5.5 та масивів.
  3. stdClass - це зло.
  4. Клас все ще використовує менше пам'яті, ніж масиви. (приблизно на 30-40% менше !!).
  5. SplFixedArray подібний до використання класу, але він використовує більше пам'яті.

Молодці, добрий сер. Було б цікаво розширити це до вкладених масивів тощо. Цікаві сайти для інших показників PHP: phpbench.com php-benchmark-script.com, але мені подобається, що ви також використовували пам’ять.
Heath N,

2
З PHP7 різниця між масивами та об'єктами стала більш значною. Сценарій показує різницю в 30% часу роботи та 60% пам'яті. Це лише моя машина, але, як правило, не використовуйте масиви як конструкції. Використовуйте замість них об’єкти :)
KingCrunch

Чи в цьому випадку об’єкти відрізняються від класів?
Matt 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

отже, на закінчення: об’єкти повільніші навіть на PHP 5.2. Не використовуйте об'єкти, якщо вам дійсно не потрібні їх функції oop.


7
від Леванині користувача stackoverflow.com/users/1473035/levans : Я побіг це з 5.3.8 і об'єкти були повільніше, +0,51839280128479 для масивів проти 0.85355806350708 для об'єктів. Я також запустив його на 5.4.13 і отримав протилежні результати, ймовірно через оптимізацію класу, виконану в 5.4, 0.6256799697876 для масивів проти 0.43650078773499. Таким чином, схоже, таблиці перевернулися, і тепер об’єкти - це шлях.
Жан-Бернар Пеллерін

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, але отримав протилежність Жану-Бернарду Пеллеріну: Масиви: 0.5020840167999 Об'єкти: 1.0378720760345 Отже, я б ще не привласнював об'єкти.
simontemplar

Я вніс деякі зміни в код, і клас швидше, ніж масиви для php 5.4 (5.4.16 32-бітна Windows). Я дав нову відповідь, яка пояснює причину.
magallanes

Результати PHP 5.5.11: Масиви: 0.17430, Об’єкти: 0.24183
Лекс

3

Я використовую код magallanes під 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
)

І користувач 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
І не забувайте, що stdClass може використовувати справжній числовий ключ рядка. ['1' => 1] буде зберігатися як [1 => 1], але ми можемо використовувати $a=new stdClass(); $a->{1} = 1; $b=(array)$a;get real ['1' => 1].
колісниця

2
Отже, на закінчення .. Масиви швидші на 18%, але споживають в 2,7 рази більше пам’яті.
jchook

3

сценарій magallanes @ PHP 7.3.5

  • SomeClass Object найшвидший і найлегший.
  • Array 1,32-кратна швидкість. Пам'ять 2,70x .
  • stdClass Object Швидкість 1,65x . Пам'ять 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

Для тих, кого все ще цікавить це питання :) Я запустив код Quazzle на PHP 7.1 Ubuntu x64 і отримав таку відповідь:

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 still evil © magallanes :)


2

Ви не показали нам код того, як $object->valueпрацює, оскільки могло б бути, що це сервер - це масив, і в цьому випадку теоретично використання масиву було б швидшим, оскільки воно включає один виклик функції менше. Вартість пошуку буде, мабуть, величезна порівняно із викликом функції. Якщо це змінна, різниця буде дуже малою, оскільки об’єкти та масиви в PHP мають дуже подібну реалізацію.

Якщо ви дивитесь на оптимізацію, вам потрібно буде здійснити профіль, щоб перевірити, де використовується більшість часу. Я підозрюю, що зміна об’єктів на масиви не матиме великої різниці.


Я припустив, що значення буде загальнодоступною змінною, тому, безумовно, O (1), хоча хеш-пошуку не може бути.
Філіп Екберг

2

Я бачу, що це якась стара публікація, тому я думав оновити її. ось мої коди та статистика, зроблені на Zend CE 5.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 на наявність таких характеристик мікропродуктивності.

Але на перший погляд, жодне виконання ['value'] не буде швидшим, оскільки PHP повинен зробити пошук, де знайти ['value'], навіть якщо пошук хеш-таблиці має бути O (1), це не гарантовано. Якщо ви використовуєте Text-index, ви отримуєте більше накладних витрат.

Якщо об’єкт містить лише 1 змінну, до якої вам потрібно отримати доступ, що є значенням, у використанні об’єкта більше накладних витрат.


І де, на вашу думку, шукають властивості? Вони теж знаходяться в хеш-таблиці ... (хоча це лише більш-менш вірно в транку).
Artefacto

1

Ну, сьогодні мені стало цікаво, базуючись на тесті @magallanes, тому я трохи розширив його. Я доповнив деякі цикли for, щоб реально виділити прогалини між речами. Це працює на Apache 2.4, mod_php та PHP 7.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 / reqs / second з 1000 класів, які використовуються з 1000s об’єктами, ви можете побачити, як це може вплинути на продуктивність.

<?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 ... дуже зручно знати, яку інформацію несуть об’єкти (заздалегідь визначеного класу), але масиви не такі

Наприклад: з масивом

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

Це суперечить іншим перевіреним відповідям і не має доказів. Отже, це скоріше ідеологічне переконання, ніж реальна відповідь.
булочки
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.