Advance Javascript concepts - Javscript V8 Engine

V8 Engine là gì ?

- V8 là một trình biên dịch và thực thi code Javascript được viết chủ yếu bằng C++ và Javascript nó có nhiệm vụ cấp phát bộ nhớ cho các đối tượng và thu hồi chúng khi chúng không được sử dụng nữa, V8 được nhúng vào 2 nền tảng nổi tiếng là Chrome và Node.js.

- V8 tuân theo chuẩn ECMA script nó cung cấp data types, loop, prototype, object, trình thu hồi rác. Nói đơn giản thì V8 giúp cho Node.js hiểu được code Javascript chúng ta viết, là thứ thực thi code Javascript (native). Javascript native là những thứ như vòng lặp, hàm, nguyên mẫu, biến.


Concept đơn giản về Javascript Engine

Hầu như tất cả mọi người đều biết về JSE , nhưng ý tưởng thực sự về nó và những gì thật sự diễn ra đằng sau hậu trường thì không nhiều người biết.

=> chúng ta đều biết rằng JS là một ngôn ngữ đơn luồng (Single-thread)
=> JS sử dụng callbackQueue và nó là một ngôn ngữ thông dịch.

Bạn có bao giờ thắc mắc rằng những dòng code bạn viết ra được chạy như thế nào ? Trước hết bạn cần hiểu rằng máy tính chỉ có thể hiểu được mã máy, nó chỉ thực hiện các hành động dựa trên chỉ dẫn được viết bằng mã máy
=> Tại sao viết code bằng JS mà nó vẫn chạy ? => Bởi vì có JSE làm trung gian, hiểu đơn giản nó là một phiên dịch viên dịch code của bạn sang machine code hoặc byte code nhằm mục đích thực thi tệp JS.

JSE là gì ?
- Nó được coi như một phiên dịch viên.
- Tuân thủ theo đặc tả ECMA Script
- Nó được viết bằng C++ phổ biến nhất là V8.

Những điều thực sự xảy ra bên trong V8

1.Trình biên dịch và trình thông dịch


Để hiểu rõ về cách thức hoạt động của JSE trong bài viết này là V8 bạn cần hiểu được một số định nghĩa cơ bản về trình biên dịch và trình thông dịch.
Có thể bạn đã biết rằng trong lập trình thường có 2 cách để dịch một ngôn ngữ nhằm mục đích máy tính có thể hiểu và thực thi.
+ Thông dịch : dịch từng dòng và thực thi ngay lập tức , hiểu đơn giả như thế này trình thông dịch dịch tệp JS theo từng dòng, bảo gì làm cái ấy ngay lập tức.
+ Biên dịch : không thực thi code ngay lập tức như trình thông dịch, những gì nó làm đầu tiên là cố gắng hiểu xem tệp đầu vào muốn thực thi hành động nào và dịch tệp đầu vào sang ngôn ngữ mà máy tính có thể hiểu được.



Nhìn biểu đồ bạn có thể thấy 
=> Trình thông dịch lấy từng khối code trong tệp JS và dịch nó sang Bytecode
=> Trình biên dịch lấy cả tệp và dịch nó sang Machinecode
(Chú ý rằng không nhất thiết trình thông dịch chỉ dịch sang Bytecode và trình thông dịch chỉ dịch sang Machinecode đâu nhé hoàn toàn có thể hoán đổi cho nhau)

Vậy Bytecode và Machinecode là gì ?

+ Machinecode : là một tập hợp các hướng dẫn được viết bằng ngôn ngữ máy (X86,...) hoặc mã nhị phân được thực thi trực tiếp bằng CPU.

 + Bytecode : là một dạng mã trung gian được tạo ra từ việc biên dịch mã nguồn có thể được thực thi bằng máy ảo.
Một chương trình máy tính là một tập hợp các hướng dẫn được viết bằng một ngôn ngữ lập trình nào đó, trình thông dịch và trình biên dịch sẽ dịch tệp này sang machinecode để máy tính có thể được thực thi. Mặt khác bytecode không phải là machinecode 100% mà chỉ là mã trung gian nó có thể được thực thi trực tiếp bằng máy ảo.(JVM,V8)


 Vậy Javascript là ngôn ngữ được thông dịch hay biên dịch ?



Ảnh trên mô tả cơ bản cách mà trình biên dịch và thông dịch hoạt động

Tại sao dùng trình thông dịch ?

- Thông dịch ban đầu sẽ thực thi chương trình nhanh hơn bởi vì nó không phải dịch JS sang ngôn ngữ khác (các hướng dẫn tùy vào bộ VXL) như với trình biên dịch, đây cũng là một tính chất rất JS ^^..nó phù hợp với tính chất tự nhiên của JS phía browser
=> Bởi vì khi tải tệp JS từ phía Server xuống chúng ta những nhà phát triển muốn tệp JS được thực thi ngay lập tức bởi vì người dùng đang đợi trên web cố gắng tương tác với ứng dụng => JS chạy càng nhanh càng tốt...vậy nên sử dụng trình thông dịch là hợp lý.
=> Nhưng có vấn đề phát sinh khi Google đã gặp phải khi họ chạy Gooogle Map trên trình duyệt
, Google Map chứa một tệp tin JS khổng lồ nó rất lớn , rất nhiều vòng lặp lúc này trình thông dịch bộ lộ điểm yếu nó chạy những tệp nặng chứa nhiều vòng lặp cực kì chậm chạp => Lý do là bởi vì trình thông dịch chạy một đoạn code nhiều lần
Ví dụ : Nếu bạn đang ở trong một vòng lặp , mỗi lần lặp có cùng một nhiệm vụ nhưng mỗi lần lặp trình thông dịch lại phải dịch lại mỗi lần vòng lặp chạy dẫn đến càng chạy càng chậm.

Tại sao dùng trình biên dịch ?

Ban đầu khi chương trình được thực thi trình biên dịch có vẻ yếu thế hơn bởi vì nó phải mất thêm một chút thời gian để khởi động ,phải trải qua bước dịch file code sang ngôn ngữ khác trình có một điều trình biên dịch hơn thông dịch rằng nó đủ thông minh để hiểu rằng nó đang chạy một vòng lặp, nó không cần dịch code mỗi lần lặp nữa => được gọi là optimization
=> Vậy nếu bạn chạy một tệp JS lớn, nhiều vòng lặp rõ ràng trình biên dịch có ưu thế hơn rất nhiều so với trình thông dịch.

Vậy cuối cùng sử dụng trình biên dịch hay thông dịch ?

Cả 2 đều có ưu nhược riêng
- Thông dịch : Thực thi tệp gần như ngay lập tức nhưng càng chạy càng chậm
-Biên dịch : Thực thi tệp chậm nhưng khi càng chạy càng nhanh (nhìn chung thì nó sẽ nhanh hơn thông dịch trong hầu hết các trường hợp ).
Có cách nào để kết hợp 2 ưu điểm của thông dịch và biên dịch với nhau ? 
=> Một số kỹ sư ở google đã nghĩ ra,  họ tự hỏi rằng nếu đã sử dụng V8 làm JSE thì sao nếu chúng ta kết hợp ưu điểm của cả hai ?
=> Ra đời JUST_IN_TIME COMPILE.


Những gì thực sự xảy ra bên trong V8

=> Đây là những gì thực sự xảy ra bên trong nó.


=> Bạn cung cấp tệp JS cho V8
- Step 1 : Sẽ có một trình phân tích, cấu trúc lại code của bạn nó sẽ xác định đâu là function , đâu là body của hàm, đâu là biến, nó sẽ cấu trúc lại để V8 có thể xác định ý nghĩa của đống code, V8 sẽ biết được code bạn đang cố gắng làm điều gì ? Sau khi cấu trúc nó sẽ có kiểu cấu trúc AST (Abstract syntax tree) dạng như thế này (bên trái là tệp JS bên phải là tệp JS sau khi cấu trúc lại)












 => Hãy xem cách V8 ENGINE làm điều này , đi sâu vào quá trình hoạt động của nó.

=> Quay trở lại với sơ đồ đầu tiên : Bạn nhớ rằng chúng ta đã đẩy tệp tin JS cho V8 rồi cấu trúc nó lại thành dạng Abstract Syntax Treee (AST)

=> Step 2 : AST được đẩy vào trình thông dịch trong V8 điều này được gọi là ignition 
, trình thông dịch lấy AST và dịch sang bytecode , về cơ bản bytecode có thể không gần với machinecode nhưng nó vẫn có thể được V8 thực thi.

=> Step 3 : Trong quá trình V8 được thực thi byte thì Profiler hay còn gọi là monitor sẽ quan sát và ghi chú (chẳng hạn như ghi chú xem vòng lặp này chạy bao nhiêu lần...) lại cách để làm thế nào có thể tối ưu code (Bạn chú ý một điểm rất quan trọng rằng Profiler quan sát và kiểm tra trong khi code đang chạy), Việc sử dụng Profiler ngay trong khi byte được dịch bởi trình thông dịch đang được thực thi sẽ có thể báo cho trình duyệt biết phải làm gì nếu một đoạn code được thực thi lặp lại quá nhiều lần.

=> Step 4 : Nếu một đoạn code được thực thi quá nhiều lần thì chúng ta sẽ đẩy đoạn code đó vào trong trình biên dịch, như mình đã nói bên trên trình biên dịch có khả năng tối ưu hóa code mà trình thông dịch không có. Hiểu đơn giản nó như kiểu trình thông dịch bảo với trình biên dịch rằng
"Hey đây là đoạn code tao éo tối ưu được, nó cứ lặp đi lặp lại nhiều lần tối ưu hóa giúp tao cái ^^."
Khi đẩy code vào trình biên dịch , nó sẽ cố gắng tối ưu hóa lại code sau đó thay thế một số thành phần của đoạn code thay cho bytecode chạy chậm, => hiểu đơn giản là nói trộn code của trình thông dịch đẩy cho và code nó tối ưu.=> Tạo ra optimize code

=> Step 5 : Optimize code được chạy


Câu trả lời cho câu hỏi Javascript là ngôn ngữ được thông dịch hay biên dịch ?

Câu trả lời là : Tùy thuộc về mặt kỹ thuật nó đươc thực thi như thế nào, như mình đã nói bên trên ở giai đoạn ban đầu code js được trình thông dịch dịch sang bytecode rồi thực thi nhưng cũng có trường hợp (đa số) code được chuyển cho trình biên dịch để tối ưu hóa rồi chạy tiếp.Như vậy thực sự rất khó để kết luận nó là thông dịch hay biên dịch vì tùy vào việc câu lệnh js được thực hiện.

Nhận xét

Bài đăng phổ biến