PHP 성능은 계속해서 향상되고 있습니다. 그러나 부적절하게 사용하거나 주의하지 않으면 여전히 PHP 내부 구현의 함정에 빠질 수 있습니다. 며칠 전에 성능 문제가 발생했습니다.
PHP 성능이 점점 좋아지고 있어요. 그러나 부적절하게 사용하거나 주의하지 않으면 여전히 PHP 내부 구현의 함정에 빠질 수 있습니다. 며칠 전에 성능 문제가 발생했습니다.
사실은 이렇습니다. 우리 인터페이스 중 하나가 매번 반환하는 데 5초가 걸린다고 동료가 보고했습니다. 우리는 코드를 함께 검토한 후 실제로 루프에서 읽기 캐시가 호출된 것을 알고 "놀랐습니다". 900번) 작업을 수행했지만 캐시된 키는 변경되지 않았기 때문에 이 코드를 루프 외부로 이동하고 다시 테스트했습니다. 인터페이스 반환 시간이 2초로 떨어졌습니다. 비록 두 배로 늘어났지만, 당연히 받아들일 수 있는 결과는 아닙니다!
성능 문제를 일으킨 코드의 양은 많지 않았습니다. 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; } } ?>
shell $ Time/USR/LOCAL/PHP/BIN/PHP TEST.PHP Rereal 0m1.132s
User 0M1.118sSys 0m0.015s
예, 그렇습니다. 문자열 번호를 사용하여 캐시에서 꺼냈을 때의 모습입니다! 따라서 여기서는 특별히 문자열로 변환됩니다(직접 숫자인 경우에는 이 문제가 발생하지 않으며 직접 확인할 수 있습니다). 소비되는 시간은 1초이며 이는 단지 3000사이클에 불과하다는 것을 알 수 있습니다. 후속 시스템 시간도 strace를 사용하여 효과적인 정보를 얻지 못할 예정입니다.
shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php
shell$ less xxx
우리는 이 두 시스템 호출 사이의 지연이 매우 크다는 것만 알 수 있습니다. 네가 무슨 짓을 했는지 모르겠어? 다행스럽게도 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이 strtol의 별칭으로 밝혀졌습니다. 간단히 말해서, 이는 문자열을 긴 정수로 변환하는 것입니다. 문자열 유형이므로 비교를 위해 긴 정수로 변환할 것으로 예상됩니다. 이 변환 프로세스는 너무 많은 시간을 소비하므로 다시 실행합니다.
코드 복사
코드는 다음과 같습니다.
shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php
아래 그림과 같이 많은 호출을 쉽게 잡을 수 있는 시점에서 in_array의 느슨한 비교 문제가 발견됩니다. 두 개의 문자열을 비교하게 되는데, 먼저 긴 정수형으로 변환한 후 비교를 하게 되는데, 이때 성능이 소모되는지는 모르겠습니다.
이제 문제의 핵심을 알았으므로 해결 방법이 많이 있습니다. 가장 간단한 방법은 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; } } ?>
코드 복사 다음은 다음과 같습니다:
shell$ time /usr/local/php/bin/ php test.php real 0m0.267s
user 0m0.247ssys 0m0.020s
몇 배 더 빨라졌습니다! ! ! 시스템 시간 소비는 거의 변하지 않았음을 알 수 있습니다. 다시 ltrace를 실행해 보겠습니다. 여전히 malloc 호출의 간섭을 제거하기 위해 $x를 직접 할당해야 합니다. 왜냐하면 실제 애플리케이션에서는 캐시에서 즉시 꺼내기 때문에 적용할 샘플 코드와 같은 루프가 없기 때문입니다. 기억을 위해.
다시 실행
코드 복사
코드는 다음과 같습니다.
shell$ ltrace -c /usr/local/php/bin/php test.php
아래와 같습니다.
__ctype_tolower_loc가 가장 많은 시간을 차지합니다! 라이브러리 함수 __ctype_tolower_loc가 수행하는 작업을 확인했습니다. 간단한 이해는 문자열을 소문자로 변환하는 것이므로 in_array 비교 문자열이 대소문자를 구분하지 않는다는 의미입니까? 사실 이 함수 호출은 우리의 in_array와 거의 관련이 없습니다. in_array의 구현에 관해서는 PHP의 소스 코드를 살펴보는 것이 더 나을 것입니다. 더 자세히 설명할 수는 없습니다. 저와 의사소통을 환영합니다. 제가 잘못 쓴 것이 있으면 정정해 주세요.
——————2013.08.29 구분선——————————
저녁에 다음 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에서 중국어를 삽입하고 MYSQL을 표시할 때 다시 한 번 잘못된 문자가 발생했습니다
위 내용은 PHP in_array의 성능 저하 문제 발생의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!