원문: http://www.puremango.co.uk/2010/04/fast-php
복사본: fast-php
PHP는 세상에서 가장 빠른 언어는 아니다. 그 명예는 아마도 기계어에게 갈 것이다. 하지만 많은 고수준 언어들처럼 PHP는 이름을 가지는 변수(named variable), 해시맵(연관 배열), C 비슷한 문법, 객체지향 능력, 느슨한 타이핑 등등과 같은 몇 가지 간편한 추상화를 제공한다 - 우리는 개발 편의성을 위해 처리 속도를 맞바꾼다.
그래서 사람들이 매우 느리게 동작하는 큰 PHP 웹 응용프로그램을 발견하는 건 꽤 공통된 문제이다.
여기 웹 응용프로그램에서 일반적으로, 그리고 PHP에서 특히 자주 만나게 되는 몇 가지 병목이 있다.
그래서 우리는 데이터베이스를 커다란 영구적인 배열처럼 다루곤 한다. 데이터베이스는 그게 아니다.
무엇보다도, DB로 들어가거나 나오는 어떤 것이든 웹서버로 전송되어야 할 것이다. 그건 네트웍 충격이다. 그래서 이미지나 다른 이진 데이터를 데이터베이스에 저장하는 것은 일반적으로 '나쁜' 생각이다.(다른 이유에서도) 하지만 단순히 이미지만 그런 게 아니다 - 모든 데이터베이스 트래픽은 로컬 네트워크 위에서 발생할 것이어서 큰 HTML 덩어리나 앞뒤로 빠르게 움직이는 수천 행의 데이터를 가졌다면 그것이 네트웍 충격을 수반할 거라는 걸 알 필요가 있다. MySQL 압축은 이걸 도울 수는 있지만 스스로의 스크립트를 위해 벤치마크를 꼭 해봐라 - CPU 부하가 서버를 위한 MySQL 압축의 이익을 무효화할지도 모르겠다.
두번째로, 위대한 많은 데이터베이스는 최적화를 위한 많은 고민없이 함께 던져진다. 실행하려는 질의가 어떤 것인지 살펴보고 그걸 위한 데이터베이스의 구조를 최적화할 필요가 있다. 종종 색인을 추가하는 것처럼 쉽지만 때로는 질의가 해야 할 일을 하는지 확인하기 위해 질의를 조사할 필요가 있을지도 모르겠다.
데이터베이스는 속도 문제의 매우 매우 공통된 근원이다 - 단순히 색인을 테이블에 추가하는 것이 고민거리를 해결해줄 거라는 걸 자주 발견할 것이다. 데이터베이스를 먼저 봐라!
여기 구글의 PHP 최적화 페이지에서 온 매우 깔끔한 팁이 있다.
루프 안에서 SQL 질의를 하는 것을 피하라.
INSERT INTO users (first_name,last_name) VALUES("John", "Doe"); INSERT INTO users (first_name,last_name) VALUES("Jane", "Doe")
이렇게 두 개의 질의를 실행하는 것보다
INSERT INTO users (first_name,last_name) VALUES("John", "Doe"),("Jane", "Doe")
하나의 질의를 이처럼 실행하는 것이 더 빠르다는 것을 언급한다. 위대한 작은 SQL 최적화 팁. 루프 안에서는 하나의 질의를 구축해서 루프 안에서 질의를 실행하는 것보다 루프 바깥에서 한 번 실행해라.
종종 “느린 웹 응용프로그램”에 대해서 들을 때, 사람들은 실제로 서버 쪽 코드 최적화 측면에 매우 독립적일 수 있는 페이지 로드 시간에 대해 말하고 있다. 이미지를 잘 들여다 봐라 - 압축되지 않은 PNG인가? 대신 그걸 90% 품질의 JPG로 변환함으로써 정말 뭔가 잃는가? 아마도 페이지 로드 속도를 얻고 대역폭을 줄일 것이다. 또한 적용 가능한 곳 어디에나 웹 서버에 gzip 압축을 사용하고 있는지 확인하고, css와 javascript, html 코드를 축소하는 것에 대해서 생각해봐라. 종종 느릿느릿한 코드에 대한 사용자 불평은 실제로는 처리보다는 배달에 더 관련이 있다. 어떤 사용 사례(예를 들어 툴바 아이콘)에서는 CSS 스프라이트를 사용하는 것이 좋은 클라이언트 쪽 최적화이지만 초기 개발 시간에 충격을 주는 것에 대해서는 잊지 마라.
클라이언트 쪽 병목이 어디에 있는지 잘 살펴보기 위해 Firebug 애드온 pagespeed와 YSlow를 설치하라.
PHP는 쉽다. 잘못되기 쉽다. 밖에는 매우 특정한 상황에서 기적을 낳을 수 있는 극소-최적화 기법들에 대한 많은 게시물들이 있지만, 일반적으로 말하자면 이런 팁들은 결국에는 역효과를 낳고 부자연스럽게 코드를 작성하도록 강제하며 코드를 변화에 저항하게 만들 것이다. 예를 들어, 1차원 배열에 접근하기 위해 &를 사용하는 것이 빠르지만 개발 중에 나중에 차원을 추가한다면 어떻게 될까 - 그것이 1차원 이상의 배열에서 성능을 저하시킨다는 것을 기억할 건가? 그리고 PHP의 다음 버전이 그 행동을 바꾼다면 어떻게 될까?
그러나 아주 명백해서 종종 무시되는 몇 가지 최적화가 있다 - 예를 들어, 루프 안에서 count()를 잡는 것:
for($i=0 ; $i < count($arr) ; $i++) {
}
vs
for($i=0, $c=count($arr) ; $i < $c ; $i++) {
}
첫번째 조각에서 그 count 함수는 매 반복 시에 실행된다. 두번째에서 그건 단 한 번만 실행된다. 이건 스크립트별 단위에서 뿐만 아니라 서버 전체에까지 정말 큰 충격을 가지게 될 수도 있다.
두번째 스타일을 습관으로 만들기 위해 정말 노력해야 할 것이다. 그건 문법적으로 거의 다르지 않지만 거대한 잠재적인 성능 증대를 제공한다.
하지만 그것같은 몇 가지 간단한 좋은 실전적인 최적화 외에, PHP 코드에 병목이 실제로 어디에 있는지 어떻게 말할까?
많은 사람들은 비슷하게 보이는 여러 장소에서 echo microtime();을 던져놓고 몇 번 실행한다. 코드 프로파일링에 대한 팁을 하나 공유해보겠다.
xdebug 확장을 이용하여 스크립트 안에 있는 함수 호출의 매 줄마다의 스피드 흔적에 대한 직관을 얻을 수 있다. 여기 우리가 얻을 수 있는 산출물의 예가 있다.
우리는 각각의 함수 호출이 차지하는 시간 크기(백분율이나 밀리세컨드)와 얼마나 많은 횟수로 실행되는지, 그리고 함수 (자체) 또는 다른 곳에서(누적으로) 시간이 소요되는지를 멋지게 분해한다. 그리고 GET 요청으로 ?XDEBUG_PROFILE을 덧붙임으로써 간단히 이 분해를 얻을 수 있다 - 코드 변경이 필요하지 않음!
슬프게도 하나의 경고가 있다. 윈도우즈에서는 그다지 안정적이지 않다. 나는 당신에게 불평하고, 당신의 동작하는 서버들과 같은 아키텍쳐 위에서 개발하는 게 좋겠다고 말하려고 하지만, 스크린샷에서 확실히 볼 수 있는 것처럼, 나는 여전히 빌 게이츠도 나르고 있는 중이다.
여기 PHP 코드 프로파일링을 위한 xdebug와 wincahcegrind를 설정하는 것에 대한 작은 글이 있다. 꽤 쉽다.
그래서 많은 컨텐트 괜리 웹 응용프로그램들은 이와 같은 코드가 특징이다.
include "db.php";
include "header.php";
$sql = "SELECT Content FROM Pages WHERE PageID=5";
$res = mysql_query($sql,CN);
if($res) {
\t$row = mysql_fetch_assoc($res);
\techo $row['Content'];
} else {
\techo "error fetching content";
}
include "footer.php";
그리고 말하자면 그건 PHP에 관한 멋진 것 중의 하나라는 거다 - 빠르게 DB와 HTML 사이의 인터페이스를 아무렇게나 만들 수 있다 - 빠른 프로토타이핑에 훌륭하다.
하지만 그것에 대해서 생각해봐라 - 얼마나 자주 페이지 컨텐트가 바뀌지? page.html에 링크를 달고 관리자가 뒷단의 CMS에서 저장을 눌렀을 때, page.html를 생성된 컨텐츠로 덮어쓰는 게 더 낫지 않을까?
그런 식으로 매달 그 페이지를 요청하는 백만명의 사용자들은 PHP 프로세스를 만들어 내지도 않는다 - Apache는 전체 트랜잭션을 다룰 수 있다. 이것은 페이지가 백만 번 같은 입력과 같은 출력을 가지고 같은 코드를 실행하는 것 대신에 매우 빠르게 서비스된다는 것을 의미한다. 모두에게 이익이다 - 서버는 중복된 코드를 적게 수행하고 있고 사용자들은 더 빠른 웹 응용프로그램을 얻는다.
게다가 이런 방법으로 최적화될 수 있는 건 드물게 접근되는 사이트의 부분 뿐만이 아니다.
온라인 상점을 고려해봐라. 음, 매우 정적인 사이트의 복사본을 만들어내는 건 어떨까 - 단지 제품 정보가 바뀔 때만 변경해 줄 필요가 있다. 명백하게 실행 중에 생성하는 것 대신에 디스크에 캐시하는 게 이치에 맞는지 알기 위해 당신 스스로의 응용프로그램을 살펴보기를 원하겠지만, 너무 자주 간과되는 선택사항이다.
그건 우리를 멋지게 향상시킨다.
이건 웹 개발자와 서버 관리자 사이의 선이 흐려지기 시작하는 곳이지만, 대규모 응용프로그램을 위해 opcode 캐싱, 그리고 memcache, squid, MySQL 질의-캐시, 다른 종류의 캐시에 대해 생각하기를 시작할 필요가 있을 것이다.
이런 종류의 해결책은 거의 항상 그 응용프로그램의 개별적인 필요에 맞춰 정교하게 다듬어지기 때문에, 그것에 대해 많이 이야기하지 않겠다 - 이런 수준의 캐싱이 필요하다면 이미 당신이 나보다 더 많이 알고 있을 것 같으니 난 이 분야의 전문가인적 하지 않겠다. 여기 이런 종류의 것에 대해 정말 알고 있는 사람들로부터 나온 약간의 링크와 그림이 있다.
오늘은 여기까지. 요약하자면,
극소 최적화에 대한 작업에 대해 꾸짖어 준 toosweettobesour의 Daniel에게 감사