P2 템플릿은 Ruby Proc으로 표현되며, #render
메소드 호출 시 자동으로 컴파일되어 HTML을 생성하는 코드를 실행합니다. 이 과정은 다음과 같은 단계로 이루어집니다.
P2 템플릿의 작동 방식
- 코드 변환 과정: P2는 Sirop Gem을 사용하여 템플릿의 소스 코드를 Prism AST(추상 구문 트리)로 파싱합니다. 이후
TagTranslator
클래스를 통해CallNode
와 같은 AST 노드를 사용자 정의TagNode
로 변환하여 HTML 태그를 나타냅니다. 이 변환된 AST는 다시 Ruby 소스 코드로 역변환됩니다. - HTML 생성 방식: HTML 코드는 즉시 생성되지 않고, 보류 중인 HTML 조각들의 배열에 저장됩니다. 정적 문자열은 하나의 버퍼 푸시로 연결되고, 동적 부분은 적절히 이스케이프된 후 별도로 푸시됩니다.
성능 최적화 배경 및 문제점 진단
P2 초기 버전은 ERB나 ERubi에 비해 성능이 만족스럽지 못했습니다. 주요 성능 저하 요인은 다음과 같았습니다.
* 보간된 문자열 푸시: 동적 값을 포함하는 보간된 문자열을 버퍼에 한 번에 푸시하는 방식은 각 부분을 개별적으로 푸시하는 것보다 느렸습니다. 이는 임시 문자열 생성 오버헤드 때문입니다.
* 불필요한 rescue
절: 생성된 코드에 포함된 rescue
절은 특히 중첩된 템플릿에서 상당한 오버헤드를 유발했습니다.
* 문자열 동결 부재: Proc
으로 평가될 때 리터럴 문자열이 기본적으로 동결되지 않아 할당 오버헤드와 GC(가비지 컬렉션) 압력이 증가했습니다.
* 비효율적인 HTML 이스케이프: CGI.escape_html
사용이 ERB::Escape.html_escape
보다 느렸습니다.
적용된 최적화 기법 및 결과
이러한 문제점을 해결하기 위해 다음과 같은 변경 사항이 적용되었습니다.
* 정적 HTML 문자열과 동적 부분의 분리된 푸시.
* 생성된 코드에서 rescue
절을 제거하고, Proc#render
에서 백트레이스 변환을 한 번만 수행.
* 컴파일된 코드 상단에 # frozen_string_literal: true
매직 코멘트를 추가하여 모든 정적 HTML 콘텐츠를 동결된 문자열로 만듦.
* CGI.escape_html
대신 ERB::Escape.html_escape
사용.
이러한 최적화 후 P2는 ERB 및 ERubi와 거의 동일한 성능을 보였습니다. 초기 벤치마크에서 ERubi보다 약 3배 느렸던 P2는 이제 ERB와 ERubi와 대등한 수준이 되었으며, 컴파일되지 않은 Papercraft나 Phlex보다 약 10배 빠른 성능을 보여주었습니다. 이는 컴파일 방식이 템플릿 생성 성능에 미치는 긍정적인 영향을 명확히 보여줍니다.