Understanding JIT Compilation and Optimizations

1. Mở đầu

Trong quá trình tìm hiểu về các phương pháp tối ưu, ngoài các phương pháp phổ biến như call-by-name parameters, boxing hay anonymous functions, mình có biết thêm về một vài khái niệm khác cũng khá hay ho như inlining, dead code elimination copy propagation. Thông qua một số paper mình đọc được,  suất có thể cải thiện lên đến 45%, nên trong blog này mình sẽ “translate” từ một nguồn nào đó về các khái niệm bên trên.

2. Phía bên trong “hộp đen”

Theo hướng nhìn nhận của chúng ta từ trước đến nay, công việc của “Oracle JRockit JVM”  là gì ? Đơn thuần là sẽ phiên dịch “source code” sang “machine code”. Ta có thể hình dùng qua hình ảnh bên dưới.

Nhưng sự thực là bên trong “nó” chứa nhiều thứ phức tạp hơn như thế. Thế nên việc mình compile “source code” A sang một “machine code” B có thể sẽ khác rất nhiều với source code ban đầu (tất nhiên định nghĩa về khác là nội dung sẽ khác, chứ không phải do nó đã trở thành 1 đống 00001111). Công việc của JVM bao gồm các hoạt động nhất định, thay đổi cấu trúc dữ liệu và chuyển đổi vài phần source code. Hình mô tả như bên dưới, nếu như ra bỏ cái phần màu đen đen đó thay bằng phần khác có ý nghĩa hơn.

3. Quá trình “Compile Code”

Đầu tiên, trình biên dịch JIT sẽ biên dịch và chạy phần code chưa được tối ưu đó. Mặc dù JIT không phải là một thành phần của JVM nhưng nó sẽ được gọi ra bất cứ khi nào một method trong Java được gọi (về lý thuyết là thế), và nó sẽ biên dịch các bytecode của method đó sang “machine code”. Sau quá trình đó trình đó, JVM sẽ trực tiếp gọi đến những method đã được biên dịch mà không cần thông qua trình biên dịch nữa, điều đó làm cho các ứng dụng Java nhanh hơn.

Tiếp theo nó sẽ tiếp tục sử dụng một số phương pháp phức tạp để đo lường và tìm ra những method được sử dụng nhiều dựa trên lịch sử chạy và dựa trên các “mẫu thử” thông qua các luồng chạy. Những phương thức này được đánh dấu là “hot”, và nó cần phải được tối ưu.

Cuối cùng JVM sẽ chạy các method đã được tối ưu ở. Việc tối ưu sẽ được chạy ở “background”.

4. Ví dụ mô tả

Hãy xem qua một đoạn code ví dụ như sau: (Nhìn thì có vẻ ngớ ngẩn nhưng làm ví dụ thì ok)

class A {
  val b: B = new B()
  val PI = 3.14
  
  def getArea(): Double = {
    var r1 = b.value()
    var r2 = b.value()
    val a = PI * r1 * r2
    a
  }
}

class B {
  val r = 123456L
  final def value(): Long = r
}

stage 1: Không có gì khác biệt với method getArea()

def getArea(): Double = {
  var r1 = b.value()
  var r2 = b.value()
  val a = PI * r1 * r2
  a
}

stage 2: Với tính năng Inlining (Trong tính toán, inlining là một kiểu biên dịch tối ưu thực hiện bằng cách thay thế lời gọi hàm bằng nội dung bên trong của hàm được gọi). Tuy nhiên liên quan đến Inline, không phải cứ nhiều sẽ tốt, và method kiểu nào sẽ được inline, làm cách nào để tìm được “hot” method, đó sẽ là 1 chủ đề khác. Hiện tại ta chỉ quan tâm đến việc method getArea() hiện tại sẽ được tối ưu như thế nào tại stage này.

def getArea(): Double = {
  var r1 = b.r // Các lời gọi hàm đã được thay bằng nội dung của hàm đó
  var r2 = b.r
  val a = PI * r1 * r2
  a
}

stage 3: Ở stage này, sẽ thực hiện việc loại bỏ những lần load dữ liệu không cần thiết, thay thế vào đó là truy cập vào những biến cục bộ

def getArea(): Double = {
  var r1 = b.r
  var r2 = r1 // Việc gọi  lại b.r là không cần thiết nữa vì r1 = b.r
  val a = PI * r1 * r2
  a
}

stage 4: Ở stage này, sẽ thực hiện việc copy ngược với những variable thừa thãi thì ta có thể có được giá trị đó khi dùng variable r2

def getArea(): Double = {
  var r1 = b.r
  r1 = r1 // Việc dùng r2 là không cần thiết nữa vì r2 = r1
  val a = PI * r1 * r1
  a
}

stage 5: Ở stage này, sẽ thực hiện Eliminate dead code. Ta thấy rằng đoạn code r1 = r1 là vô nghĩa , cần bị loại bỏ, đoạn code tối ưu cuối cùng

def getArea(): Double = {
  var r1 = b.r
  val a = PI * r1 * r1
  a
}

5. Kết luận

Theo những gì mình tìm hiểu được thì máy ảo JRockit chỉ support cho tới Java 6, và hiện tại  là HotSpot cho version 7 trở lên đồng thời một vài tính năng đã được phát triển thêm. Việc JVM thực hiện có thể sẽ khác nhau ở cách JIT biên dịch, tối ưu, trình thu dọn rác, nền tảng support cũng như phiên bản support cho Java. Thậm chí đối với những open-source JVM cũng sẽ khác nhau đôi chút. Đôi khi việc mình tìm hiểu thêm phía bên dưới hoạt động như thế nào sẽ giúp mình tối ưu phần việc mà đáng nhé mình có thể nhận thức để tối ưu, hoặc đôi khi cũng là đọc để cho “vui”. Những phần mình tìm hiểu được chỉ là một phần nhỏ, những link tham khảo mình sẽ để bên dưới cho những ai cần.

6. Tài liệu tham khảo

https://www.infoq.com/articles/inline-classes-java/

https://advancedweb.hu/profile-based-optimization-techniques-in-the-jvm/

https://en.wikipedia.org/wiki/CPU_cache#ICACHE

https://en.wikipedia.org/wiki/Inline_expansion

https://ssw.jku.at/Research/Papers/Wimmer08PhD/Wimmer08PhD.pdf

https://docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/diagnos/underst_jit.html

https://www.scala-lang.org/old/sites/default/files/linuxsoft_archives/docu/files/ScalaByExample.pdf

 

Add a Comment

Scroll Up