2012-05-23 3 views
9

in-object 배열을 속성으로 사용할 때와 전역 PHP 배열 변수를 사용할 때 큰 성능 문제가 있습니다. 그 이유는 무엇입니까?전역 배열이 수행하는 동안 PHP 객체 배열이 선형으로 스케일되지 않습니까?

이 문제를 벤치마킹하기 위해 점차적으로 더 큰 배열을 stdClass를 노드로 저장하는 벤치 마크를 만들었습니다. 두 개의 테스트는 다른 전역 배열의 클래스에서 배열 속성을 사용하여 실행되었습니다.

테스트 코드

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

그리고 충격적인 결과 :

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 

이제 객체의 명백한 오버 헤드 자체가 정말 가끔 3 복용 객체 배열 속성 스케일 호출 외에 - 이상시 4 회 배열이 커지지 만 표준 전역 배열 변수에서는 그렇지 않습니다.

이 문제와 관련된 생각이나 해답이 PHP 엔진의 버그 일 수 있습니까?

+0

의심의 여지가 있지만 배열 문제입니다,하지만 당신은 객체 내부에 그 배열을 구축하고 있기 때문에 OOP 오버 헤드가 될 수 있습니다 - 많은 OOP 오버 헤드. 일시적으로 해당 개체 구성원을 표준 전역 변수로 바꾸면 성능이 전혀 변하지 않습니까? –

+0

이 질문을 실제 문제로 줄이시고 전체 prggmr 항목을 남겨 둘 수 있습니까? 그것은 질문을 이해하기 어렵게 만듭니다. – NikiC

+0

@MarcB 변경 사항이 있지만 중요한 변경 사항은 아닙니다 (true를 단지'$ this -> _ event_history [] = [true]''로 저장하면 10 초 후에 초당 9532가 생성됩니다) @NikiC Do – Nick

답변

6

나는 PHP 5.3.9에서 코드를 테스트했다. 이렇게하려면 내가 array()[]을 번역했고, 나 또한 당신의 라인 # 12 수정했다 : $a=new MyTest($size)에서 $mytest=new MyTest($size)에 (BTW, 생성자의 인수가 자동으로 무시됩니다 재미를). 당신 5000000의와,

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

나도 같은 지점에서 루프로 메모리 사용량 라인을 추가 (보다 일관된 메모리 로그를 얻기 위해) 객체의 경우 후 unset($mytest);을 추가하고, 또한 대체 : 나는이 코드를 추가 1000000은 RAM이 2GB 밖에 없기 때문입니다. 이것은 내가 가진 것입니다 : 당신이 볼 수 있듯이

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 

는 함수 호출 내부 배열에 추가하면 원래 메서드 호출 내에서 그 일을 거의만큼 비용 (와 같은 비선형 거동이있다). 한 가지는 확실히 말할 수 있습니다 :

그것은 CPU 시간을 먹는 함수 호출입니다!

비선형 동작에 관해서는 특정 임계 값 이상에서만 실제로 분명해집니다. (불완전 gargabe 수집 이것은 "일반 배열"이 로그에 "배열 함수 내에서"케이스 사이에서만 명백하므로) 세 경우 모두가 동일한 메모리 동작 동안, 그것은 "메소드 내부 배열"이고 " 함수 내부에 배열 "같은 실행 시간 동작을 갖는 경우를 나타냅니다. 즉, 비선형 적으로 시간이 증가하는 것은 함수 호출 자체를 의미합니다. 함수 호출시 주변에있는 데이터의

양이 기간에 영향을 미친다 :이라고 할 수있는 날 것으로 보인다.이를 확인하려면

나는 모든 $a[0]$a[] 모든 1000000 교체 5000000 (비슷한 총 실행 시간을 얻기 위해)이 출력을 얻을 : 시간이 지금은 거의 완벽하게 선형 얼마나

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 

참고. 물론 배열 크기는 1로 고정되어 있습니다. 세 경우의 실행 시간의 차이가 이전보다 덜 두드러진 점에 유의하십시오. 가장 안쪽에있는 작업은 모든 경우에서 동일하다는 것을 기억하십시오.

나는이 모든 것을 설명하려고하지는 않는다. (함수 호출에 대한 gargabe 콜렉션? 메모리 조각화? ...?).하지만 나는 모든 사람들에게 유용한 정보를 수집했다고 생각한다. 너무.

+1

또한 unset ($ mytest);을 추가하지 않으면 모든 것이 약 10 % 느려집니다. – sanmai

+0

위대한 대답은 ... 여기에 PHP 소스에 대한 다이빙이 필요합니다. 왜냐하면 사용 시간이 엄청나게 단축되어 포인터와 참조 일 뿐이라는 컨텍스트간에 변수에 액세스 할 때 뭔가가 일어나고있는 것 같습니다. (그들은 존재하지 않아야합니까?) 데이터의 크기는 중요하지 않습니다. 추가 된 데이터 이외에는 아무 것도 쓰여지고 있지 않기 때문입니다. 아니면 개발자의 결정과 만나는 것일 수도 있습니다. 아마도 눈에 띄지 않는 언어를 개발했을 때 찍은 ... – Nick

2

나는이 모든 것을 코멘트에 게시 할 수 없으므로, 이것은 대답이 아닌 관측에 가깝다. SplObjectStorage가 상당히 느린 것 같습니다. 또한 array_push는 $ array [] = 'item'보다 훨씬 빠릅니다.

면책 조항 : 정치 못한 코드에 대한 사과는 :)

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

나는 이것이 왜 투표를 잘못한 이유를 알고 싶습니다. 합리적인 대답처럼 보입니다. –

+0

@ AlixAxel : 아마 대답이 아니기 때문일 것입니다. 코드가 PHP 특성을 탐색하는 데 유용하기 때문에 나는 하향 투표하지 않습니다. – wallyk