GDG WebTech 워크샵에 참여해서 들었던 크롬 브라우저를 이용한 성능 측정 방법을 정리한 것
웹 브라우저(특히 크롬)에서 렌더링의 성능을 결정하는데 중요한 것이 무엇인지 알아보고 그 것의 개념과 개선하기 위한 방법을 알아본다.
하드웨어 가속을 사용하는 것은 같은 기능을 하드웨어(GPU)의 도움을 받는 것을 의미한다.
소프트웨어 렌더링 실행 구조
계산, 메모리에 올리는 일, 출력하는 일을 모두 도맡아 하게 된다.
소프트웨어 렌더링 성능 = 주요 기능의 수행시간 + 그래픽스 출력 시간
따라서 주요 기능의 수행은 CPU에 맡기고 그래픽 관련 처리를 GPU에 맡기는 것이 포인트.
CPU가 어떤 것을 그려야 하는지 GPU에 넘겨 주는데 서로 다른 메모리 공간을 사용하기 때문에 BUS를 통해 CPU -> GPU 전달한다. 문제는 이 BUS를 통한 커뮤니케이션이 느리다는 것.
처리할 모든 데이터는 메모리에 있지만, 이 메모리는 한계가 있다는 것.
CPU의 데이터 변경 시 GPU 메모리도 변경되어야 반영할 수 있다.
GPU는 공간 좌표(Vertex)를 모아 도형(Polygon)을 만들고 여기에 이미지(Texture)를 씌워 그린다(Mapping).
GPU는 수신된 데이터로 무언가를 그리는데 적합하다.
CPU -[BUS]-> GPU 데이터 전송 속도
GPU의 데이터는 CPU에서 생성 후 전송한다.
즉, CPU에서 데이터 가공 시간 + CPU -> GPU 데이터 전달 시간이 문제이다.
[DOM Tree] --------
| --- [Render tree] --- [Paint!]
[Styles struct] ---
위에 서술한 부분들은 모두 Render tree 부분이다.
레이어(Layer)는 웹페이지를 렌더링하기 위해 필요한 이미지 단위 요소.
CPU가 이 레이어를 생성한다. 즉, 레이어에서 생성되는 이미지는 CPU 시간 소모.
웹 페이지는 레이어들의 겹침으로 표현된다.
각 요소들을 텍스쳐 이미지를 합치는 GPU의 작업.
DOM 노드가 가지는 레이아웃 정보가 변경되면 재배치를 위한 계산이 필요하다.
레이아웃의 변경이 트리를 따라 전파되고, 많은 경우 레이어 이미지의 갱신이 필요하다.
레이아웃 내 컨텐츠 변경 시 텍스쳐를 새로 생성 필요.
어떤 노드가 렌더링하는데 오래 걸리는지 알 수 있다.
체크하면 repaint 되는 부분을 강조 표시해 준다.
애니메이션이 일어나는 부분들은 쉽게 볼 수 있다.
체크하면 레이아웃의 레이어 구조를 볼 수 있다.
오렌지색 선이 레이어이며, 이는 GPU에 의해 렌더링 되는 것을 의미한다.
크롬 개발자 도구는 GUI를 지원하므로 디테일한 시간으로 캐치하기 힘들다.
이를 위해서 코드 내에서 캐치할 수 있는 API가 존재한다.
실제로 사용해보지 않았음.
이하는 크롬에서만 동작할 것
Reference: https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
console.timeline
레이어는 GPU가 그리기 때문에 빠른 성능을 요구하는 부분을 레이어로 분리하여 GPU가 그리게 하는 것.
하지만 모든 렌더링을 GPU에 전가하는 것 또한 병목현상을 유발하므로 최소한으로 분리하는 것이 좋다.
크롬에서 DOM 노드가 레이어로 분리되는 조건
<video>
Element<canvas>
ElementZ축 값으로 0을 주는 무의미한 코드이나 레이어 분리 조건에 들어간다.
필요한 경우가 아니라면 사용하지 않는 것이 좋다.
이는 추가적인 메모리를 소모함을 의미하며, 메모리는 유한하다.
메모리 공간이 부족해지면 기존 데이터 릴리즈 후 새로운 메모리를 업로드 한다.
이 경우가 반복되면 레이어 분리를 통한 성능 이점이 오버헤드로 상쇄된다.
will-change: 속성명;
이 속성이 변경 될 수 있음을 브라우저에 힌트를 준다. 브라우저는 실제 요소가 변화되기 전에 적절하게 최적화 할 수 있게 된다.
로딩을 개선하기 위해서 목표는 다음과 같다:
그러면 위 목표들을 위해서 무엇을 해야 할까?
iFrame은 사용하지 않음에도 메인이 로드될 때 함께 로드된다. 메인 시스템이 로드 되는 것을 방해하는 것이다.
스크립트를 통해 메인 시스템 로드 후 로드될 수 있도록 한다.
스크립트 예
<iframe data-src="https://example.com"></firame>
<iframe data-src="https://another.example.com"></firame>
<script>
document.addEventListener('load', () => {
Array.from(document.querySelectorAll('iframe'))
.forEach(iframe => iframe.src = iframe.dataset.src);
});
</script>
기존의 HTTP 1.1은 분할된 이미지가 있으면 요청을 분할하여 여러번 나눠서 요청한다.
이미지를 255조각 내었다면 요청을 255번 하는 것이다.
반면에 HTTP/2는 한 번에 모두 요청한다.
HTTP/2를 사용하려면 https를 사용해야 한다.
오프라인에 대응할 수 있고, 싱글 스레드인 자바스크립트에서 분리된 스레드로 앱의 백그라운드에서 동작하는 서비스 워커에 대해 알아본다.
네트워크가 애매한 경우 (Lie-fi 라고 한다.) 앱은 느린 네트워크를 통해 데이터를 받아 오려고 시도하고 사용자는 언제 로드 될지 알 수 없는 컨텐츠를 하염없이 기다려야 한다.
서비스 워커의 캐싱을 사용하면 요청 중 오프라인이 되거나 서버로부터 컨텐츠를 받기 전까지 사용자에게 저장된 캐싱 데이터를 보여줄 수 있다.
서비스 워커는 특정한 워커 중 하나이다.
그리고 웹 워커는 메인 페이지와 병렬 스크립트를 실행하는 백그라운드 워커를 생성하는 API다.
메시지 전송 기반의 Thread와 유사한 동작을 가능하게 한다.
즉, 병렬 스크립트 실행을 위한 API.
특징으로는
지속적인 백그라운드 처리를 하는 워커로 이벤트 드리븐 모델이다.
지속적이라는 의미는 페이지가 로딩되지 않았을 때도 동작한다.
따라서 페이지가 실행되지 않더라도 브라우저가 관리한다.
크롬 브라우저는 오프라인일 때 간단한 게임을 할 수 있는 서비스 페이지를 제공한다.
오프라인 캐쉬는 이용자가 네트워크 요청시 서버로 바로 가는 것이 아니라 개발자가 어떻게 동작할 것인지 제어할 수 있는 기능이다.
위에서 페이지가 로딩되지 않았을 때도 동작한다고 하였다.
이를 이용하여 사용자가 데이터 입력 중 인터넷이 끊어지더라도 데이터를 저장해 두었다가 나중에 네트워크 연결이 되면 앱을 다시 실행하지 않더라도 데이터를 서버에 보낼 수 기능을 구현할 수 있다.
Using service worker you can hijack connections, fabricate, and filter responses. Powerful stuff. While you would use these powers for good, a man-in-the-middle might not. To avoid this, you can only register for service workers on pages served over HTTPS, so we know the service worker the browser receives hasn't been tampered with during its journey through the network.
HTTPS를 통해 제공되어야만 한다. 데이터의 신뢰성이 보장되지 않으면 사용자의 의도와는 다른 데이터를 서버에 전송할 수 있기 때문이다.
Polyfill이 없어서 시스템에서 지원하지 않으면 그냥 동작하지 않는다.