2010-10-07 4 views
7

я столкнулся следующий кодом в специальном выпуске 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) 
} 

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

+0

ленивый должен вытолкнуть вычисление значения до его вызова. Однако, хороший вопрос. Создает ли объект объект для хранения функции для оценки? – wheaties

+2

Просто чтобы вы знали, вы можете получить тот же строковый формат, вызвав '(x, y) .toString' – 2010-10-07 17:00:43

ответ

0

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

+3

Неизменяемый по определению? scala> класс case Foo (var x: Int) определенный класс Foo –

+0

Не знал, что модификатор 'var' может использоваться для членов класса класса case. Я должен был сказать: «класс дела, заданный в вопросе, неизменен по определению», хотя я все еще думаю, что вы сделали там чистое зло. –

1

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

0

Похож на микро-оптимизацию для меня. JVM может достаточно позаботиться о таких случаях.

+6

В JVM абсолютно ничего нет, которые будут мемуаровать результаты метода, подобные этому за пределами локальной области (т. Е. Если вы дважды вызываете .toString в одном и том же методе, JVM имеет шанс кэшировать простой метод возврата.) Устойчивое кэширование таких результатов - это далеко за пределами его сферы, что хорошо (в этом случае), автоматическое запоминание приведет к значительному увеличению объема памяти. –

19

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

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

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

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

Обратите внимание, что def не может переопределить val - вы можете сделать только члены более стабильны в классе к югу.

+4

+1, не знал, что 'def' может быть переопределено' lazy val '. :) – missingfaktor

Смежные вопросы