2012-06-07 36 views
13

Điểm phát sóng Java có thể tối ưu hóa mã tuần tự rất tốt. Nhưng tôi đoán rằng với sự ra đời của các máy tính đa lõi, thông tin trong thời gian chạy có thể hữu ích để phát hiện các cơ hội song song mã trong thời gian chạy, ví dụ phát hiện pipelining phần mềm có thể trong vòng lặp và những thứ tương tự.JVM có khả năng phát hiện các cơ hội để song song không?

Có bất kỳ công việc thú vị nào được thực hiện về chủ đề này không? Hoặc là nó là một thất bại nghiên cứu hoặc một số vấn đề tạm dừng mà là rất khó để giải quyết?

+1

Thông thường, một ExecutorService hoặc ngã ba/join là một lựa chọn tốt hơn. Do thời gian cần để phân phối nhiệm vụ giữa các chủ đề là cao, rất khó có khả năng một trình biên dịch tự động sẽ tốt hơn mã viết tay. Thông thường đối với các vòng lặp đơn giản, sử dụng nhiều luồng sẽ chậm hơn. –

+0

Rất nhiều trình biên dịch khác làm việc này vào những ngày này, tôi không thấy lý do tại sao một JVM/JIT không nên. –

+0

Từ kinh nghiệm thực tế của tôi, JVM thực sự là khá tốt ở đây. – dagnelies

Trả lời

14

Tôi nghĩ rằng các đảm bảo hiện tại của Java memory model làm cho nó khá khó khăn để làm nhiều, nếu có, tự động song song ở trình biên dịch hoặc cấp VM. Ngôn ngữ Java không có ngữ nghĩa để đảm bảo rằng bất kỳ cấu trúc dữ liệu nào thậm chí có hiệu quả bất biến, hoặc bất kỳ câu lệnh cụ thể nào là thuần túy và không có các tác dụng phụ, do đó trình biên dịch sẽ phải tự động hóa chúng để song song. Một số cơ hội cơ bản sẽ có thể suy ra trong trình biên dịch, nhưng trường hợp chung sẽ được để lại cho thời gian chạy, vì tải động và ràng buộc có thể đưa ra các đột biến mới không tồn tại trong thời gian biên dịch.

Xét đoạn mã sau:

for (int i = 0; i < array.length; i++) { 
    array[i] = expensiveComputation(array[i]); 
} 

Nó sẽ là tầm thường để parallelize, nếu expensiveComputation là một pure function, mà đầu ra chỉ phụ thuộc vào đối số của nó, và nếu chúng ta có thể đảm bảo rằng array sẽ không bị thay đổi trong quá vòng lặp (thực ra chúng ta đang thay đổi nó, thiết lập array[i]=..., nhưng trong trường hợp cụ thể này expensiveComputation(array[i]) luôn được gọi đầu tiên vì vậy nó không sao ở đây - giả sử rằng array là cục bộ và không được tham chiếu từ bất kỳ nơi nào khác).

Hơn nữa, nếu chúng ta thay đổi vòng lặp như thế này:

for (int i = 0; i < array.length; i++) { 
    array[i] = expensiveComputation(array, i); 
    // expensiveComputation has the whole array at its disposal! 
    // It could read or write values anywhere in it! 
} 

sau đó song song là không nhỏ nữa ngay cả khi expensiveComputation là tinh khiết và không làm thay đổi đối số của nó, bởi vì các chủ đề song song sẽ được thay đổi nội dung của array trong khi những người khác đang đọc nội dung đó! Bộ song song sẽ phải tìm ra các bộ phận của mảng expensiveComputation đang đề cập đến trong các điều kiện khác nhau và đồng bộ hóa cho phù hợp.

Có lẽ nó sẽ không phải là hoàn toàn không thể để phát hiện tất cả các đột biến và tác dụng phụ mà có thể xảy ra và có những vào tài khoản khi parallelizing, nhưng nó sẽ là rất cứng, chắc chắn, có lẽ không khả thi trong thực hành. Đây là lý do tại sao song song, và tìm ra rằng tất cả mọi thứ vẫn hoạt động chính xác, là đau đầu của lập trình viên trong Java.

Ngôn ngữ chức năng (ví dụ: Clojure trên JVM) là câu trả lời nóng cho chủ đề này. Các hàm thuần túy, không có tác dụng phụ cùng với cấu trúc dữ liệu persistent ("có thể thay đổi một cách hiệu quả") có khả năng cho phép song song ngầm hoặc ngầm gần như ẩn. Hãy tăng gấp đôi mỗi phần tử của một mảng:

(map #(* 2 %) [1 2 3 4 5]) 
(pmap #(* 2 %) [1 2 3 4 5]) ; The same thing, done in parallel. 

Đây là minh bạch vì 2 điều:

  1. Chức năng #(* 2 %) là tinh khiết: phải mất một giá trị trong và đưa ra một giá trị ra, và đó là nó. Nó không thay đổi bất cứ điều gì, và đầu ra của nó chỉ phụ thuộc vào đối số của nó.
  2. Vectơ [1 2 3 4 5] là không thay đổi: bất kể ai đang xem hoặc khi nào thì cũng giống nhau.

Có thể tạo các hàm thuần túy trong Java, nhưng 2), bất biến, là gót chân Achilles ở đây. Không có mảng bất biến trong Java. Để là hình tròn, không có gì là bất biến trong Java vì thậm chí có thể thay đổi các trường final bằng cách sử dụng phản chiếu. Do đó không có đảm bảo nào có thể được thực hiện rằng đầu ra (hoặc đầu vào!) Của một phép tính sẽ không bị thay đổi bởi sự song song -> vì vậy việc song song tự động thường không khả thi.

Người câm "tăng gấp đôi các yếu tố" dụ kéo dài đến tùy tiện chế biến phức tạp, nhờ vào tính bất biến:

(defn expensivefunction [v x] 
    (/ (reduce * v) x)) 


(let [v [1 2 3 4 5]] 
    (map (partial expensivefunction v) v)) ; pmap would work equally well here! 
+0

Điều này hoàn toàn bỏ qua trường hợp (tương đối phổ biến) của các phép tính hoàn toàn-số học, mà HotSpot có thể song song cũng như bất kỳ ai khác. Có thể không? –

+0

@Louis: Ý của bạn chính xác là gì bởi "tính toán số học thuần túy"? Tôi không biết liệu HotSpot có thực hiện điều đó hay không, nhưng tất cả các bộ vi xử lý hiện đại đều đang thực hiện [dự đoán nhánh] (http://en.wikipedia.org/wiki/Branch_predictor) và [tối ưu hóa đầu cơ] (http: //en.wikipedia. org/wiki/Speculative_execution) vì vậy tôi không chắc liệu HotSpot có thể thêm bất cứ thứ gì vào đó hay không. –

+0

Thay vì gọi các phương thức 'expensiveComputation', các vòng lặp chỉ làm toán trực tiếp trên mảng? –

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