7. V8 JIT 컴파일러의 구조적 한계
Parser FullCodeGen
Crankshaft
*.js Native
Code
Optimized
Native
Code
Code Cache
Hot Spot
FullCodeGen: depth first traversal 기반 AST 처리의 비효율성
Crankshaft: target ISA에 독립적인 최적화의 추가가 어려운 layer
AST
8. V8 6.0 이후 자바스크립트 실행
Ignition + Turbofan pipeline
1. Ignition: 바이트코드 인터프리터, FullCodeGen의 컴파일 과정 제거
2. Turbofan: 최적화 JIT 컴파일러, 더욱 최적화된 native code 생성
3. 소스코드를 변환한 바이트코드를 Ignition으로 실행
4. 실행 빈도가 높은 자바스크립트는 Turbofan으로 다시 컴파일
9. V8 6.0 이후 JIT 컴파일 Pipeline
Ignition에 의한 응답성 개선 및 메모리 사용량 감소 (5~10%)
Turbofan의 최적화된 native code로 벤치마크 성능 향상 (5~10%)
Parser Ignition
Turbofan
*.js
Optimized
Native
Code
Code Cache
Hot Spot
Bytecode
10. 자바스크립트 성능은 여전히 문제인가?
웹페이지 로딩 시간의 제 1 요소
- 뚜렷한 hot spot이 없는 자바스크립트 로딩 (다량의 객체 생성 및 초기화)
- 따라서 Crankshaft나 Turbofan에 의한 최적화는 가성비가 낮음
- 하지만 자주 방문하는 웹페이지라면?
11. HTTP Cache 기반 JS AOT 컴파일
HTTP Cache
자주 방문하는 URL에 포함된 resource(js, css, img, ...)를 disk cache에 저장 후 재사용
자바스크립트 Ahead-of-Time (AOT) 컴파일
- 자바스크립트를 JIT 컴파일한 native code를 HTTP cache에 저장 후 재사용함
- 동일한 자바스크립트를 매번 JIT 컴파일하는 부담을 버릴 수 있음
12. V8의 AOT 컴파일
FullCodeGen 기반 AOT
1. FullCodeGen으로 native code 생성
2. Serializer가 native code를 snapshot 형태로 변환
3. Http cache에 의해 snapshot이 disk에 저장
4. Cache hit된 자바스크립트의 snapshot을 disk로 부터 로드
5. Deserializer가 snapshot의 native code 복원
14. 캐시된 자바스크립트를 더 빠르게
Crankshaft로 AOT 컴파일한다면...
1. FullCodeGen에 포함되지 않은 다양한 코드 최적화 기법 적용
- e.g. loop 최적화, 코드 배치 최적화, 번복 연산 제거, ...
2. AOT 컴파일시 추가적인 최적화 비용 1회 발생
2. 그러나 이후 로딩 시간에 실행되는 캐시된 자바스크립트를 매번 최적화 비용 없이 가속화
16. 일부 제한 및 Backup Plan
Crankshaft로 AOT 컴파일이 가능하려면...
1. Crankshaft로 재활용 가능한 neutral native code를 생성
- Runtime profiling을 바탕으로 type에 특화된 최적화 제외
- 실행 환경에 종속적인 정보 제거 (e.g. V8 context)
2. 오류 발생 시 FullCodeGen의 native code로 전환될 수 있는 deoptimization 지원
3. 다양한 AOT failure에 대한 대처
- Crankshaft 컴파일, Serialization, Deserialization 실패
18. 개발 중 가장 큰 시련은...
첫째도, 둘째도, 셋째도 최적화된 native code debugging
Source level debugging 불가능
- assembly code, memory dump, register value를 열심히 trace할 수 밖에…
1. JavaScript test에서 발생하면 다행!
2. 100% 재현 가능한 crash면 해볼 만 하고...
3. 간헐적으로 발생하거나 웹페이지 기능에 문제가 생기면 좌절ㅠㅠ
- 페이지 로딩이 완료되어도 불규칙한 시간이 지난 후 뻗는다던지,
- Smart Editor 3.0 웹페이지의 로딩 후 글이 안 써지기도...
19. Crankshaft AOT의 성능
웹페이지 로딩 시간 단축
실제 실행 여부에 상관없이 적극적으로 AOT를 적용할(eager AOT) 때 더욱 효과적
92%
93%
94%
95%
96%
97%
98%
99%
100%
101%
OnLoad FirstMeaningfulPaint
w/o AOT w/ AOT w/ eager AOT
22. 브라우저에게 시동 시간이란?
아이콘 클릭해도 아무것도 안 뜨고 시계만 돌고 있다면
“뭐, 이리 느려... 빨리 좀 떠라...”
아이콘 클릭과 동시에 창이 뜨고 첫페이지 로딩이 시작돼야
“오~ 빠릿하네! ㅋ”
브라우저 성능의 첫 인상을 좌우하는 지표
초기 웨일은 크롬과 동일하게 창이 뜨기 까지 1초 이상 걸림
24. Crashpad 프로세스의 생성 시간
Crashpad 프로세스
타 프로세스에서 크래시가 발생하면 stack trace와 여러 metadata를 갈무리하여 저장
브라우저 프로세스의 생성부터 시동 사이에 Crashpad 프로세스 생성
500ms 소모
그래서 Lazy Crashpad
Crashpad 프로세스의 생성을 지연시킴으로써 웨일의 시동 시간 단축
브라우저 프로세스의 process launcher thread에서 렌더러와 GPU 이후에 생성
25. 시동 시간 개선 및 부작용
체감할 수 있는 웨일 시동 성능 개선
500ms 감소
Crashpad 생성 이전에 발생하는 crash dump를 생성할 수 없음
원격으로 해당 crash dump를 수집할 수 없어 재현 경로를 확보하지 못한 크래시의 해결이
어려움
30. 메모리가 계속 증가하는 이유가 뭘까?
Cache의 크기가 무한히 늘어나는가?
Cache를 사용하지 않게 하여도 메모리가 증가
Memory Pressure가 제대로 동작하는가?
Memory Cache 및 Decoded Image 삭제, GC 등으로 꾸준히 메모리 확보
Garbage Collector가 제대로 동작하는가?
JavaScript가 없는 페이지에서도 여전히 메모리 증가
31. 메모리 누수의 범인은 누구인가?
범인은 이 가운데 있어!!
WebCore
JavaScriptCore
Third-Party Library
WebCore와 JavaScriptCore 메모리 측정
총 메모리 = WebCore + JavaScriptCore + Third-Party Library
찬조출연 : 소년탐정 김전일
33. WebCore를 모두 들여다 봐야하나?
코드 리딩의 한계
총 317만 라인, .CPP, .H 만 세어보아도 158만 라인
한정된 리소스
소스코드 분석에 너무 많은 인력과 시간이 필요
Document의 부재
분석에 도움이 될만한 상세한 문서가 없음
34. 기존 메모리 프로파일링 기법
Dynamic Binary Instrumentation Framework
Valgrind (Memcheck), Pin, DynamoRio (Dr. Memory)
컴파일러 기반 Instrumentation
GCC Instrumentation, LLVM (Leak Sanitizer with ASan)
그 외
DLL Injection, CRT Debug Heap 등
35. 윈도우에서 사용가능 한 기법은?
Dynamic Binary Instrumentation Framework
Valgrind (Memcheck), Pin, DynamoRio (Dr. Memory)
컴파일러 기반 Instrumentation
GCC Instrumentation, LLVM (Leak Sanitizer with ASan)
그 외
DLL Injection, CRT Debug Heap, WinDbg, UMDH 등
36. 할 수 있는 건 다 해봤는데...
비정상 종료
안정적으로 실행이 되지 않아 데이터를 뽑을 수 없거나 크래시 발생
무의미할 만큼 많은 데이터 제공
어떤 것이 실제 문제인지 파악할 수 없어 로그 분석에 너무 많은 시간이 소요
너무 느린 속도
데이터 분석에 너무 오랜 시간이 걸려서 사용이 불가능
좌절하는 코이와이 씨 (요츠바랑! 4권 175쪽)
37. 하지만, 느린 건 빠르게 만들면 된다
WinDbg는 안정적이면서 매우 강력한 도구
분석에 필요한 다양한 명령어 지원, 그러나 수정 불가
User-Mode Dump Heap
Snapshot 기반으로 파일로 데이터 저장 및 결과 출력, 역시 수정 불가
하지만, UMDH Log 분석은 빠르게 할 수 있지 않을까?
로그 형식을 분석하여 보다 빠르게 프로파일링 결과 출력
41. 측정 결과
UMDH FastUMDH Parse Extract Symbol Print
LeakTest 7.469 0.018 34.9% 0.4% 62.9% 1.8%
PDF Viewer 6.353 0.060 72.1% 1.2% 25.6% 1.0%
WebBrowser 777.296 3.668 5.3% 0.1% 92.6% 1.9%
WebBrowser 11,735.696 17.295 2.7% 0.1% 95.5% 1.7%
Symbol은 모두 디스크 캐싱이 된 상태에서 실행
43. 이제 문제를 찾아 볼까?
메모리 모니터링 도구에 포함
웹 사이트 이동 마다 Snapshot을 생성하여 결과 분석
어느 사이트가 메모리 누수를 유발하는지 탐색
메모리 할당을 많이 하고 해제하지 않는 로그 분석
주요한 콜 스택 분석
SVG 이미지 관련 로그가 다수 발견됨
44. SVG만 열심히 돌려보자
Leaky Image : http://static2.wikia.nocookie.net/__cb20100227155641/finalfantasy/images/9/94/Final_Fantasy_10_Sphere_Grid.svg
47. Continuous Integration
빌드 봇 연동
개발 버전의 메모리를 모니터링하여 리그레션이 발생하는지 확인
다양한 메모리 누수 탐지
Cairo, Font, Translation 등 메모리 누수 탐지 및 해결
겨우 메모리를 안정화했는데...
전격! 크로미움 기반으로 변경!
http://cfile24.uf.tistory.com/image/210D9D40536F62892C826C
51. 프로세스 모델이 다르네?
기본 프로세스 모델 : Process-per-site-instance
사용자가 방문한 사이트 인스턴스 별로 렌더러 프로세스가 할당됨
기존의 탭당 하나의 전담 렌더러 프로세스 구조인 Process-per-tab과 차이가 있음
“새 술은 새 부대에” : Cross-Process Navigations
한 탭에서 다른 사이트로 이동하는 경우 새로운 렌더러 프로세스로 교체됨
- 옴니박스에 새로운 도메인 입력
- 북마크 클릭
- 탭 내에서 링크를 통한 사이트 전환
52. 언제 메모리가 부족할까?
물리 메모리 크기가 적은 경우
32bit 윈도우와 3GB 이하의 메모리를 사용하는 시스템
탭을 많이 사용하는 경우
검색, 쇼핑, 유튜브 등등 탭은 늘어나는데 닫지 않을 때
다른 프로그램이 메모리를 많이 사용하고 있는 경우
게임 같은 프로그램이 메모리를 많이 사용하여 브라우저가 사용할 메모리가 부족한 경우
53. 크래시 보다 나쁜 건 없다!
결국은 가용 메모리가 부족한 상황
가용 메모리의 부족으로 OOM 크래시를 만드는 것 보다는
성능을 떨어뜨리더라도 쓰지 않는 메모리를 회수하자
- 물리 메모리 크기가 적은 경우
- 탭을 많이 사용하는 경우
- 다른 프로그램이 메모리를 많이 사용하고 있는 경우
54. 메모리를 어떻게 회수하지?
우리는 정말 많은 탭을 쓴다
실제로 사용하는 탭은 몇 개 되지 않는다
그렇다면 안쓰는 Inactive 탭에서 메모리를 회수할 수 없을까?
55. 메모리 세이버
놀고 있는 탭이 있다면 잠시 꺼 두셔도 좋습니다
Tab Discarding 기법을 이용하여 Inactive Tab의 프로세스를 제거하여 메모리 회수
다시 탭을 보고 싶으면 어떡해?
최대한 기존 데이터를 복원하여 불편함을 최소화
결제 중이거나 음악을 듣고 있다면?
결제, 미디어탭 등 중요한 사이트나 복원이 어려운 사이트는 대상에서 제외
56. 메모리 세이버 레벨
레벨 조건 대상 반복 적용 유지 탭 수
1 (최소) 크래시 직전 10분 한번 최소 4개
2 (기본) 항시 2시간 한번 최소 3개
3 항시 1시간 (최대 4시간) 반복 최소 2개
4 항시 30분 (최대 4시간) 반복 최소 1개
5 (강력) 항시 10분 (최대 2시간) 반복 최소 4개
※ 사용자 피드백을 바탕으로 추후 변경될 수 있음
57. 메모리 사용량 비교
프로세스 사용량 (최소)
Browser Process 1개
Crashpad Handler 1개
GPU Process 1개
Default Extension Process 1개
PPAPI Process 1개
Renderer Process 17개
총 22개 프로세스, 1,654MB 소모
프로세스 사용량 (강력)
Browser Process 1개
Crashpad Handler 1개
GPU Process 1개
Default Extension Process 1개
Renderer Process 3개
총 7개 프로세스, 476MB 소모