2010-09-30 38 views
7

Tôi đang làm việc trên một hệ thống phân tích dựa trên Scala (http://www.hiringthing.com) và tôi nhận thấy rằng tôi thường tự hỏi mình câu hỏi sau. Với một hàm "thuần" không có tác dụng phụ, nếu tôi nhấn hàm đó hai lần với cùng các đầu vào, tôi có thể mong đợi trình biên dịch sử dụng lại giá trị được tạo ra từ lần chạy đầu tiên, hoặc nó sẽ đi qua tất cả mã một lần nữa. Nói cách khác, là ví dụ đầu tiên dưới hiệu quả hơn thứ hai?Trình biên dịch Scala có hiệu quả như thế nào khi sử dụng lại các kết quả hàm đã biết?

def add(x: Int, y: Int) = x + y * 10000000000 

val a = add(1,2) 
do_something(a) 
do_another_thing(a) 

vs

def add(x: Int, y: Int) = x + y * 10000000000 

do_something(add(1,2)) 
do_another_thing(add(1,2)) 

Nếu trình biên dịch thực sự có thể tối ưu hóa các trường hợp thứ hai, là có giới hạn cho sự phức tạp của hàm?

Những gì tôi muốn làm là tránh chạy toán nhất định chức năng nặng nhiều lần chỉ vì lợi ích của sự thuận tiện lập trình ...

Cảm ơn

+0

javap có thể giúp xem nhanh mã byte được trình biên dịch tạo ra. Tôi nghi ngờ rằng mặc dù trình biên dịch hiện tại sẽ thực hiện tối ưu hóa, đơn giản vì không có monads, thật khó để xác định xem mã có bất kỳ tác dụng phụ nào không, nhưng tôi có thể sai. –

Trả lời

9

Từ những gì tôi đã thấy, trình biên dịch Scala không tối ưu hóa điều đó chút nào. JVM có thể, nếu nó có thể xác định rằng kết quả sẽ không thay đổi, nhưng thường thì nó không có cách nào tốt để biết. Vì vậy, nói chung, nếu nó là một tính toán tầm thường, nó không tạo ra sự khác biệt, bởi vì nó là tầm thường, và bởi vì JVM có thể tìm ra rằng nó chỉ cần làm điều đó một lần. Nếu nó phức tạp và tốc độ là cần thiết, bạn nên sử dụng phương thức val a = trừ khi bạn có điểm chuẩn chứng minh cho bạn rằng JVM đủ thông minh trong trường hợp này.

Lưu ý rằng đôi khi cảm thấy khó xử khi đặt vals. Có hai cách để giải quyết vấn đề này. Đầu tiên, lưu ý rằng hầu hết mọi thứ có thể được thay thế bằng câu lệnh tương đương trong niềng răng, vì vậy các ngoại lệ có thể ít thường xuyên hơn bạn nghĩ. Ngoài ra, phương pháp này có thể hữu ích trong một số trường hợp:

def reuse[A,B](a: A)(f: A => B) = f(a) 
reuse(add(1,2))(a => { do_something(a); do_another_thing(a)}) 
+1

Thông qua truyền thống Lisp và Haskell dài, chức năng "tái sử dụng" có lẽ nên được gọi là "cho" –

+0

Có, có thể vậy. –

5

Trình biên dịch scala không cố gắng để tối ưu hóa bất kỳ thông qua các cuộc gọi đến các hàm hoặc phương thức, trừ khi bạn sử dụng cụ thể chú thích @inline (và thậm chí là chú thích không phải là gauranteed). Điều đó nói rằng, JVM, và trình biên dịch JIT Hotspot nói riêng, gần như chắc chắn sẽ có thể nội tuyến cuộc gọi để "thêm" trong các ví dụ của bạn, và sau đó sẽ có thể loại bỏ các kết quả chung-subexpressions.

Như thường lệ khi đặt câu hỏi về tối ưu hóa hiệu suất và trình biên dịch, không có câu trả lời nào được coi là phúc âm mà không có điểm chuẩn chuyên sâu. Hiệu suất trước đây không phải là gaurantee của lợi nhuận trong tương lai. Nội dung có thể giải quyết trong quá trình vận chuyển. Nếu sưng vẫn còn sau bốn giờ, hãy đi khám bác sĩ. Tất cả các mô hình trên 18.

+2

Bạn đã quên "hoặc 21 khi được yêu cầu". –

5

Nếu không có một hệ thống có hiệu lực, trình biên dịch đơn giản là không thể quyết định sử dụng lại phương pháp (hoặc Function) giá trị trả về.

Như trường hợp của hầu hết các ngôn ngữ trong suốt lịch sử điện toán, bạn có thể xóa bỏ sự thừa từ các thuật toán của mình.

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