2015-07-31 17 views
5

Mọi người cố gắng sử dụng khuôn khổ JMH để tạo một số xét nghiệm có ý nghĩa sẽ thấy các xét nghiệm mẫu JMH (http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/). Khi chúng tôi đi qua chúng, chúng tôi bị mắc kẹt bởi sự truy quét mã chết (JMHSample_08_DeadCode.java).Làm thế nào để loại bỏ mã chết Math.log() hoạt động trong mẫu JMH

Trích:

private double x = Math.PI; 

@Benchmark 
public void baseline() { 
// do nothing, this is a baseline 
} 

@Benchmark 
public void measureWrong() { 
// This is wrong: result is not used, and the entire computation is optimized out. 
Math.log(x); 
} 

Việc đo measureWrong() sẽ xấp xỉ như đối với các thử nghiệm cơ bản. Bởi vì giá trị trả về của Math.log() không bao giờ được sử dụng. Do đó trình biên dịch HotSpot sẽ loại bỏ mã chết. Ok, hiểu nhưng làm thế nào trình biên dịch có thể quyết định rằng Math.log() có thể được loại bỏ.

Khi chúng tôi xem xét kỹ thử nghiệm, chúng tôi lưu ý rằng Math.log() là một phương pháp gốc. Và các cuộc gọi bản địa đi xuống hệ điều hành và thực hiện một lib tương ứng. Đúng? Điều này dẫn chúng ta đến giả định rằng các cuộc gọi tự nhiên có thể bị loại bỏ bởi trình biên dịch nếu giá trị trả về của chúng không được sử dụng và chúng không thực hiện các hoạt động io.

Chúng tôi tự hỏi nếu lib nằm ở đâu đó trong hệ điều hành và xử lý cuộc gọi gốc từ thế giới java không cung cấp giá trị trả về nhưng thực hiện các thao tác io (ví dụ: ghi nhật ký). Các hướng dẫn đó có bị xóa hoàn toàn không?

Để chứng minh giả định của chúng tôi, chúng tôi đã xây dựng lại kịch bản bằng một bài kiểm tra JMH đơn giản và một cuộc gọi riêng. Chúng tôi biên soạn ba libs c-có nguồn gốc từ thực hiện:

  1. trở về 42
  2. tham số bổ sung
  3. tạo tập tin rỗng

Như chúng ta gọi chúng là trong một thử nghiệm JMH (tương tự như measureWrong() kiểm tra) không ai trong số họ đã được loại bỏ, thậm chí không phải là một trong đó không thực hiện một hoạt động io. Do kết quả thử nghiệm giả định của chúng tôi không thể được xác nhận. Các cuộc gọi bản địa không thể được tối ưu hóa, nghĩa là Math.log() và các cuộc gọi bản địa tùy chỉnh không có cùng cơ sở. Họ không phải là bản địa bình đẳng. Có lẽ chúng tôi đã phạm sai lầm trong mã lib gốc của chúng tôi và ít nhất là cuộc gọi bản địa của thử nghiệm 1 nên đã được eleminated. Nếu điều này đúng, chúng tôi sẽ chia sẻ mã của chúng tôi với bạn.

Vì vậy, chúng tôi đã tìm kiếm thêm và tìm thấy thuật ngữ Intrinsics nơi mã java sẽ được thay thế bằng mã số tương ứng với mã kiến ​​trúc rất tối ưu. java.lang.Math.log() đang thực hiện nội tại như vậy. Có mối quan hệ nào giữa người bản địa và người bản xứ không? Nếu giả thuyết trên về mối quan hệ giữa người bản xứ và nội tại là hợp lệ thì trình biên dịch sẽ thực hiện các bước sau để loại bỏ cuộc gọi gốc?

  • Tại thời điểm biên dịch, HotSpot kiểm tra sự tồn tại của thực thi nội tại (trong jdk?) Cho Math.log() và thay thế Math.log() bằng mã đó.
  • Sau đó kiểm tra thứ hai xảy ra khi HotSpot xem xét sau giá trị trả lại của một phương thức. Và kết quả này, HotSpot quyết định loại bỏ hoàn toàn Math.log().
+1

Math.log thực chất là một nội tại. Điều này có nghĩa là, nó được biết đến với JIT và được thay thế bằng một bộ hướng dẫn máy định sẵn. Ngoài ra, hướng dẫn bản địa không được loại bỏ bởi jit vì nó không thể chứng minh họ không có tác dụng phụ. Android vẫn có thể coi các phương pháp gốc của nó là nội tại. –

Trả lời

9

Khi chúng tôi xem xét kỹ kiểm tra, chúng tôi lưu ý rằng Math.log() là phương pháp gốc. Và các cuộc gọi bản địa đi xuống hệ điều hành và thực hiện một lib tương ứng. Đúng?

Cuộc gọi tự nhiên không đi tới hệ điều hành, họ đi đến thư viện gốc qua JNI. Điều đó có thể kết thúc lên hệ điều hành, hoặc nó có thể đi vào một số người dùng cung cấp lib. Trong trường hợp của các phương thức nguyên gốc trong JDK, chúng ta cũng có thể mong đợi một số lời gọi gốc được biên dịch thành nội tại.

Điều này dẫn chúng ta đến giả định rằng các cuộc gọi gốc có thể bị loại bỏ bởi trình biên dịch nếu giá trị trả về của chúng không được sử dụng và chúng không thực hiện các hoạt động io.

JVM không nhìn vào các cuộc gọi tự nhiên để xác định loại tác dụng phụ nào mà chúng có thể có hoặc không có. Điều này có nghĩa là các cuộc gọi bản địa thực sự được thực hiện như các cuộc gọi phương thức (ở cấp độ lắp ráp, bạn nhảy vào mã foriegn ở đâu đó, một khung khác trên ngăn xếp, vv). Điều này cũng có nghĩa là JVM không thể loại bỏ chúng hoặc các đầu vào phụ thuộc của chúng.

Cuộc gọi gốc không thể được tối ưu hóa, nghĩa là Math.log() và các cuộc gọi bản địa tùy chỉnh không có cùng cơ sở.

Có.

Có mối quan hệ nào giữa người bản xứ và nội tại không?

Một số phương pháp JDK gốc là nội tại. Nhưng các phương thức JDK bình thường cũng có thể là nội tại. Tập hợp các phương thức nội tại cũng khác nhau từ một JVM đến JVM tiếp theo.

Nếu giả định trên về mối quan hệ giữa người bản xứ và nội tại là hợp lệ thì trình biên dịch sẽ thực hiện các bước sau để loại bỏ cuộc gọi gốc?

Chức năng Math.log được chuyển thành nút đặc biệt trong trình biên dịch C2 IR (biểu diễn trung gian). Nút này có thể được tối ưu hóa bởi vì nó được biết là không có tác dụng phụ và giá trị của nó không bao giờ được sử dụng. Nếu giá trị được sử dụng, JVM sẽ biết phát ra mã máy chuyên dụng cho nút này.

Tóm tắt: Nội tại là các phương án thay thế tối ưu được tích hợp trong trình biên dịch JVM. Chúng có thể được sử dụng để thay thế bất kỳ phương pháp (có nguồn gốc hay cách khác) với:

  1. lắp ráp chuyên mã
  2. chuyên IR
  3. Một JVM nội gọi phương thức
  4. Một sự kết hợp của các bên trên
+0

Cảm ơn bạn đã trả lời chi tiết và hữu ích.Việc thiếu kiến ​​thức về các tác dụng phụ có thể xảy ra trong mã gốc giải thích tại sao cuộc gọi bản địa của chúng tôi không bị loại bỏ. – mwa

Các vấn đề liên quan