PHP 성능은 나날이 향상되고 있습니다. 그러나 부적절하게 사용하거나 주의하지 않으면 PHP 내부 구현의 함정에 빠질 수 있습니다. 며칠 전에 성능 문제가 발생했습니다. 성능 문제를 일으킨 코드의 양은 많지 않았습니다. IO 문제를 해결한 후 테스트 코드를 작성했는데, 당연히 문제가 빨리 다시 나타났습니다.
<?php $y="1800"; $x = array(); for($j=0;$j<2000;$j++){ $x[]= "{$j}"; } for($i=0;$i<3000;$i++){ if(in_array($y,$x)){ continue; } } ?>
실제 0m1.132s
사용자 0m1.118s
sys 0m0.015s
예, 문자열 번호를 사용하고 있는데 캐시에서 꺼내면 이런 모습입니다! 따라서 여기서는 특별히 문자열로 변환됩니다(직접 숫자인 경우에는 이 문제가 발생하지 않으며 직접 확인할 수 있습니다). 소비되는 시간은 1초이며 이는 단지 3000사이클에 불과하다는 것을 알 수 있습니다. 후속 시스템 시간도 strace를 사용하여 효과적인 정보를 얻지 못할 예정입니다.
shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php
shell$ less xxx
us 나는 이 두 시스템 호출 사이의 지연이 매우 크다는 것만 보았지만 무슨 일이 일어났는지 몰랐습니다. 다행스럽게도 strace 외에도 Linux의 디버깅 도구에는 ltrace도 포함되어 있습니다(물론 dtrace 및 ptrace도 있으므로 이 기사의 범위를 벗어나므로 생략하겠습니다).
인용문: strace는 시스템 호출이나 프로세스의 신호 생성을 추적하는 데 사용되는 반면, ltrace는 (IBM 개발자 작업을 통해) 라이브러리 함수 호출 프로세스를 추적하는 데 사용됩니다.
간섭 요인을 제거하기 위해 $x를 배열 형식("0", "1", "2",...)에 직접 할당하여 결과에 영향을 미치는 과도한 malloc 호출을 방지합니다.
shell$ ltrace -c /usr/local/php/bin/php test.php
그림 2
를 실행합니다. 라이브러리 함수 __strtol_internal이 매우 자주 호출되어 94%에 도달하는 것을 확인했는데 이는 너무 과장된 것입니다. 그런 다음 이 라이브러리 함수 __strtol_internal이 수행하는 작업을 확인해보니 간단히 말해서 문자열을 변환하는 것입니다. 긴 정수로 변환하면 PHP 엔진이 이것이 문자열 숫자임을 감지하여 비교를 위해 긴 정수로 변환할 것으로 예상됩니다. 이 변환 프로세스는 너무 많은 시간을 소비하므로 다시 실행합니다.
이때 문제가 발견됩니다. 느슨한 비교인 In_array는 먼저 두 개의 문자열을 긴 정수로 변환한 후 비교합니다. 성능이 얼마나 좋을지 모르겠습니다.shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php
이제 문제의 핵심을 알았으므로 많은 해결책이 있습니다. 가장 간단한 방법은 in_array의 세 번째 매개변수를 true에 추가하는 것입니다. 이는 엄격한 비교가 됩니다. 또한 유형을 비교하기 위해 이는 PHP의 영리한 유형 변환을 방지하고 코드는 다음과 같습니다.
<?php $y="1800"; $x = array(); for($j=0;$j<2000;$j++){ $x[]= "{$j}"; } for($i=0;$i<3000;$i++){ if(in_array($y,$x,true)){ continue; } } ?>몇 배 더 빠릅니다. ! ! sys에 소요되는 시간은 거의 변하지 않았음을 알 수 있습니다. 다시 ltrace할 때 malloc 호출의 간섭을 제거하기 위해 $x를 직접 할당해야 합니다. 실제 애플리케이션에서는 캐시에서 즉시 꺼내기 때문에 신청할 샘플 코드와 같은 루프가 없습니다. 메모리.
shell$ time /usr/local/php/bin/php test.php real 0m0.267s user 0m0.247s sys 0m0.020s다음과 같이
shell$ ltrace -c /usr/local/php/bin/php test.php
__ctype_tolower_loc가 시간이 가장 많이 걸립니다! 라이브러리 함수 __ctype_tolower_loc가 수행하는 작업을 확인했습니다. 간단한 이해는 문자열을 소문자로 변환하는 것이므로 in_array 비교 문자열이 대소문자를 구분하지 않는다는 의미입니까? 사실 이 함수 호출은 우리 in_array와 거의 관련이 없습니다. in_array 구현에 대해서는 PHP 소스 코드를 살펴보는 것이 더 좋으며 아마도 더 철저하게 이해할 것입니다
저녁에 , 다음 PHP 5.4.10 소스 코드를 읽었습니다. in_array에 정말 관심이 있습니다. 하하. ./ext/standard/array.c의 1248번째 줄에 있습니다. php_search_array 함수를 호출하는 것을 볼 수 있습니다. 아래의 array_serach도 이를 조정하지만 마지막 매개변수는 다릅니다. 몇 가지 추적 후, in_array 느슨한 비교의 경우 마침내 그는 비교를 위해 ./Zend/zend_operators.c에 있는 함수 zendi_smart_strcmp를 호출했습니다. 우리는 ltrace를 사용하여 많은 수의 캡처된 데이터를 변환했습니다. 작업은 is_numeric_string_ex의 동작입니다.
is_numeric_string_ex 함수는 ./Zend/zend_operators.h에 정의되어 있습니다. 일련의 판단과 변환 후에 strtol이 232번 라인에서 호출됩니다. 언급된 시스템 함수, 문자열을 긴 정수로 변환, 그림과 사실이 있습니다.
php의 in_array와 더 많은 만남 관련 기사 성능 저하 문제가 있는 경우 PHP 중국어 웹사이트를 주목하세요!