2012-05-23 33 views
9

Nesne içi dizileri bir özellik olarak kullanırken global bir php array değişkenini kullanarak büyük bir performans sorunu var, neden?PHP nesne dizisi doğrusal değil ölçek mi?

Bu sorunu karşılaştırmak için, giderek daha büyük bir diziyi stdClass ile düğüm olarak depolayan aşağıdaki karşılaştırmayı oluşturdum, iki sınama biri diğerinde bir genel dizide bir dizi özelliği kullanılarak çalıştırıldı.

test kodu

ini_set('memory_limit', '2250M'); 
class MyTest { 
    public $storage = []; 
    public function push(){ 
     $this->storage[] = [new stdClass()]; 
    } 
} 

echo "Testing Objects".PHP_EOL; 
for($size = 1000; $size < 5000000; $size *= 2) { 
    $start = milliseconds(); 
    for ($a=new MyTest(), $i=0;$i<$size;$i++) { 
     $a->push(); 
    } 
    $end = milliseconds(); 
    echo "Array Size $size".PHP_EOL; 
    echo $end - $start . " milliseconds to perform".PHP_EOL; 
} 
echo "================".PHP_EOL; 
echo "Testing Array".PHP_EOL; 
for($size = 1000; $size < 5000000; $size *= 2) { 
    $start = milliseconds(); 
    for ($a=[], $i=0;$i<$size;$i++) { 
     $a[] = [new stdClass()]; 
    } 
    $end = milliseconds(); 
    echo "Array Size $size".PHP_EOL; 
    echo $end - $start . " milliseconds to perform".PHP_EOL; 
} 

Ve şok edici sonuçlar:

Testing Objects 
Array Size 1000 
2 milliseconds to perform 
Array Size 2000 
3 milliseconds to perform 
Array Size 4000 
6 milliseconds to perform 
Array Size 8000 
12 milliseconds to perform 
Array Size 16000 
35 milliseconds to perform 
Array Size 32000 
97 milliseconds to perform 
Array Size 64000 
246 milliseconds to perform 
Array Size 128000 
677 milliseconds to perform 
Array Size 256000 
2271 milliseconds to perform 
Array Size 512000 
9244 milliseconds to perform 
Array Size 1024000 
31186 milliseconds to perform 
Array Size 2048000 
116123 milliseconds to perform 
Array Size 4096000 
495588 milliseconds to perform 
================ 
Testing Array 
Array Size 1000 
1 milliseconds to perform 
Array Size 2000 
2 milliseconds to perform 
Array Size 4000 
4 milliseconds to perform 
Array Size 8000 
8 milliseconds to perform 
Array Size 16000 
28 milliseconds to perform 
Array Size 32000 
61 milliseconds to perform 
Array Size 64000 
114 milliseconds to perform 
Array Size 128000 
245 milliseconds to perform 
Array Size 256000 
494 milliseconds to perform 
Array Size 512000 
970 milliseconds to perform 
Array Size 1024000 
2003 milliseconds to perform 
Array Size 2048000 
4241 milliseconds to perform 
Array Size 4096000 
14260 milliseconds to perform 

Şimdi nesnenin bariz havai kendisi korkunç bazen 3 alarak nesne dizisi özelliği ölçekler çağırır yanında - daha uzun zaman 4 kez dizi büyür, ancak standart global dizi değişkeninde durum böyle değildir.

Herhangi bir düşünce veya bu sorunu ile ilgili cevaplar ve bu PHP motoru ile olası bir hata olduğunu?

+0

Bu bir dizi sorunudur, ancak OOP ek yükü olabilir, çünkü bu diziyi bir nesnenin içinde oluşturuyorsunuz - bir sürü OOP ek yükü. Bu nesne üyesini standart bir global değişkenle geçici olarak değiştirirseniz, performans değişmez mi? –

+0

Bu soruyu asıl soruna indirebilir misiniz, bütün prggmr şeylerini bırakıyor musunuz? Soruyu anlamak zorlaştırıyor. – NikiC

+0

@MarcB Bir değişiklik var ama bu önemli bir değişiklik değil (sadece saklamak için geçmişi değiştirerek '' '' $ -> _ event_history [] = [true] '' '10 saniyede saniyede 9532 verir. Bunu nasıl düzelteceğine dair tavsiyen var mı? – Nick

cevap

6

Ben PHP 5.3.9 kodunuzu test etti. Bunu yapmak için ben array() için [] çevirmek zorunda kaldı ve ben de çizgi # 12 düzeltmek zorunda kaldı: $a=new MyTest($size) itibaren $mytest=new MyTest($size) için (BTW, yapıcı argüman sessizce yok sayılır, komik). sizin 5000000 en birlikte,

echo "================".PHP_EOL; 
echo "Testing Function".PHP_EOL; 
for($size = 1000; $size < 1000000; $size *= 2) { 
    $start = milliseconds(); 
    for ($a=array(), $i=0;$i<$size;$i++) { 
     my_push($a); 
    } 
    $end = milliseconds(); 
    echo "Array Size $size".PHP_EOL; 
    echo $end - $start . " milliseconds to perform".PHP_EOL; 
    echo "memory usage: ".memory_get_usage()." , real: ".memory_get_usage(true).PHP_EOL; 
} 

function my_push(&$a) 
{ 
    $a[] = array(new stdClass()); 
} 

aynı noktada döngüler için bellek kullanımı satırı eklendi (daha tutarlı bellek kaydını almak için) nesne durumda sonra unset($mytest); eklendi ve ayrıca değiştirilir: Ben de bu kod eklendi 1000000 çünkü sadece 2GB RAMim var. Bu bende ne olduğunu: Gördüğünüz gibi

Testing Objects 
Array Size 1000 
2 milliseconds to perform 
memory usage: 1666376 , real: 1835008 
Array Size 2000 
5 milliseconds to perform 
memory usage: 2063280 , real: 2097152 
Array Size 4000 
10 milliseconds to perform 
memory usage: 2857008 , real: 2883584 
Array Size 8000 
19 milliseconds to perform 
memory usage: 4444456 , real: 4718592 
Array Size 16000 
44 milliseconds to perform 
memory usage: 7619392 , real: 8126464 
Array Size 32000 
103 milliseconds to perform 
memory usage: 13969256 , real: 14417920 
Array Size 64000 
239 milliseconds to perform 
memory usage: 26668936 , real: 27262976 
Array Size 128000 
588 milliseconds to perform 
memory usage: 52068368 , real: 52690944 
Array Size 256000 
1714 milliseconds to perform 
memory usage: 102867104 , real: 103546880 
Array Size 512000 
5452 milliseconds to perform 
memory usage: 204464624 , real: 205258752 
================ 
Testing Array 
Array Size 1000 
1 milliseconds to perform 
memory usage: 18410640 , real: 20709376 
Array Size 2000 
4 milliseconds to perform 
memory usage: 18774760 , real: 20709376 
Array Size 4000 
7 milliseconds to perform 
memory usage: 19502976 , real: 20709376 
Array Size 8000 
13 milliseconds to perform 
memory usage: 20959360 , real: 21233664 
Array Size 16000 
29 milliseconds to perform 
memory usage: 23872176 , real: 24379392 
Array Size 32000 
61 milliseconds to perform 
memory usage: 29697720 , real: 30146560 
Array Size 64000 
124 milliseconds to perform 
memory usage: 41348856 , real: 41943040 
Array Size 128000 
280 milliseconds to perform 
memory usage: 64651088 , real: 65273856 
Array Size 256000 
534 milliseconds to perform 
memory usage: 111255536 , real: 111935488 
Array Size 512000 
1085 milliseconds to perform 
memory usage: 204464464 , real: 205258752 
================ 
Testing Function 
Array Size 1000 
357 milliseconds to perform 
memory usage: 18410696 , real: 22544384 
Array Size 2000 
4 milliseconds to perform 
memory usage: 18774768 , real: 22544384 
Array Size 4000 
9 milliseconds to perform 
memory usage: 19503008 , real: 22544384 
Array Size 8000 
17 milliseconds to perform 
memory usage: 20959392 , real: 22544384 
Array Size 16000 
36 milliseconds to perform 
memory usage: 23872208 , real: 24379392 
Array Size 32000 
89 milliseconds to perform 
memory usage: 29697720 , real: 30146560 
Array Size 64000 
224 milliseconds to perform 
memory usage: 41348888 , real: 41943040 
Array Size 128000 
529 milliseconds to perform 
memory usage: 64651088 , real: 65273856 
Array Size 256000 
1587 milliseconds to perform 
memory usage: 111255616 , real: 111935488 
Array Size 512000 
5244 milliseconds to perform 
memory usage: 204464512 , real: 205258752 

, bir işlev çağrısı içinde diziye ekleme orijinal yöntem çağrısı içinde yapıyor neredeyse kadar maliyeti (ve aynı doğrusal olmayan davranışı vardır). Kesin olan tek şey söylenebilir:

CPU zamanını yiyip işlev çağrıları var! Doğrusal olmayan davranış ile ilgili

, sadece belirli bir eşik değerin üzerinde çok belirgin hale gelir. Her üç durumda da aynı hafıza davranışı (tamamlanmamış gargabe koleksiyonu nedeniyle) bu sadece “düz dizi” ve “dizinin içindeki fonksiyon dizisi” arasında belirgindir, bu “dizinin içindeki yöntem” ve “ dizinin içindeki işlev "aynı yürütme zamanı davranışına sahip durumlar. Bu, fonksiyonun, zaman içinde doğrusal olmayan bir artışa neden olan çağrıların kendileri olduğu anlamına gelir. bir işlev çağrısı sırasında etrafında verilerin

miktar süresini etkiler: söylenebilir geliyor bana.Bunu doğrulamak için

Ben bütün $a[0] ile $a[] ve tüm 1000000 yerini 5000000 (benzer toplam yürütme sürelerini almak için) ve bu çıktıyı elde: kez artık neredeyse tamamen doğrusal nasıl

Testing Objects 
Array Size 1000 
2 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 2000 
4 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 4000 
8 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 8000 
15 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 16000 
31 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 32000 
62 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 64000 
123 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 128000 
246 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 256000 
493 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 512000 
985 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 1024000 
1978 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 2048000 
3965 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
Array Size 4096000 
7905 milliseconds to perform 
memory usage: 1302672 , real: 1572864 
================ 
Testing Array 
Array Size 1000 
1 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 2000 
3 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 4000 
5 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 8000 
10 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 16000 
20 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 32000 
40 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 64000 
80 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 128000 
161 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 256000 
322 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 512000 
646 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 1024000 
1285 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 2048000 
2574 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 4096000 
5142 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
================ 
Testing Function 
Array Size 1000 
1 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 2000 
4 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 4000 
6 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 8000 
14 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 16000 
26 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 32000 
53 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 64000 
105 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 128000 
212 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 256000 
422 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 512000 
844 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 1024000 
1688 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 2048000 
3377 milliseconds to perform 
memory usage: 1302464 , real: 1572864 
Array Size 4096000 
6814 milliseconds to perform 
memory usage: 1302464 , real: 1572864 

Not. Tabii ki, dizi boyutu şimdi 1'e sıkıştı. Ayrıca, üç vakanın icra zamanlarındaki farklılıkların eskisinden daha az nasıl belirlendiğine de dikkat ediniz. En içteki işlemin her durumda aynı olduğunu unutmayın.

Bütün bunları açıklamaya çalışmıyorum (gargabe koleksiyonunda işlev çağrısı? Bellek parçalanması? ...?), Fakat yine de bazı yararlı bilgiler topladığımı düşünüyorum, buradaki herkes için ve kendim için çok.

+1

Ayrıca "unset ($ mytest)" eklemiyor olsaydınız, her şey yaklaşık% 10 daha yavaş olurdu. – sanmai

+0

Buraya büyük bir cevap ... Bu gerçekten php kaynağına dalışı gerektiriyor çünkü bana, kullanımları arasında bu muazzam bir azalmaya neden olan bağlamları arasında değişkenlere erişirken ve sadece işaretçiler ve referanslar olduklarını düşünerek devam eden bir şey olduğunu düşünüyorum. (değil mi? Olmalı mı?) Verilerin boyutu önemli değil, çünkü eklenmiş verilerden başka hiçbir şey yazılmadığı için ... ya da belki bu sadece geliştiricilerin bazı kararlarıyla bir karşılaşmadır. Muhtemelen fark edilmeden geçen dili geliştirdiklerinde almışlar ... – Nick

2

Öyle bu bir cevap daha bir gözlem daha, hepsi bir yorum bu yayınlayamaz. SplObjectStorage oldukça yavaş görünüyor. Ayrıca, array_push $ dizi [] = 'item' daha hızlıdır;

Yasal Uyarı: özensiz kodu Özür :)

<?php 

$time = microtime(); 
$time = explode(' ', $time); 
$time = $time[1] + $time[0]; 
$start = $time; 

$iteration = 10000; 

switch ($_REQUEST['test']) 
{ 
    case 1: 
     $s = new SplObjectStorage(); 

     for ($i = 0; $i < $iteration; $i++) { 
      $obj = new stdClass; 
      $s[$obj] = 'test'; 
     } 
     break; 
    case 2: 

     $s = array(); 
     for ($i = 0; $i < $iteration; $i++) { 
      $obj = new stdClass; 
      $s[$i] = $obj; 
     } 
     break; 

    case 3: 
     class Test { 
      public $data = array(); 
     } 
     $s = new Test; 
     for ($i = 0; $i < $iteration; $i++) { 
      $obj = new stdClass; 
      $s->data[] = $obj; 
     } 
     break; 

    case 4: 
     class Test { 
      public static $data = array(); 
     } 
     $s = new Test; 
     for ($i = 0; $i < $iteration; $i++) { 
      $obj = new stdClass; 
      $s->data[] = $obj; 
     } 
     break; 
    case 5: 
     class Test { 
      public $data = array(); 
     } 
     $s = new Test; 
     for ($i = 0; $i < $iteration; $i++) { 
      $obj = new stdClass; 
      array_push($s->data, $obj); 
     } 
     break; 
    default: 
     echo 'Type in ?test=#'; 
} 

$time = microtime(); 
$time = explode(' ', $time); 
$time = $time[1] + $time[0]; 
$finish = $time; 
$total_time = round(($finish - $start), 6); 
echo 'Page generated in '.$total_time.' seconds.'; 
+1

Ben neden bu aşağı-oy, neden makul bir cevap gibi görünüyor bilmek istiyorum. –

+0

@AlixAxel: Muhtemelen bir cevap olmadığı için. Ben downvoting değilim çünkü kod PHP özelliklerini keşfetmek için yararlı gibi görünüyor. – wallyk