JVM có ngăn chặn tối ưu hóa cuộc gọi đuôi không?


99

Tôi đã thấy câu trích dẫn này về câu hỏi: Ngôn ngữ chức năng tốt để xây dựng dịch vụ web là gì?

Đặc biệt, Scala không hỗ trợ loại bỏ lệnh gọi đuôi ngoại trừ trong các hàm tự đệ quy, điều này giới hạn các loại thành phần bạn có thể thực hiện (đây là hạn chế cơ bản của JVM).

Điều này có đúng không? Nếu vậy, điều gì ở JVM đã tạo ra hạn chế cơ bản này?

Câu trả lời:


74

Bài đăng này: Đệ quy hay Lặp lại? có thể giúp.

Nói tóm lại, việc tối ưu hóa cuộc gọi đuôi là khó thực hiện trong JVM vì mô hình bảo mật và nhu cầu luôn có sẵn dấu vết ngăn xếp. Những yêu cầu này về lý thuyết có thể được hỗ trợ, nhưng nó có thể sẽ yêu cầu một mã bytecode mới (xem đề xuất không chính thức của John Rose ).

Cũng có nhiều cuộc thảo luận hơn trong Sun bug # 4726340 , nơi kết thúc đánh giá (từ năm 2002):

Tôi tin rằng điều này có thể được thực hiện, nhưng nó không phải là một nhiệm vụ nhỏ.

Hiện tại, có một số công việc đang diễn ra trong dự án Da Vinci Machine . Trạng thái của tiểu dự án cuộc gọi đuôi được liệt kê là "proto 80%"; nó không có khả năng đưa nó vào Java 7, nhưng tôi nghĩ nó có cơ hội rất tốt ở Java 8.


Tôi đã không hoàn toàn làm theo lời giải thích. Tôi nghĩ rằng tối ưu hóa cuộc gọi đuôi đã được thực hiện bởi trình biên dịch. Giả sử bạn có một hàm có thể được trình biên dịch tối ưu hóa cuộc gọi đuôi, thì bạn cũng có thể có một hàm không đệ quy tương đương thực hiện cùng một chức năng bằng cách sử dụng một vòng lặp, đúng không? Nếu vậy, điều này không thể được thực hiện bởi trình biên dịch. Tôi không thể tuân theo sự phụ thuộc vào JVM. Làm thế nào điều này so sánh với một trình biên dịch Đề án đã tạo ra mã i386 gốc?
Gautham Ganapathy 21/12/09

4
@Gautham: Tuyên bố của tôi về gỡ lỗi liên quan đến việc sử dụng trampolines như một giải pháp thay thế cho việc thiếu loại bỏ lệnh gọi đuôi trên JVM. Loại bỏ lệnh gọi đuôi có thể và đã được thực hiện trên JVM (Arnold Schaighofer đã làm trong OpenJDK, và cả LLVM) vì vậy không có câu hỏi về việc liệu nó có thể được thực hiện hay không. Tất nhiên, CLR của Microsoft đã hỗ trợ loại bỏ cuộc gọi đuôi trong 10 năm và việc phát hành F # đã chứng minh rằng nó là một người thay đổi cuộc chơi. Tôi nghĩ câu trả lời là JVM đã đình trệ từ lâu.
JD

3
Đây là một quan niệm sai lầm phổ biến và là một lý do thường xuyên lặp lại, nhưng không chính xác. Nó đã được thiết lập tốt trong một số năm rằng bảo mật bằng cách kiểm tra ngăn xếp (và cung cấp các dấu vết ngăn xếp hữu ích) không tương thích với các lệnh gọi đuôi thích hợp. Ví dụ, hãy xem bài báo này từ năm 2004. citeseerx.ist.psu.edu/viewdoc/… Phản đối, vì câu trả lời không chính xác.
Justin Sheehy

5
@JustinSheehy: Điều gì không chính xác? Câu hỏi đặt ra là "JVM có ngăn chặn tối ưu hóa cuộc gọi đuôi không?" Và câu trả lời là, "Không, nhưng rất khó."
Michael Myers

5
không biết trong java8 có cái này không?
nachokk

27

Hạn chế cơ bản đơn giản là JVM không cung cấp các lệnh gọi đuôi trong mã byte của nó và do đó, không có cách nào trực tiếp để một ngôn ngữ được xây dựng dựa trên JVM tự cung cấp các lệnh gọi đuôi. Có những cách giải quyết có thể đạt được hiệu quả tương tự (ví dụ: trampolining) nhưng chúng phải trả giá bằng hiệu suất khủng khiếp và làm xáo trộn mã trung gian được tạo ra khiến trình gỡ lỗi trở nên vô dụng.

Vì vậy, JVM không thể hỗ trợ bất kỳ ngôn ngữ lập trình chức năng chất lượng sản xuất nào cho đến khi Sun thực hiện các lệnh gọi đuôi trong chính JVM. Họ đã thảo luận về nó trong nhiều năm nhưng tôi nghi ngờ họ sẽ bao giờ thực hiện các lệnh gọi đuôi: sẽ rất khó khăn vì họ đã tối ưu hóa quá sớm máy ảo của mình trước khi triển khai chức năng cơ bản như vậy và nỗ lực của Sun tập trung mạnh vào các ngôn ngữ động hơn là các ngôn ngữ chức năng.

Do đó, có một lập luận rất mạnh mẽ rằng Scala không phải là một ngôn ngữ lập trình chức năng thực sự: các ngôn ngữ này đã coi các lệnh gọi đuôi là một tính năng thiết yếu kể từ khi Scheme lần đầu tiên được giới thiệu cách đây hơn 30 năm.


5
Hence there is a very strong argument that Scala is not a real functional programming language - lập luận thực sự khá yếu. Chắc chắn là như vậy tail calls [as] an essential feature, và thật tuyệt nếu phần cứng bên dưới (hoặc máy ảo) hỗ trợ trực tiếp. Nhưng đó là chi tiết thực hiện.
Ingo

8
@Ingo: Chỉ khi bạn không coi việc tràn ngăn xếp trong chương trình của mình tại thời điểm chạy được người dùng coi là một vấn đề nghiêm trọng. Theo trình theo dõi lỗi của nó, ngay cả bản thân trình biên dịch Scala cũng gặp phải tình trạng tràn ngăn xếp. Vì vậy, ngay cả những nhà phát triển Scala dày dạn nhất vẫn đang nhận được nó sai ...
JD

7
Là một người ủng hộ, nói F #. Nhưng tôi đã lưu ý bạn trong một thời gian dài (thậm chí nhiều năm trước khi sử dụng mạng) vì có thái độ thù địch với mọi thứ không phải là F #, và những công phu của bạn cho thấy rằng bạn không biết mình đang nói về cái gì. Giống như đây: lập luận của bạn dường như là một ngôn ngữ mà tôi có thể viết một chương trình hủy bỏ với tràn ngăn xếp không phải là một ngôn ngữ chức năng? Nhưng không thể lập luận tương tự cho các ngôn ngữ mà tôi có thể gây ra hiện tượng tràn đống? Do đó, bản thân F # thánh sẽ không được coi là có chức năng.
Ingo

7
@Ingo: Một số thành ngữ trong lập trình hàm, như kiểu đệ quy lẫn nhau và kiểu truyền tiếp tục, có thể yêu cầu loại bỏ lệnh gọi đuôi để hoạt động. Nếu không có nó, các chương trình của bạn sẽ bị tràn. Nếu một ngôn ngữ không thể chạy mã chức năng thành ngữ một cách đáng tin cậy, thì nó có chức năng không? Câu trả lời là một lời kêu gọi phán xét, như bạn nói, nhưng là một sự khác biệt quan trọng trong thực tế. Martin Trojer vừa xuất bản một bài đăng trên blog thú vị về điều này: martinsprogrammingblog.blogspot.com/2011/11/…
JD

2
Tuy nhiên, chỉ vì JVM (đáng tiếc, không có câu hỏi) không thể thực hiện lệnh gọi đuôi, điều này không có nghĩa là việc loại bỏ lệnh gọi đuôi là không thể. Điều này giống như thể người ta đã nói rằng các phép tính dấu phẩy động chỉ có thể thực hiện được trên các máy tính có FPU.
Ingo

22

Scala 2.7.x hỗ trợ tối ưu hóa cuộc gọi đuôi để tự đệ quy (một hàm gọi chính nó) của các phương thức cuối cùng và các hàm cục bộ.

Scala 2.8 cũng có thể đi kèm với hỗ trợ thư viện cho tấm bạt lò xo, đây là một kỹ thuật để tối ưu hóa các hàm đệ quy lẫn nhau.

Bạn có thể tìm thấy nhiều thông tin về trạng thái của đệ quy Scala trong blog của Rich Dougherty .


Bạn vui lòng cập nhật câu hỏi về tình trạng bỏng nước hiện tại được không?
om-nom-nom

@ om-nom-nom AFAIK, không có gì thay đổi, cả về phía Scala, cũng như phía JVM.
Daniel C. Sobral

8

Ngoài bài báo được liên kết trong Lambda The Ultimate (từ liên kết mmyers được đăng ở trên), John Rose từ Sun có một số điều khác để nói về tối ưu hóa cuộc gọi đuôi.

http://blogs.oracle.com/jrose/entry/tail_calls_in_the_vm

Tôi đã nghe nói rằng nó có thể được triển khai trên JVM vào một ngày nào đó. Hỗ trợ cuộc gọi đuôi trong số những thứ khác đang được xem xét trên Máy Da Vinci.

http://openjdk.java.net/projects/mlvm/


0

Tất cả các nguồn đều chỉ ra rằng JVM không thể tối ưu hóa trong trường hợp đệ quy đuôi, nhưng khi đọc phần điều chỉnh hiệu suất Java (2003, O’reilly), tôi thấy tác giả tuyên bố rằng anh ta có thể đạt được hiệu suất đệ quy lớn hơn bằng cách triển khai đệ quy đuôi.

Bạn có thể tìm thấy tuyên bố của anh ấy trên trang 212 (tìm kiếm 'đệ quy đuôi' nó sẽ là kết quả thứ hai). Đưa cái gì?


IBM đã hỗ trợ một số hình thức TCO trong việc triển khai JVM của họ (như một sự tối ưu hóa, vì vậy không có gì đảm bảo). Có thể các tác giả của điều chỉnh Hiệu suất Java nghĩ rằng tính năng này cuối cùng sẽ được thực hiện bởi tất cả các JVM. ibm.com/developerworks/java/library/j-diag8.html
llemieng
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.