V8 engine là gì
V8 engine - một JS engine được sử dụng bởi Google Chrome để compile code JS sang maching code (đoạn script mà browser có thể hiểu được). Dựa vào
những cách thức hoạt động của nó chúng ta có thể optimize code nhằm tăng đáng kể được hiệu suất khi brower chạy code của ae (~ 50% theo
Session Stack)
Trước khi đến với những tips có một số keyword chúng ta cần nắm được, Đó là
Hidden Class, và
Inline Caching.
1, Hidden Class :
JS là prototype-based language: Không có class. Với JS, chúng ta có thể dễ dàng add và remove các property khỏi object
sau khi object đã được khởi tạo (điều này rất khác biệt so với các ngôn ngữ như Java, C#).
Hầu hết các trình biên dịch JS sử dụng dictionary-like structures (hash function based) để lưu trữ vị trí của object property trong bộ nhớ.
Điều này có nghĩa là mỗi property của object khi được lưu trữ trong memory sẽ được mark bằng 1 đoạn hash, điều này sẽ
giúp việc chúng ta add hay remove property của object dễ dàng. Nhưng điều này sẽ ảnh hưởng đến việc khi chúng ta muốn get value của property đó, JS sẽ tra quyển từ điển chứa list các hash key đó để tìm ra value
của propety giống như việc chúng ta tra từ điển tiếng anh vậy.
Điều này sẽ làm cho việc truy xuất giá trị của property của object trong JS sẽ tốn kém hơn so với
các ngôn ngữ Java và C#. Vì các object trong Java, C# không thêm hay add các property sau khi đã khởi tạo nên
các property sẽ có offset cố định.
Để improve quá trình này, V8 đã sử dụng 1 phương pháp khác thay thế
đó là hidden class. Hidden class đơn giản là copy cách làm việc
với Object của java sang JS. Tức là trong thời gian compile code JS
sang machine code, V8 sẽ tạo những object ẩn , cố định và không thể
dynamic thêm bớt property.
Ví dụ
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2)
Trong ví dụ trên V8 sẽ tạo 1 hidden class gọi là C0 tương ứng với Object Point.
- Line 1, khi mới declare function Point, tương ứng object C0 sẽ được tạo và trống.
- Line 2, chúng ta define property x cho object Point. V8 sẽ tạo 1 hidden class thứ 2 gọi là C1 , C1 được tạo dựa theo C0.
C1 sẽ có vị trí trên memory nơi mà property có thể tìm thấy
(liên quan đến object pointer) . Trong trường hợp này x được lưu trữ
ở offset 0, điều này có nghĩa là khi view object point trong object
thì vị trí đầu tiên là x. V8 sẽ mark V0 là một "class transition" ,
Lúc này hidden class đã được switch từ C0 sang C1
Quá trình này sẽ lặp lại tương tư khi line "this.y = y" được executed. Và sau quá trình này hidden class sẽ được update bằng C2.
Việc chuyển tiếp hidden class từ C0 sang C1 sang C2 phụ thuộc vào properties được add vào object.
Chúng ta sẽ xem 1 đoạn ví dụ việc add property không theo 1 thứ tự vào các object sẽ ảnh hưởng đến performance.
VD:
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;
var p2 = new Point(3, 4);
p2.b = 7;
p2.a = 8;
sau khi p1, p2 được gán cho 2 object Point mới được khởi tạo,
thì ngay tại lúc này p1, p2 sẽ cùng có 1 hidden class, nhưng tiếp theo
khi p1 được gán property a, p2 được gán property b thì đến khi kết
thúc quá trình excute thì p1 p2 sẽ được gán cho 2 hidden class khác nhau
Vì vậy trong những trường hợp như này, sẽ là tốt hơn nếu các dynamic
property được khởi tạo cùng 1 thứ tự như vậy thì hidden class sẽ được
tái sử dụng.
2, Inline Caching
Đây là cơ chế khi JS engine nhận thấy một function được dùng lại nhiều lần với cùng 1 loại object được truyền vào param.
V8 sẽ cache lại hidden class của object đó. Điều này sẽ giúp bỏ qua trình được quá V8 xử lí để tìm cách access vào object's
properties và thay vào đó là sử dụng thông tin đã được lưu trữ từ những quá trình xử lí trước đối với object được truyền
vào param.
Vậy hidden class và inline caching liên quan với nhau như thế nào? Khi một method được gọi
với một object đặc biệt, V8 engine sẽ phải thực hiện quá trình tìm kiếm đến một hidden class
của object đó nhằm xác định offset để truy cập vào 1 property của object. Sau hai lần gọi thành
công đến cùng một method của cùng một hidden class. V8 sẽ bỏ qua quá trình tìm tìm kiếm này
mà đơn giản là add offset của property này đến object pointer của chính nó. Và các lần gọi
đến method này sau này , V8 sẽ cho rằng hidden class không thay đổi và sẽ nhảy trực tiếp đến
memory sử dụng offset đã được lưu trữ từ lần tìm kiếm trước đó.
Inline caching cũng nói lên tầm quan trọng của việc sử dụng các object share hidden class.
Nếu chúng ta tạo hai object cùng loại nhưng khác kiểu hidden class, tức là add các property không theo
một thứ tự nhất định (như ví dụ trong hidden class) thì chúng ta sẽ không thể sử dụng inline caching.
TLDR;
1, Methods : Nên sử dung lại một method nhiều lần sẽ tối ưu hết rất nhiều với việc sử dụng
một method 1 lần. Vì thế hãy cố gắng chia nhỏ các function, mỗi function nên chỉ có 1 chức
năng duy nhất (Pure function) để có thể re-use dễ dàng. tips này dựa theo tính
năng Inline Caching của V8 engine.
2, Arrays : Tránh sử dụng các sparse array. Sparse Array là các mảng mà các element bên
trong nó có key không phải nó các số incremental .
Ví dụ
const a = [1, , 3, 4, 5];
const b = new Array(5);
Việc sử dụng mảng dạng này sẽ tốn hiệu năng để access các element trong nó.
Tránh khởi tạo trước các mảng lớn, hãy cứ để nó grow tự nhiên.
Không xóa element trong mảng bởi vì nó sẽ biến mảng thành Sparse Array. Thay vào đó hãy
dùng Array.filter.
3, Inlining : Không nên sử dụng các function chỉ được gọi 1 lần. Với những case đó nên
thay thế line mà gọi function đó thành body của function đó
4, Thứ tự của object dynamic properties khi khởi tạo 1 object:
Khi khởi tạo 1 object nhiều lần hãy cố gắng sắp xếp thứ tự của các property cùng 1 thứ tự
, việc này sẽ giúp các hidden class có thể share giữa mỗi lần khởi tạo
giúp tối ưu performance
5, Dynacmic Properties: Nên tránh việc thêm các property vào object sau khi đã khởi tạo chúng.
Như ví dụ ở tip 4, việc sau khi khởi tạo ta add proprety "name", "color" vào Object Dog được gọi là Dynacmic Properties.
==> Thay vào đó hay truyền
tất cả vào param khi khởi tạo 1 object. Bởi vì các Dynamic properties sẽ làm thay đổi và làm
chậm các methods đã được optimized bởi hidden class.
Reference
https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e