Использование lazy val для кэширования строковых представлений - PullRequest
7 голосов
/ 07 октября 2010

В специальном выпуске Scala JAXMag я обнаружил следующий код:

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

Или это просто случай ненужной оптимизации?

Ответы [ 5 ]

19 голосов
/ 07 октября 2010

В отношении отложенных значений следует обратить внимание на то, что, хотя они рассчитываются только один раз, каждый доступ к ним защищен двойной проверкой блокировочной оболочки.Это необходимо для предотвращения одновременной инициализации значения двумя разными потоками с веселыми результатами.Теперь двойная проверка блокировки довольно эффективна (теперь, когда она действительно работает в JVM), и в большинстве случаев не потребует блокировки, но требует больше затрат, чем простой доступ к значению.

Дополнительно (инесколько очевидно), кэшируя строковое представление вашего объекта, вы явно торгуете за счет циклов ЦП для возможно значительного увеличения использования памяти.Строки в версии «def» можно собирать мусором, а строки в версии «lazy val» - нет.

Наконец, как всегда в случае с вопросами производительности, основанные на теории гипотезы почти ничего не значат без основанного на фактах сопоставительного анализа.Вы никогда не узнаете наверняка без профилирования, так что можете попробовать и посмотреть.

13 голосов
/ 07 октября 2010

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 - вы можете только сделать члены более стабильными в подпрограмме.класс.

1 голос
/ 07 октября 2010

В первом фрагменте position будет вычислено только один раз, по требованию, [когда | если] вызывается метод toString. Во втором фрагменте тело toString будет переоцениваться при каждом вызове метода. Учитывая, что x и y нельзя изменить, это бессмысленно, и значение toString должно быть сохранено.

0 голосов
/ 07 октября 2010

Выглядит как микрооптимизация для меня. JVM достаточно способна позаботиться о таких случаях.

0 голосов
/ 07 октября 2010

Классы падежей по определению неизменны.Любое значение, возвращаемое toString, само будет неизменным.Таким образом, имеет смысл «кэшировать» это значение, используя ленивый val.С другой стороны, предоставленная реализация toString делает немного больше, чем стандартная toString, предоставляемая всеми классами case.Я не удивлюсь, если под классом ванильного класса toString будет использоваться ленивый символ val.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...