11

Tôi đang xem xét sử dụng Scala trên một chương trình khá tính toán chuyên sâu. Việc biên dịch phiên bản C++ của mã của chúng tôi cho thấy rằng chúng ta có thể hưởng lợi đáng kể từ việc đánh giá Lười biếng. Tôi đã thử nó trong Scala 2.9.1 và thực sự thích nó. Tuy nhiên, khi tôi chạy lớp thông qua một trình biên dịch ngược, việc thực hiện không hoàn toàn đúng. Tôi giả định rằng nó là một artifact của decompiler, nhưng tôi muốn để có được một câu trả lời thuyết phục hơn ...Đây có phải là một lỗi trong Scala 2.9.1 lười biếng thực hiện hoặc chỉ là một tạo tác của decompilation

xem xét ví dụ tầm thường như sau:

class TrivialAngle(radians : Double) 
{ 
    lazy val sin = math.sin(radians) 
} 

khi tôi biên soạn lại nó, tôi có được điều này:

import scala.ScalaObject; 
import scala.math.package.; 
import scala.reflect.ScalaSignature; 

@ScalaSignature(bytes="omitted") 
public class TrivialAngle 
    implements ScalaObject 
{ 
    private final double radians; 
    private double sin; 
    public volatile int bitmap$0; 

    public double sin() 
    { 
    if ((this.bitmap$0 & 0x1) == 0); 
    synchronized (this) 
    { 
     if (
     (this.bitmap$0 & 0x1) == 0) 
     { 
     this.sin = package..MODULE$.sin(this.radians); 
     this.bitmap$0 |= 1; 
     } 
     return this.sin; 
    } 
    } 

    public TrivialAngle(double radians) 
    { 
    } 
} 

Với tôi, khối trả về nằm sai chỗ và bạn sẽ luôn có khóa. Đây không thể là mã thực sự đang làm, nhưng tôi không thể xác nhận điều này. Bất cứ ai có thể xác nhận hoặc phủ nhận rằng tôi có một sự giải mã không có thật, và việc thực hiện lười biếng có phần hợp lý (nghĩa là, chỉ khóa khi nó tính toán giá trị, và không có khóa cho các cuộc gọi tiếp theo?)

Cảm ơn!

Để tham khảo, đây là decompiler tôi đã sử dụng: http://java.decompiler.free.fr/?q=jdgui

+0

Tính chuyên sâu và bạn muốn làm ổ khóa? –

+0

không, tôi có rất nhiều mục mà tôi chỉ muốn tính nếu/khi tôi cần chúng, và tôi muốn các kết quả đó được lưu trữ sau khi tính toán. Tùy thuộc vào việc thực hiện, lười biếng không chính xác những gì tôi muốn. Nếu tôi có thể chỉ định không khóa, điều đó thậm chí sẽ tốt hơn, nhưng đó không phải là điểm của câu hỏi này. – fbl

+1

Tôi đã thực hiện rất nhiều điều chỉnh mã C/C++/Fortran chuyên sâu tính toán (mô phỏng dược phẩm). Phương pháp tôi sử dụng [là này] (http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024). (Bạn không thể lúc nào cũng tin vào các trình biên dịch, ngay cả khi họ nói rõ ràng.) –

Trả lời

9

scala -Xprint:jvm tiết lộ câu chuyện có thật:

[[syntax trees at end of jvm]]// Scala source: lazy.scala 
package <empty> { 
    class TrivialAngle extends java.lang.Object with ScalaObject { 
    @volatile protected var bitmap$0: Int = 0; 
    <paramaccessor> private[this] val radians: Double = _; 
    lazy private[this] var sin: Double = _; 
    <stable> <accessor> lazy def sin(): Double = { 
     if (TrivialAngle.this.bitmap$0.&(1).==(0)) 
     { 
      TrivialAngle.this.synchronized({ 
      if (TrivialAngle.this.bitmap$0.&(1).==(0)) 
       { 
       TrivialAngle.this.sin = scala.math.`package`.sin(TrivialAngle.this.radians); 
       TrivialAngle.this.bitmap$0 = TrivialAngle.this.bitmap$0.|(1); 
       () 
       }; 
      scala.runtime.BoxedUnit.UNIT 
      }); 
     () 
     }; 
     TrivialAngle.this.sin 
    }; 
    def this(radians: Double): TrivialAngle = { 
     TrivialAngle.this.radians = radians; 
     TrivialAngle.super.this(); 
    () 
    } 
    } 
} 

Đó là một (vì JVM 1.5) an toàn, và rất nhanh chóng, kiểm tra đôi khóa.

Thông tin chi tiết:

What's the (hidden) cost of Scala's lazy val?

Hãy lưu ý rằng nếu bạn có nhiều thành viên val lười biếng trong một lớp học, chỉ một trong số họ có thể được khởi tạo cùng một lúc, khi chúng được bảo vệ bởi synchronized(this) { ... }.

+0

Cảm ơn! Nếu tôi có thể đánh dấu 2 câu trả lời là 'đúng', tôi sẽ làm. Tôi đã chọn cái này vì nó dễ đọc hơn một chút;) Tôi hoàn toàn nhận thức được thực tế là các trường lười chỉ có thể được khởi tạo một lần tại một thời điểm. Điều này có thể, hoặc có thể không ảnh hưởng đến cách chúng tôi kiến ​​trúc sư giải pháp của chúng tôi, nhưng nó chắc chắn thông báo quyết định. – fbl

9

Những gì tôi nhận được với javap -c không tương ứng với dịch ngược của bạn. Đặc biệt, không có màn hình nhập khi trường được tìm thấy được khởi tạo. Phiên bản 2.9.1 cũng vậy. Vẫn còn có rào cản bộ nhớ ngụ ý bởi sự truy cập dễ bay hơi của khóa học, vì vậy nó không hoàn toàn miễn phí. Nhận xét bắt đầu với /// là của tôi

public double sin(); 
    Code: 
    0: aload_0 
    1: getfield  #14; //Field bitmap$0:I 
    4: iconst_1 
    5: iand 
    6: iconst_0 
    7: if_icmpne  54 /// if getField & 1 == O goto 54, skip lock 
    10: aload_0 
    11: dup 
    12: astore_1 
    13: monitorenter 
      /// 14 to 52 reasonably equivalent to synchronized block 
      /// in your decompiled code, without the return 
    53: monitorexit 
    54: aload_0 
    55: getfield  #27; //Field sin:D 
    58: dreturn  /// return outside lock 
    59: aload_1  /// (this would be the finally implied by the lock) 
    60: monitorexit 
    61: athrow 
    Exception table: 
    from to target type 
    14 54 59 any 
+0

Cảm ơn! Nếu tôi có thể đánh dấu 2 câu trả lời là 'đúng', tôi sẽ làm. Cả câu trả lời và từ viết tắt này đều tiết lộ bản chất thật sự của Lazy. – fbl

+0

Không vấn đề gì, tôi cũng thích câu trả lời của từ viết tắt hơn, tất cả những gì tôi không phải là mới của tùy chọn -Xprint: jvm này. javap có thể vẫn là thẩm phán cuối cùng nếu bạn thực sự không tin tưởng scala, nhưng tốt hơn nếu nó không đến đó. –

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