Mã dưới đây gọi hai hàm đơn giản 10 tỷ lần mỗi đối số.Tại sao truyền hai đối số chuỗi hiệu quả hơn một đối số danh sách
public class PerfTest {
private static long l = 0;
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b");
long time1 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func1("a", "b");
}
long time2 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func2(list);
}
System.out.println((time2 - time1) + "/" + (System.currentTimeMillis() - time2));
}
private static void func1(String s1, String s2) { l++; }
private static void func2(List<String> sl) { l++; }
}
Giả định của tôi là hiệu suất của hai cuộc gọi này sẽ gần giống nhau. Nếu bất cứ điều gì tôi đã có thể đoán rằng đi qua hai đối số sẽ hơi chậm hơn so với đi qua một. Cho tất cả các đối số là tham chiếu đối tượng Tôi đã không mong đợi một thực tế rằng một trong những là một danh sách để làm cho bất kỳ sự khác biệt.
Tôi đã chạy thử nghiệm nhiều lần và kết quả điển hình là "12781/30536". Nói cách khác, cuộc gọi sử dụng hai chuỗi mất 13 giây và cuộc gọi bằng danh sách mất 30 giây.
Giải thích cho sự khác biệt này về hiệu suất là gì? Hay đây là một thử thách không công bằng? Tôi đã cố gắng chuyển đổi hai cuộc gọi (trong trường hợp đó là do hiệu ứng khởi động) nhưng kết quả là như nhau.
Cập nhật
Đây không phải là một thử nghiệm công bằng vì nhiều lý do. Tuy nhiên nó thể hiện hành vi thực sự của trình biên dịch Java. Lưu ý hai bổ sung sau đây để chứng minh điều này:
- Thêm biểu
s1.getClass()
vàsl.getClass()
vào các chức năng làm cho hai chức năng cuộc gọi phí phạm cùng - Chạy thử nghiệm với
-XX:-TieredCompilation
cũng làm cho hai chức năng cuộc gọi thực hiện cùng
Giải thích cho hành vi này nằm trong câu trả lời được chấp nhận bên dưới. Bản tóm tắt ngắn gọn về câu trả lời của @ apangin là func2
không được trình biên dịch điểm nóng nêu rõ vì lớp đối số của nó (tức là List
) không được giải quyết. Buộc phân giải lớp học (ví dụ: sử dụng getClass
) làm cho nội dung được inlined giúp cải thiện đáng kể hiệu suất của lớp. Như đã chỉ ra trong câu trả lời, các lớp chưa được giải quyết có thể không xảy ra trong mã thực mà làm cho mã này trở thành một trường hợp không thực tế.
Bạn có thể thêm những gì bạn mong đợi và tại sao không? – ChiefTwoPencils
@ChiefTwoPencils đã thêm đoạn mã vào đó. – sprinter
Tôi không bỏ phiếu để đóng, nhưng trừ khi ai đó sẵn sàng hack ngoài thời gian chạy để xem xét tối ưu hóa biên dịch cụ thể, hầu hết các câu hỏi hiệu suất không thực sự hữu ích (mặc dù chúng có thể thú vị/thú vị) - và câu trả lời có thể thay đổi từ bản phát hành sang bản phát hành. Trong trường hợp này, tôi chỉ giả định rằng JVM tìm thấy dễ dàng hơn để biên dịch hoặc ghi nhớ hai tham số cuộc gọi hơn là cuộc gọi mảng, nhưng nghiêm túc - chỉ cần viết bất cứ điều gì là dễ đọc nhất! Cũng lưu ý, phiên bản dễ đọc nhất thường là phiên bản mà JVM tối ưu hóa tốt nhất. –