使用lazy val 缓存字符串表示

发布于 2024-09-26 12:13:15 字数 471 浏览 8 评论 0原文

我在 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)
}

Does the use of lazy val in the 上述代码提供了比以下代码显着更高的性能?

package com.weiglewilczek.gameoflife

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

或者这只是不必要的优化?

I encountered the following code in JAXMag's Scala special issue:

package com.weiglewilczek.gameoflife

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

Does the use of lazy val in the above code provide considerably more performance than the following code?

package com.weiglewilczek.gameoflife

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

Or is it just a case of unnecessary optimization?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

记忆里有你的影子 2024-10-03 12:13:15

关于惰性值需要注意的一件事是,虽然它们只计算一次,但对它们的每次访问都受到双重检查锁定包装器的保护。这对于防止两个不同的线程尝试同时初始化该值并产生令人捧腹的结果是必要的。现在,双重检查锁定非常高效(现在它实际上在 JVM 中工作),并且在大多数情况下不需要获取锁,但比简单的值访问有更多的开销。

另外(有点明显),通过缓存对象的字符串表示形式,您可以显式地牺牲 CPU 周期,以换取可能大幅增加的内存使用量。 “def”版本中的字符串可以被垃圾收集,而“lazy val”版本中的字符串则不能。

最后,与性能问题的情况一样,如果没有基于事实的基准测试,基于理论的假设几乎毫无意义。如果不进行分析,您永远无法确定,所以不妨尝试一下看看。

One thing to note about lazy vals is that, while they are only calculated once, every access to them is protected by a double-checked locking wrapper. This is necessary to prevent two different threads from attempting to initialize the value at the same time with hilarious results. Now double-checked locking is pretty efficient (now that it actually works in the JVM), and won't require lock acquisition in most cases, but there is more overhead than a simple value access.

Additionally (and somewhat obviously), by caching the string representation of your object, you are explicitly trading off CPU cycles for possibly large increases in memory usage. The strings in the "def" version can be garbage-collected, while those in the "lazy val" version will not be.

Finally, as is always the case with performance questions, theory-based hypotheses mean nearly nothing without fact-based benchmarking. You'll never know for sure without profiling, so might as well try it and see.

铁憨憨 2024-10-03 12:13:15

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))

请注意,def 不能覆盖val——您只能使子类中的成员更加稳定。

toString can be directly overriden with a 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))

Note that a def may not override a val -- you can only make members more stable in the sub class.

伪心 2024-10-03 12:13:15

在第一个片段中,position 将根据需要仅计算一次,[when|if] toString 方法被调用。在第二个片段中,每次调用该方法时都会重新评估 toString body。鉴于xy无法更改,这是没有意义的,并且应该存储toString值。

In the first snippet position will be calculated just once, on demand, [when|if] toString method is called. In the second snippet, toString body will be re-evaluated every time the method is called. Given that x and y cannot be changed, it's senseless, and toString value should be stored.

冷了相思 2024-10-03 12:13:15

根据定义,案例类是不可变的。 toString 返回的任何值本身也是不可变的。因此,通过利用惰性 val 来“缓存”该值是有意义的。另一方面,提供的 toString 实现的作用与所有案例类提供的默认 toString 没什么区别。如果普通案例类 toString 在下面使用了惰性 val,我不会感到惊讶。

Case classes are, by definition, immutable. Any value returned by toString will itself be immutable, too. Thus it makes sense to essentially "cache" this value by utilizing a lazy val. On the other hand, the provided toString implementation does little more than the default toString provided by all case classes. I would not be surprised if a vanilla case class toString used a lazy val underneath.

还给你自由 2024-10-03 12:13:15

对我来说看起来像是一个微观优化。 JVM 足以处理此类情况。

Looks like a micro-optimization to me. JVM is able enough to take care of such cases.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文