2010-10-07 26 views
7

tôi gặp phải đoạn mã sau trong vấn đề đặc biệt Scala JAXMag của:Sử dụng val lười biếng cho bộ nhớ đệm chuỗi đại diện

package com.weiglewilczek.gameoflife 

case class Cell(x: Int, y: Int) { 
    override def toString = position 
    private lazy val position = "(%s, %s)".format(x, y) 
} 

Liệu việc sử dụng lazy val trong đoạn code trên cung cấp hiệu suất đáng kể hơn so với đoạn mã sau?

package com.weiglewilczek.gameoflife 

case class Cell(x: Int, y: Int) { 
    override def toString = "(%s, %s)".format(x, y) 
} 

Hoặc chỉ là trường hợp tối ưu hóa không cần thiết?

+0

lười nên đẩy tính toán giá trị cho đến khi nó được gọi. Tuy nhiên, câu hỏi hay. Liệu nó tạo ra một đối tượng để giữ chức năng để đánh giá? – wheaties

+2

Để bạn biết, bạn có thể nhận định dạng chuỗi đó bằng cách gọi '(x, y) .toString' –

Trả lời

0

Các trường hợp, theo định nghĩa, không thay đổi. Bất kỳ giá trị nào được trả về bởi toString cũng sẽ không thay đổi. Vì vậy, nó có ý nghĩa về cơ bản "cache" giá trị này bằng cách sử dụng một val lười biếng. Mặt khác, việc cung cấp toString được cung cấp thực hiện nhiều hơn so với tham số toString mặc định được cung cấp bởi tất cả các lớp chữ hoa chữ thường. Tôi sẽ không ngạc nhiên nếu một lớp học vani toString sử dụng một val lười biếng bên dưới.

+3

Không thể định nghĩa theo định nghĩa? scala> trường hợp lớp Foo (var x: Int) lớp được xác định Foo –

+0

Không biết bộ sửa đổi 'var' có thể được sử dụng cho các thành viên dữ liệu lớp vỏ. Tôi nên nói, "trường hợp lớp học được đưa ra trong câu hỏi là bất biến theo định nghĩa" mặc dù tôi vẫn nghĩ rằng những gì bạn đã làm có cái ác thuần khiết. –

1

Trong đoạn đầu tiên position sẽ được tính chỉ một lần, theo yêu cầu, [khi | if] toString phương pháp được gọi. Trong đoạn thứ hai, toString nội dung sẽ được đánh giá lại mỗi khi phương thức được gọi. Cho rằng xy không thể thay đổi, nó vô nghĩa và giá trị toString sẽ được lưu trữ.

0

Dường như tối ưu hóa vi mô cho tôi. JVM có đủ khả năng để xử lý các trường hợp như vậy.

+6

Hoàn toàn không có gì trong JVM sẽ ghi nhớ các kết quả phương thức như thế này bên ngoài phạm vi cục bộ (tức là, nếu bạn gọi .toString hai lần trong cùng một phương thức, JVM có cơ hội lưu vào bộ nhớ đệm phương thức đơn giản.) Việc lưu trữ tạm thời các kết quả như vậy nằm ngoài phạm vi của nó, điều này tốt vì (trong trường hợp này), tự động ghi nhớ sẽ dẫn đến việc tăng bộ nhớ đáng kể –

19

Một điều cần lưu ý về vals lười là, trong khi chúng chỉ được tính một lần, mọi quyền truy cập vào chúng đều được bảo vệ bởi trình bao bọc khóa được kiểm tra kép. Điều này là cần thiết để ngăn chặn hai chủ đề khác nhau từ cố gắng để khởi tạo giá trị cùng một lúc với kết quả vui nhộn. Bây giờ kiểm tra kép khóa là khá hiệu quả (bây giờ nó thực sự hoạt động trong JVM), và sẽ không yêu cầu mua lại khóa trong nhiều trường hợp, nhưng có nhiều chi phí hơn một truy cập giá trị đơn giản.

Ngoài ra (và phần nào hiển nhiên), bằng cách lưu trữ biểu diễn chuỗi của đối tượng của bạn, bạn đang giao dịch một cách rõ ràng các chu kỳ CPU để tăng mức sử dụng bộ nhớ. Các chuỗi trong phiên bản "def" có thể được thu thập rác, trong khi các phiên bản "lazy val" sẽ không được.

Cuối cùng, như mọi khi với câu hỏi về hiệu suất, các giả thuyết dựa trên lý thuyết có nghĩa là gần như không có gì mà không có điểm chuẩn dựa trên thực tế. Bạn sẽ không bao giờ biết chắc chắn mà không có hồ sơ, vì vậy cũng có thể thử nó và xem.

13

toString có thể được ghi đè trực tiếp với lazy val.

scala> case class Cell(x: Int, y: Int) { 
    | override lazy val toString = {println("here"); "(%s, %s)".format(x, y)} 
    | } 
defined class Cell 

scala> {val c = Cell(1, 2); (c.toString, c.toString)} 
here 
res0: (String, String) = ((1, 2),(1, 2)) 

Lưu ý rằng một def có thể không ghi đè lên một val - bạn chỉ có thể làm cho các thành viên ổn định hơn trong lớp phụ.

+4

+1, không biết 'def' có thể bị ghi đè bởi một' lazy val '. :) – missingfaktor

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