7

JAXMag の Scala 特別号で次のコードに遭遇しました。

package com.weiglewilczek.gameoflife

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

上記のコードで を使用lazy valすると、次のコードよりもパフォーマンスが大幅に向上しますか?

package com.weiglewilczek.gameoflife

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

それとも、不必要な最適化の単なるケースですか?

4

5 に答える 5

19

lazy val について注意すべきことの 1 つは、それらは 1 回だけ計算されますが、それらへのすべてのアクセスは二重チェックのロック ラッパーによって保護されるということです。これは、2 つの異なるスレッドが同時に値を初期化しようとしておかしな結果になるのを防ぐために必要です。現在、二重チェックのロックは非常に効率的であり (JVM で実際に機能するようになったため)、ほとんどの場合、ロックの取得は必要ありませんが、単純な値アクセスよりも多くのオーバーヘッドがあります。

さらに (そしていくらか明らかに)、オブジェクトの文字列表現をキャッシュすることにより、メモリ使用量が大幅に増加する可能性があるため、CPU サイクルを明示的にトレードオフします。「def」バージョンの文字列はガベージ コレクションできますが、「lazy val」バージョンの文字列はそうではありません。

最後に、パフォーマンスの問題でよくあることですが、理論に基づく仮説は、事実に基づくベンチマークなしではほとんど意味がありません。プロファイリングをしなければ確実なことはわからないので、試してみてください。

于 2010-10-07T15:59:15.120 に答える
13

toStringで直接オーバーライドできます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))

defa は aをオーバーライドできないことに注意してくださいval-- サブクラスでのみメンバーをより安定させることができます。

于 2010-10-07T16:16:20.753 に答える
1

最初のスニペットpositionでは、必要に応じて一度だけ計算され、[when|if]toStringメソッドが呼び出されます。2 番目のスニペットでtoStringは、メソッドが呼び出されるたびに body が再評価されます。それと変更できないことを考えるとx、それは無意味であり、値を保存する必要があります。ytoString

于 2010-10-07T15:39:22.823 に答える
0

ケース クラスは、定義上、不変です。toString によって返される値も、それ自体が不変になります。したがって、遅延値を利用してこの値を本質的に「キャッシュ」することは理にかなっています。一方、提供されている toString 実装は、すべてのケース クラスで提供されているデフォルトの toString にすぎません。バニラのケース クラス toString がその下で遅延 val を使用したとしても、私は驚かないでしょう。

于 2010-10-07T15:38:00.203 に答える
0

私にはマイクロ最適化のように見えます。JVM は、このようなケースを十分に処理できます。

于 2010-10-07T15:58:19.953 に答える