Các trình biên dịch như Javac có tự động phát hiện các hàm thuần túy và song song hóa chúng không?


12

Các chức năng thuần túy được biết là tạo điều kiện thuận lợi cho parellelizing. Điều gì về lập trình chức năng làm cho nó vốn thích nghi với thực thi song song?

Các trình biên dịch như Javac có đủ thông minh để phát hiện khi một phương thức là một hàm thuần túy không? Người ta luôn có thể thực hiện các lớp thực hiện các giao diện chức năng như Hàm , nhưng có tác dụng phụ.


7
Câu hỏi không chỉ là liệu trình biên dịch có thể biết liệu một hàm có thuần túy hay không mà còn có thể lên lịch trình thực hiện song song các hàm thuần túy một cách thông minh hay không. Nó không đủ để bắn ra một chủ đề mới cho mỗi người: điều này không hiệu quả. GHC (Haskell) giải quyết vấn đề này bằng cách sử dụng sự lười biếng và "chủ đề xanh"; Tôi thực sự sẽ ngạc nhiên nếu bất kỳ ngôn ngữ không tinh khiết nào thậm chí đã thử, do khó khăn hơn trong việc đảm bảo các luồng thuần túy được lên lịch chính xác đối với luồng không tinh khiết chính.
Ryan Reich

@RyanReich, có bất kỳ hiệu suất nào khi sử dụng lập trình chức năng trong một ngôn ngữ chức năng không tinh khiết như Java không? lợi ích của lập trình chức năng hoàn toàn là chức năng như mô đun?
Naveen

@RyanReich GHC giải quyết vấn đề bằng cách chú thích lập trình viên khi họ muốn song song. Độ tinh khiết ngụ ý rằng các chú thích này không bao giờ thay đổi ngữ nghĩa, chỉ là hiệu suất. (Cũng có những cơ chế đồng thời có thể làm phát sinh song song, nhưng đây là một ấm cá khác.)
Derek Elkins rời SE

@Naveen Có những lợi ích khác đối với các hàm thuần túy liên quan đến tối ưu hóa bên cạnh tính song song như mã sắp xếp lại tự do lớn hơn, ghi nhớ và loại bỏ phổ biến phụ. Tôi có thể sai, nhưng tôi nghi ngờ javac cố gắng phát hiện độ tinh khiết, vì có lẽ nó khá hiếm trong mã thành ngữ và hơi khó đối với tất cả các trường hợp ngoại trừ những trường hợp tầm thường nhất. Ví dụ, bạn cần biết rằng sẽ không có bất kỳ NullPointerExceptions. Lợi ích của việc tối ưu hóa dựa trên điều này có lẽ cũng khá nhỏ đối với các ứng dụng Java điển hình.
Derek Elkins rời SE

6
javac là trình biên dịch java, lấy mã nguồn java và tạo các tệp lớp mã byte java. Nó khá hạn chế về những gì nó có thể (và được cho là) ​​làm. Nó không có quyền tự do hoặc các cơ chế cơ bản cần thiết để đưa tính song song vào tệp lớp mã byte.
Erik Eidt

Câu trả lời:


33

là các trình biên dịch như Javac đủ thông minh để phát hiện khi một phương thức là một hàm thuần túy.

Đó không phải là một câu hỏi "đủ thông minh". Cái này được gọi là Phân tích độ tinh khiết và có thể chứng minh là không thể trong trường hợp chung: nó tương đương với việc giải quyết vấn đề dừng.

Bây giờ, tất nhiên, tối ưu hóa làm những điều không thể chứng minh mọi lúc, "có thể chứng minh là không thể trong trường hợp chung" không có nghĩa là nó không bao giờ hoạt động, nó chỉ có nghĩa là nó không thể hoạt động trong mọi trường hợp. Vì vậy, trên thực tế, có các thuật toán để kiểm tra xem một hàm có thuần hay không, chỉ là kết quả sẽ không thường xuyên hơn là "Tôi không biết", điều đó có nghĩa là vì lý do an toàn và chính xác, bạn cần phải giả sử rằng chức năng đặc biệt này có thể không tinh khiết.

Và ngay cả trong trường hợp nó làm làm việc, các thuật toán rất phức tạp và tốn kém.

Vì vậy, đó là vấn đề # 1: nó chỉ hoạt động cho các trường hợp đặc biệt .

Vấn đề # 2: Thư viện . Để một hàm được thuần túy, nó chỉ có thể gọi các hàm thuần túy (và các hàm đó chỉ có thể gọi các hàm thuần túy, v.v.). Javac rõ ràng chỉ biết về Java và nó chỉ biết về mã mà nó có thể nhìn thấy. Vì vậy, nếu hàm của bạn gọi một hàm trong một đơn vị biên dịch khác, bạn không thể biết liệu nó có thuần túy hay không. Nếu nó gọi một chức năng được viết bằng ngôn ngữ khác, bạn không thể biết. Nếu nó gọi một chức năng trong thư viện thậm chí chưa được cài đặt, bạn không thể biết. Và như thế.

Điều này chỉ hoạt động, khi bạn có phân tích toàn bộ chương trình, khi toàn bộ chương trình được viết bằng cùng một ngôn ngữ và tất cả được biên dịch cùng một lúc. Bạn không thể sử dụng bất kỳ thư viện.

Vấn đề # 3: Lập kế hoạch . Một khi bạn đã tìm ra phần nào là thuần túy, bạn vẫn phải sắp xếp chúng để tách các chủ đề. Hay không. Bắt đầu và dừng các chủ đề rất tốn kém (đặc biệt là trong Java). Ngay cả khi bạn giữ một nhóm luồng và không bắt đầu hoặc dừng chúng, chuyển đổi ngữ cảnh luồng cũng tốn kém. Bạn cần chắc chắn rằng tính toán sẽ chạy lâu hơn đáng kể so với thời gian cần thiết để lên lịch và chuyển đổi ngữ cảnh, nếu không bạn sẽ mất hiệu suất, không đạt được nó.

Như bạn có thể đoán được bây giờ, việc tính toán sẽ mất bao lâu để tính toán trong trường hợp chung (chúng ta thậm chí không thể biết liệu nó có mất một khoảng thời gian hữu hạn hay không, kể cả bao nhiêu thời gian) và khó khăn và tốn kém ngay cả trong trường hợp đặc biệt

Ngoài ra: Javac và tối ưu hóa . Lưu ý rằng hầu hết các triển khai javac không thực sự thực hiện nhiều tối ưu hóa. Ví dụ, việc triển khai javac của Oracle dựa vào công cụ thực thi cơ bản để thực hiện tối ưu hóa . Điều này dẫn đến một loạt vấn đề khác: giả sử, javac đã quyết định rằng một hàm cụ thể là thuần túy và nó đủ đắt, và do đó, nó biên dịch nó để được thực thi trên một luồng khác. Sau đó, trình tối ưu hóa của nền tảng (ví dụ: trình biên dịch JIT HotSpot C2) xuất hiện và tối ưu hóa toàn bộ chức năng. Bây giờ, bạn có một chủ đề trống không làm gì. Hoặc, hãy tưởng tượng, một lần nữa, javac quyết định lên lịch một chức năng trên một luồng khác và trình tối ưu hóa nền tảng có thể tối ưu hóa nó hoàn toàn, ngoại trừ nó không thể thực hiện nội tuyến trên các ranh giới luồng, và do đó, một chức năng có thể được tối ưu hóa hoàn toàn hiện đang được thực hiện một cách không cần thiết.

Vì vậy, làm một cái gì đó như thế này chỉ thực sự có ý nghĩa nếu bạn có một trình biên dịch duy nhất thực hiện hầu hết các tối ưu hóa trong một lần, để trình biên dịch biết và có thể khai thác tất cả các tối ưu hóa khác nhau ở các cấp độ khác nhau và tương tác của chúng với nhau.

Lưu ý rằng, ví dụ, trình biên dịch JIT HotSpot C2 thực sự không thực hiện một số tính năng tự động vector hóa, mà còn là một hình thức tự động song song.


Vâng, tùy thuộc vào định nghĩa của bạn về "chức năng thuần túy", sử dụng các chức năng không tinh khiết trong việc thực hiện có thể được cho phép.
Ded repeatator

@Deduplicator Vâng, tùy thuộc vào định nghĩa của bạn definition, bằng cách sử dụng khác nhau definitioncủa puritycó lẽ là tối nghĩa
con mèo

1
Vấn đề # 2 của bạn hầu hết bị vô hiệu bởi thực tế là tất cả các tối ưu hóa đều được thực thi bởi JIT (bạn rõ ràng biết điều đó, nhưng bỏ qua nó). Vấn đề tương tự # 3 bị vô hiệu một phần vì JIT phụ thuộc rất nhiều vào số liệu thống kê được thu thập bởi người phiên dịch. Tôi đặc biệt không đồng ý với "Bạn không thể sử dụng bất kỳ thư viện nào" khi có sự giải thích cho việc giải cứu. Tôi đồng ý rằng sự phức tạp thêm vào sẽ là một vấn đề.
maaartinus

2
@maaartinus: Bên cạnh đó, chỉ phần cuối câu trả lời của tôi là dành riêng cho javac. Tôi đặc biệt làm đề cập đến, ví dụ, rằng: "Đây chỉ hoạt động, khi bạn có phân tích toàn bộ chương trình, khi toàn bộ chương trình được viết bằng ngôn ngữ giống nhau, và tất cả được biên dịch cùng một lúc trong một đi." Điều này rõ ràng đúng với C2: nó chỉ giao dịch với một ngôn ngữ (mã byte JVM) và nó có quyền truy cập vào toàn bộ chương trình cùng một lúc.
Jörg W Mittag

1
@ JörgWMittag Tôi biết rằng OP hỏi về javac, nhưng tôi cá là họ cho rằng javac là thứ chịu trách nhiệm cho việc tối ưu hóa. Và họ hầu như không biết rằng có C2. Tôi không nói, câu trả lời của bạn là xấu. Chỉ là để javac thực hiện bất kỳ tối ưu hóa nào (ngoại trừ việc tầm thường như sử dụng StringBuilder) là không có ý nghĩa, vì vậy tôi bỏ qua nó và đơn giản giả sử, OP viết javac nhưng có nghĩa là Hotspot. Vấn đề # 2 của bạn là một lý do khá chính đáng để tối ưu hóa mọi thứ trong javac.
maaartinus

5

Câu trả lời nâng cao không thành công để lưu ý một điều. Giao tiếp đồng bộ giữa các chủ đề là vô cùng tốn kém. Nếu chức năng có khả năng được thực thi với tốc độ nhiều triệu cuộc gọi mỗi giây, thì nó thực sự gây tổn hại cho bạn nhiều hơn khi song song hóa nó chứ không phải để nguyên như vậy.

Hình thức nhanh nhất của giao tiếp liên luồng đồng bộ, sử dụng các vòng lặp bận rộn với các biến nguyên tử, không may là không hiệu quả về năng lượng. Nếu bạn phải sử dụng các biến điều kiện để tiết kiệm năng lượng, hiệu suất của giao tiếp liên luồng của bạn bị ảnh hưởng.

Vì vậy, trình biên dịch không chỉ cần xác định xem một hàm có thuần túy hay không, nó cũng cần ước tính thời gian thực hiện của hàm để xem liệu song song hóa có phải là một chiến thắng thuần hay không. Ngoài ra, nó sẽ cần phải chọn giữa các vòng lặp bận rộn bằng cách sử dụng các biến nguyên tử hoặc biến điều kiện. Và nó sẽ cần phải tạo ra các chủ đề phía sau lưng của bạn.

Nếu bạn tạo các chủ đề một cách linh hoạt, nó thậm chí còn chậm hơn so với việc sử dụng các biến điều kiện. Vì vậy, trình biên dịch sẽ cần thiết lập một số luồng nhất định đang chạy.

Vì vậy, câu trả lời cho câu hỏi của bạn là không , trình biên dịch không đủ "thông minh" để tự động song song hóa các hàm thuần túy, đặc biệt là trong thế giới Java. Họ thông minh bằng cách không tự động song song chúng!


5
" Họ thông minh bằng cách không tự động song song hóa chúng! " : Điều này đi quá xa. Mặc dù đúng là song song tại mọi điểm có thể chỉ vì mục đích riêng của nó nói chung sẽ không hiệu quả, một trình biên dịch thông minh sẽ xác định một chiến lược song song thực tế. Tôi nghĩ rằng hầu hết mọi người đều hiểu điều này, vì vậy khi chúng ta nói về tự động song song hóa, chúng ta có nghĩa là tự động hóa song song.
Nat

@Nat: Vô lý quá khó. Điều này sẽ yêu cầu xác định các hàm thuần túy trên thang thời gian chạy là 100 mili giây và hy vọng trình biên dịch sẽ có bất kỳ ý tưởng nào về thời gian chạy của các vòng lặp không có hằng số trong các lần lặp của chúng (và các trường hợp bạn muốn không) là điều ngớ ngẩn.
Joshua

Tôi đồng ý - Nhận xét của @ Nat ngụ ý rằng song song hóa không nhất thiết có nghĩa là nhiều luồng, điều này đúng. Ví dụ, JIT có thể thực hiện nhiều cuộc gọi đến một hàm thuần túy và xen kẽ các hướng dẫn CPU của chúng trong một số trường hợp nhất định. Ví dụ, nếu cả hai phương thức gọi tìm nạp một hằng số, nó có thể được tìm nạp một lần và được giữ trong một thanh ghi CPU cho cả hai phiên bản của phương thức sử dụng. CPU hiện đại là những quái thú với nhiều thanh ghi mục đích chung và các hướng dẫn chuyên biệt có thể khá hữu ích khi tối ưu hóa mã.

1
@Joshua: Thực sự dễ dàng hơn nhiều cho trình biên dịch JIT. Trình biên dịch JIT cũng có thể chỉ ra rằng một hàm có thể không thuần túy, nhưng cho đến nay không có lệnh gọi nào gọi ra hành vi không tinh khiết.
gnasher729

Tôi đồng ý với @Joshua. Tôi có một thuật toán khó song song trong công việc. Tôi đã cố gắng tự song song hóa nó, thậm chí bằng cách thực hiện một số phép tính xấp xỉ đơn giản (và do đó sửa đổi thuật toán), và đã thất bại thảm hại mỗi lần. Ngay cả một chương trình cho biết liệu có khả thi song song một thứ gì đó hay không là vô cùng khó khăn, mặc dù nó sẽ đơn giản hơn nhiều so với thực tế song song hóa nó. Hãy nhớ rằng chúng ta đang nói về Turing - hoàn thành các ngôn ngữ lập trình.
juhist
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.