Sơ lược về JavaScript Engine

19 tháng 5, 2021 By DEVERA ACADEMY

Tất cả các engine JavaScript đều phải tuân theo tiêu chuẩn ECMAScript Standard (giống như C phải tuân theo ANSI C/C99/...). Mục đích của việc này là để chuẩn hóa cách hoạt động và tính năng của các engine.

Xét về các ngôn ngữ bậc cao, thì JavaScript nằm xa mã máy hơn nhiều so với những ngôn ngữ như C và C++.

Đầu tiên, ta xem rằng engine JS nó nằm ở đâu trong hệ thống của browser.

Blink là rendering engine phụ trách toàn bộ quá trình render, bao gồm DOM tree, styles, events và có tích hợp V8.

V8 là một JavaScript engine được phát triển bởi Google và hiện tại được dùng bởi Chrome và NodeJS.

Biên dịch và phiên dịch

Biên dịch (Compilation) và phiên dịch (Interpretation) là 2 nguyên tắc hoạt động của nhiều ngôn ngữ lập trình hiện nay. Nếu nói về JS thì JS sử dụng cả 2 nguyên tắc này.

Các JS engine hiện đại sử dụng JIT compilation (compile ở runtime)

Just-In-Time(JIT)

Một lợi ích lớn của trình biên dịch JIT là một khi code đã bắt đầu chạy, compiler sẽ bắt đầu tối ưu hóa tùy theo nguyên lý hoạt động của JIT compiler đó.

Mỗi browser và runtime thường có một JIT compiler riêng, tuy nhiên cấu trúc chung thường sẽ giống nhau và giống như sau:


Phiên dịch và thực thi được phụ trách bởi trình phiên dịch Ignition. Mặc dù kết quả phiên dịch của Ignition là low-level bytecode chậm hơn so với mã máy nhưng quá trình biên dịch của bytecode lại nhanh hơn.

Trình biên dịch Full-codegen được dùng để biên dịch bytecode thành mã máy chưa tối ưu. Sau đó, trình biên dịch JIT Turbofan sẽ biên dịch code và bắt đầu tối ưu hóa những phần code được sử dụng nhiều trong quá trình thực thi code.

Các object được sử dụng trong quá trình thực thi code đều được giám sát bởi garbage collector. Khi cần dọn dẹp các object, V8 tự động ngừng thực thi chương trình.

Profiler

Nhiệm vụ chính của profiler là giúp tối ưu hóa code. Đầu tiên, profiler đưa code qua interpreter. Profiler quan sát quá trình thực thi code, xem đoạn code nào “ấm” (thực thi một vài lần) và đoạn code nào “nóng” (thực thi nhiều lần)

The baseline compiler

JIT gửi những đoạn code “ấm” cho compiler tiêu chuẩn và cố tái sử dụng những đoạn code đã được compile này.

JIT cũng gửi những đoạn code “nóng” cho compiler tối ưu hóa. Compiler này dùng những thông tin thu thập được từ trình biên dịch để đưa ra các giả thuyết và thực hiện tối ưu hóa code dựa trên các giả thuyết đó.

Tuy nhiên, nếu các giả thuyết trên bị phát hiện là sai, compiler tối ưu hóa sẽ thực hiện deoptimization (loại bỏ phần code đã được tối ưu hóa)

V8 JavaScript Engine

V8 là engine JavaScript và WebAssembly open-source của Google, được viết bằng ngôn ngữ C++. V8 được dùng trong Chrome và Node.js.

Ignition dịch abstract syntax tree (AST) thành bytecode. Chuỗi bytecode này được thực thi. Trong quá trình thực thi, Ignition theo dõi và ghi lại những thông tin cần thiết giúp Ignition thực thi code đúng và giúp TurboFan tối ưu hóa code.

TurboFan tối ưu hóa bytecode bằng cách dịch bytecode thành mã máy theo kiến trúc máy tính đang chạy V8.

Ignition

Ignition có vai trò giảm kích thước code và tăng tốc độ xử lý của các script. Bytecode tạo bởi Ignition giảm kích thước từ 25-50%, tăng độ tương thích cho V8 với nhiều use case khác nhau và giúp tăng tốc cho các thiết bị Android có bộ nhớ ít.

Ngoài ra, việc biên dịch thành bytecode đồng nghĩa rằng lúc tối ưu hóa, TurboFan không cần phải biên dịch lại JavaScript gốc, giúp giảm lượng bộ nhớ của mỗi tab Chrome khoảng 5%. Việc này rất đáng kể so với các máy có cấu hình thấp, đang chạy nhiều tab.

TurboFan

TurboFan sử dụng các thông tin thu thập được từ Ignition để dịch và tối ưu hóa bytecode thành mã máy.

TurboFan được thiết kế với cấu trúc sea (soup) of nodes hướng tới các mục tiêu sau:

  • Đạt được hiệu suất cao nhất có thể

  • Vận dụng tối đa thông tin kiểu tĩnh (Static type information) 

  • Giảm thời gian implement giữa các nền tảng khác nhau

  • Tối ưu hóa cho việc kiểm thử

Khi compiler thu thập được thông tin về một đoạn code “nóng” được gọi nhiều lần, với các loại tham số giống nhau, đoạn code đó sẽ được truyền cho TurboFan. TurboFan sẽ tạo mã máy tối ưu hóa, chạy trực tiếp trên CPU (thay vì phải phiên dịch), khiến cho tốc độ đoạn chương trình đó cao hơn tất nhiều.

JavaScript Runtime

So với các ngôn ngữ lập trình khác, JavaScript là một ngôn ngữ single-threaded vào lúc runtime. Khi ta dùng trình duyệt web, đôi khi ta sẽ gặp hiện tượng đứng máy. Đây là do đang có một phần code chạy trên thread chính của JavaScript chặn các tác vụ khác. Thread này có nhiệm vụ điều khiển mọi thứ như cuộn trang, quản lý DOM và các tác vụ khác. Do đó, khi có một đoạn code đang chặn thread chính thì cả trình duyệt sẽ đứng, đến khi nào tác vụ đó hoàn tất.


Tác giả Ritik Chopra

Dịch bởi Devera Academy