2012-05-23 12 views
9

Hay un problema de rendimiento importante cuando se utiliza una matriz en el objeto como una propiedad en comparación con el uso de una variable de matriz php global, ¿por qué?¿La matriz de objetos PHP no es de escala lineal mientras que las matrices globales sí?

Para evaluar este problema creé el siguiente benchmark que almacena una matriz cada vez más grande con stdClass como nodo, dos pruebas se ejecutaron una usando una propiedad de matriz en una clase y la otra una matriz global.

El código de prueba

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

Y los resultados impactantes:

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 

Ahora, además de la sobrecarga obvia del objeto llama a sí misma la balanza propiedad de matriz objeto terriblemente a veces tomando 3 - 4 veces más tiempo cuando la matriz se vuelve más grande, pero este no es el caso con la variable de matriz global estándar.

¿Alguna idea o respuesta con respecto a este problema y es esto un posible error con el motor de PHP?

+0

Duda es un problema de matriz, pero podría ser una sobrecarga de OOP, ya que estás construyendo esa matriz dentro de un objeto - muchos sobrecarga de OOP. Si reemplaza temporalmente ese miembro objeto con una variable global estándar, ¿cambia el rendimiento? –

+0

¿Podrías por favor reducir esta pregunta al problema real, dejando todo el asunto de prggmr? Hace la pregunta difícil de entender. – NikiC

+0

@MarcB Hay un cambio, pero no es un cambio significativo (cambiar el historial para almacenar solo verdadero '' $ this -> _ event_history [] = [true] '' 'produce 9532 por segundo en 10 segundos. @NikiC Do usted tiene un consejo sobre cómo podría recortar esto? – Nick

Respuesta

6

que prueban su código en PHP 5.3.9. Para hacerlo tuve que traducir [] a array(), y también tuve que corregir tu línea # 12: de $a=new MyTest($size), a $mytest=new MyTest($size) (por cierto, el argumento constructor se ignora silenciosamente, es divertido). También he añadido este código:

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

he añadido la línea de uso de la memoria a sus bucles en el mismo punto, añadí un unset($mytest); después del caso objeto (para obtener un registro de memoria más consistente), y también se sustituye el 5000000 de la 1000000 porque solo tengo 2GB de RAM. Esto es lo que tengo:

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 

Como se puede ver, añadiendo a la matriz dentro de una llamada a la función cuesta casi tanto como (y tiene el mismo comportamiento no lineal como) lo hace dentro de su llamada método original. Una cosa se puede decir con certeza:

¡Son las llamadas a las funciones las que consumen el tiempo de la CPU!

En cuanto al comportamiento no lineal, se vuelve realmente evidente solo por encima de un cierto umbral. Mientras que los tres casos tienen el mismo comportamiento de memoria (debido a la recolección incompleta de gargabe esto solo es evidente entre los casos de "matriz simple" y "matriz dentro de función", en este registro), es el "método dentro de matriz" y el " array inside function "casos que tienen el mismo comportamiento de tiempo de ejecución. Esto significa que son las llamadas de función las que provocan un aumento de tiempo no lineal. Me parece que esto se puede decir:

La cantidad de datos que hay alrededor durante una llamada de función influye en su duración.

Para comprobar esto que sustituyen todas $a[] con $a[0] y todos con 1,000,000 5,000,000 (para obtener tiempos de ejecución totales similares) y se obtuvo este resultado:

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 

Nota cómo los tiempos son casi perfectamente lineal ahora. Por supuesto, el tamaño de la matriz está pegado a 1 ahora. Tenga en cuenta también cómo las diferencias de los tiempos de ejecución de los tres casos son menos pronunciadas que antes. Recuerde que la operación más interna es la misma en todos los casos.

No voy a tratar de explicar completamente todo esto (¿colección gargabe en función de llamada? ¿Fragmentación de memoria? ...?), Pero creo que he recopilado información útil, para todos aquí y para mí también.

+1

Además, si no agrega 'unset ($ mytest);' todo sería un 10% más lento. – sanmai

+0

Gran respuesta aquí ... esto realmente requiere sumergirse en la fuente de php ya que me parece que algo está pasando cuando acceden a variables entre su contexto que está causando esta tremenda reducción en el tiempo de uso y considerando que solo son punteros y referencias (¿lo son? ¿No deberían serlo?) el tamaño de los datos no debería importar, ya que no se escribe nada en ningún otro lugar que los datos adicionales añadidos ... o tal vez esto sea solo un encuentro con algunas de las decisiones que los desarrolladores han tomado cuando desarrollaron el lenguaje que posiblemente ha pasado desapercibido ... – Nick

2

No puedo publicar todo esto en un comentario, por lo que esto es más una observación que una respuesta. Parece que SplObjectStorage es bastante lento. Además, array_push es mucho más rápido que $ array [] = 'item';

responsabilidad: Disculpas por el código descuidado :)

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

Me gustaría saber el motivo por el que se votó negativamente, parece una respuesta razonable. –

+0

@AlixAxel: probablemente porque no es una respuesta. No estoy bajista porque parece que el código es útil para explorar las características de PHP. – wallyk

Cuestiones relacionadas