The cost of JavaScript in 2019을 번역 요약한 글입니다.
이것이 웹 개발자들에게 의미하는 것이 무엇이냐? 파싱과 컴파일에 드는 시간이 더이상 생각만큼 느리지 않다는 것이다. 자바스크립트 번들에 필요한 세가지 사항은 아래와 같다.
Chrome has a minimum size for code caches, currently set to 1 KiB of source code. This means that smaller scripts are not cached at all, since we consider the overheads to be greater than the benefits.
왜 다운로드와 실행 시간을 최적화 해야하는가? 다운로드 시간은 성능이 구린 네트워크 환경에서 치명적이다. 4G와 5G 환경이 전세계적으로 구축되고 있지만, NetworkInformation.effectiveType 는 많은 요소들로 인해 3G 혹은 더 느린 네트워크 처럼 작동할 수 있다.
다른 여러가지 요소로 인해 매번 빠른 속도를 유지해 줄 수 없다는 뜻 같습니다.
자바스크립트의 실행속도는 느린 CPU를 사용하는 스마트폰에서 중요하다. CPU, GPU, 서멀 스로틀링 등의 차이로 인해 고오급 스마트폰과 보급형 스마트폰의 성능 차이가 크다. 이는 실행 시간이 CPU와 연관되어 있기 때문에, 자바스크립트 성능에 있어 중요하다.
사실 크롬과 같은 브라우저에서 전체 페이지 로딩에 걸리는 시간 중 최대 30% 정도는 자바스크립트 실행에 사용될 수 있다. 아래는 일반적인 웹사이트 Reddit.com을 하이엔드 데스크톱에서 접근했을 때 걸리는 시간이다.
그러나 모바일에서는 이와 상황이 많이 다르다.
자바스크립트 실행 시간을 최적화 할때는, UI스레드를 장기간 독점하고 있는 Long Tasks에 주의 하자. 이는 시각적으로 페이지가 준비된 것처럼 보여도, 중요한 태스크 실행을 차단하고 있을 수 있다. 이러한 것들은 가능한 작게 나누어야 한다. 코드를 분할하고, 로드 되는 순서에 우선순위를 지정하여 페이지 상호작용을 더 빠르게 할 수 있고, 입력 지연 시간을 줄일 수도 있다.
V8의 원시 자바스크립트 구문 분석속도는 크롬 60 이후 2배가량 증가했다. 동시에, 파싱과 컴파일 비용은 이를 병렬화 하는 크롬의 다른 최적화 작업으로 인해 덜 눈에 띄게 되었고, 중요성도 떨어졌다.
V8은 메인 스레드의 파싱 및 컴파일 작업을 평균 40% 감소시켰다(Facebook 46%, 핀터레스트 62%, youtube 80%)
또한 V8의 서로 다른 버전에서 이러한 변경점이 CPU 시간에 미치는 영향을 시각화 할수 있다.
이러한 변화들이 어떻게 이루어졌는지 살펴보자. 요약하자면, 스크립트 리소스는 worker스레드에서 스트리밍 파싱되고, 컴파일 될 수 있는데 이는 다음과 같은 것을 의미한다.
<script/>
태그를 만나면 스트리밍이 시작된다. 파서 블락 스크립트의 경우, HTML파서가 만들어내지만, 다른 async
스크립트는 계속해서 진행된다.오래된 크롬 버전의 경우, 파싱을 시작하기전에 스크립트를 모두 다운로드 해야 했으므로, 이는 간단한 접근 법인 한편으로 CPU를 충분히 사용하지는 못했다. 41~68사이의 크롬은 다운로드가 시작되자마자 별도의 스레드에서 async
deferred
스크립트 구문을 파싱하기 시작했다.
크롬 72에서는, 스트리밍을 파싱을 하는 주요 방법으로 채택했다. 이제는 일반 동기식 스크립트도 동일하게 파싱된다. (인라인은 제외) 또한 메인 스레드에서 필요로 할 경우 이미 수행된 모든 작업을 불필요하게 복제하기 때문에, 작업 기반 구문분석을 취소하는 것을 중단했다.
이전 버전의 크롬은 스트리밍 파싱과 컴파일을 지원했는데, 여기에서 네트워크로 부터 들어오는 스크립트 소스 데이터가 스트리머로 전달되기 전에 크롬의 메인 스레드로 이동해야 했다.
이로 인해 스트리밍 파서는 이미 네트워크에서 도착한 데이터를 기다리는 경우가 많았지만, 메인 스레드의 다른 작업 (HTML파싱, 레이아웃, 또는 자바스크립트 실행 등) 에 의해 차단되서 아직 스트리밍 작업으로 전달되지 않았다.
레딧의 경우 100+kb의 번들이 몇개 있는데, 이번들은 외부 다른 함수에 의해 랩핑되어 있어서 메인스레드에 레이지 컴파일을 야기한다.
페이스북은 압축된 6mb 정도의 데이터를 최대 292개의 요청으로 부터 가져오는데, 몇개는 비동기로, preloaded로, 혹은 낮은 우선순위로 fetch 된다. 대부분의 스크립트는 작고 세분화되어 있다. 이러한 작은 스크립트는 스트리밍-파싱 / 컴파일을 할 수 있기 때문에 백가라운드 - worker 스레드에서 전체 병렬화에 많은 도움을 준다.
JSON 문법은 자바스크립트 문법에 비해 간결하므로,JSON을 파싱하는 것이 자바스크립트를 파싱하는 것보다 더욱 효과적이다. 이 사실은 JSON 객체 리터럴을 필요로 하는 웹 앱의 시작 성능 향상에 적용할 수 있다.
const data = { foo: 42, bar: 1337 } // 🐌 객체 리터럴이라 느리다.
위 형식은 JSON 형식으로 바꿔 쓸 수 있고, 이는 런타임에서 더 빠르게 실행된다.
const data = JSON.parse('{"foo":42,"bar":1337}') // 🚀
JSON 문자열은 딱 한번 수행되기 때문에, JSON.parse
접근은 자바스크립트 객체 리터럴에 비해 훨씬 빠르며, 특히 콜드 로드 시 더 두각을 나타낸다. 경험적으로 보았을때, 10kb 이상의 객체에 이기법을 적용하는 것이 효과적인데, 이를 적용하기 전에 실제로 테스트 해보기를 권한다.
V8의 코드 캐싱 최적화가 도움을 줄 수 있다. 스크립트가 처음 요청되면 크롬이 다운로드 하여 V8에 전달하고, 이를 컴파일 한다. 이후 브라우저의 온디스크 캐시에 파일을 저장한다. 만약 JS 파일이 두번째로 요청되면, 크롬은 브라우저에서 캐시된 파일을 가져다가 다시 V8에 전달하여 컴파일 한다. 그러나 이번에는 컴파일된 코드가 직렬화 되어, 캐시된 스크립트 파일에 메타데이터로 첨부된다.
세번째에는, 크롬은 캐시에서 파일과 파일의 메타데이터를 모두가져가서 V8에 넘겨준다.V8은 메타데이터를 역질렬화하여 컴파일을 건너 뛸 수 있다. 처음 두번의 방문이 72시간내에 이루어지면 코드 캐싱이 시작된다. 크롬은 또는 서비스 워커가 스크립트를 캐싱하는데 사용되는 경우 빠른 코드 캐싱을 할 수도 있다.
defered
스크립트 등을 작은 번들로 만드는 것을 목표로 하라.