2016-09-27 20 views
5

Tại vJUG24, một trong các chủ đề là JVM performance.Tại sao một phương pháp có thể sử dụng phương pháp varargs được tối ưu hóa thành một chuỗi các cuộc gọi đơn hình chỉ khi nó là tĩnh?

Trang trình bày có thể được tìm thấy here.

Ông có một ví dụ:

static void log(Object... args) { 
    for(Object arg : args) { 
     System.out.println(arg); 
    } 
} 

được gọi thông qua (không thể khá đọc slide đúng, nhưng nó tương tự):

void doSomething() { 
    log("foo", 4, new Object()); 
} 

Ông nói bởi vì nó là một phương pháp tĩnh , nó có thể được tối ưu hóa bằng cách đặt nội tuyến như sau:

void doSomething() { 
    System.out.println("foo"); 
    System.out.println(new Integer(4).toString()); 
    System.out.println(new Object().toString()); 
} 

Tại sao điều quan trọng là phương thức đăng nhập là tatic cho JVM để thực hiện tối ưu hóa này?

+1

Bạn muốn chắc chắn rằng phương pháp này không bị ghi đè nếu nó không phải là 'tĩnh'? (và nó không phải là 'private' hoặc' final' hoặc là) – Tom

+0

@Tom: JVM * biết * liệu một phương thức có bị ghi đè hay không. Nếu biết mọi lớp được nạp. – Holger

Trả lời

5

Bản trình bày không hoàn toàn chính xác hoặc bạn không hiểu đúng.

Thực tế, JVM có thể các phương thức không tĩnh nội tuyến, ngay cả với varargs. Hơn nữa, nó có thể loại bỏ phân bổ các mảng Object[] tương ứng trong một số trường hợp nhất định. Thật không may, nó không làm điều này khi một phương pháp vararg lặp qua mảng bằng cách sử dụng vòng lặp for.

Tôi đã thực hiện tiêu chuẩn JMH sau để xác minh lý thuyết và chạy nó với GC profiler (-prof gc).

package bench; 

import org.openjdk.jmh.annotations.Benchmark; 
import org.openjdk.jmh.infra.Blackhole; 

public class VarArgs { 

    @Benchmark 
    public void inlineNonStatic(Blackhole bh) { 
     inlineNonStaticVA(bh, "foo", 4, new Object()); 
    } 

    @Benchmark 
    public void inlineStatic(Blackhole bh) { 
     inlineStaticVA(bh, "foo", 4, new Object()); 
    } 

    @Benchmark 
    public void loopNonStatic(Blackhole bh) { 
     loopNonStaticVA(bh, "foo", 4, new Object()); 
    } 

    @Benchmark 
    public void loopStatic(Blackhole bh) { 
     loopStaticVA(bh, "foo", 4, new Object()); 
    } 

    public void inlineNonStaticVA(Blackhole bh, Object... args) { 
     if (args.length > 0) bh.consume(args[0]); 
     if (args.length > 1) bh.consume(args[1]); 
     if (args.length > 2) bh.consume(args[2]); 
     if (args.length > 3) bh.consume(args[3]); 
    } 

    public static void inlineStaticVA(Blackhole bh, Object... args) { 
     if (args.length > 0) bh.consume(args[0]); 
     if (args.length > 1) bh.consume(args[1]); 
     if (args.length > 2) bh.consume(args[2]); 
     if (args.length > 3) bh.consume(args[3]); 
    } 

    public void loopNonStaticVA(Blackhole bh, Object... args) { 
     for (Object arg : args) { 
      bh.consume(arg); 
     } 
    } 

    public static void loopStaticVA(Blackhole bh, Object... args) { 
     for (Object arg : args) { 
      bh.consume(arg); 
     } 
    } 
} 

-XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining cho thấy rằng tất cả 4 phiên bản đều được sắp xếp thành công vào người gọi:

@ 28 bench.VarArgs::inlineNonStaticVA (52 bytes) inline (hot) 
    @ 27 bench.VarArgs::inlineStaticVA (52 bytes) inline (hot) 
    @ 28 bench.VarArgs::loopNonStaticVA (35 bytes) inline (hot) 
    @ 27 bench.VarArgs::loopStaticVA (33 bytes) inline (hot) 

Kết quả xác nhận rằng không có sự khác biệt về hiệu năng giữa gọi tĩnh so với các phương pháp không tĩnh.

Benchmark    Mode Cnt Score Error Units 
VarArgs.inlineNonStatic avgt 20 9,606 ± 0,076 ns/op 
VarArgs.inlineStatic  avgt 20 9,604 ± 0,040 ns/op 
VarArgs.loopNonStatic avgt 20 14,188 ± 0,154 ns/op 
VarArgs.loopStatic  avgt 20 14,147 ± 0,059 ns/op 

Tuy nhiên, GC profiler chỉ ra rằng vararg Object[] mảng được phân bổ cho loop* phương pháp, nhưng không phải cho inline* phương pháp.

Benchmark         Mode Cnt  Score  Error Units 
VarArgs.inlineNonStatic:·gc.alloc.rate.norm avgt 20 16,000 ± 0,001 B/op 
VarArgs.inlineStatic:·gc.alloc.rate.norm  avgt 20 16,000 ± 0,001 B/op 
VarArgs.loopNonStatic:·gc.alloc.rate.norm avgt 20 48,000 ± 0,001 B/op 
VarArgs.loopStatic:·gc.alloc.rate.norm  avgt 20 48,000 ± 0,001 B/op 

Tôi đoán, điểm ban đầu là các phương pháp tĩnh luôn là đơn hình. Tuy nhiên, JVM cũng có thể phương thức đa hình nội tuyến nếu không có quá nhiều người nhận thực tế trong trang web cuộc gọi cụ thể.

+1

Tôi vừa kiểm tra bản trình bày; không có lúc nào nó nói "bởi vì nó là một phương pháp tĩnh", trên thực tế, nó thậm chí còn mô tả khả năng của JVM về việc phác thảo các phương thức không phải 'tĩnh 'ngay trước ví dụ về câu hỏi của OP. – Holger

+0

À, chắc hẳn là tôi, người đã nhầm lẫn với người trình bày. – Fodder

+0

Bạn có thể giải thích ý bạn bằng cách loại bỏ sự phân bổ của Object []? Điều đó có nghĩa là JVM tối ưu hóa bằng cách thay đổi chữ ký phương thức thành 'log (String, Integer, Object)', bởi vì nó biết đó là những gì đang được gọi? – Fodder

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