2014-10-25 2 views
7

Этот вопрос в основном касается внутренних элементов Ruby, скорость может быть измерена простым эталоном.ruby ​​memoization, efficiency

Каков наиболее эффективный способ запоминания возвращаемого значения в рубине?

Я всегда memoized значения с:

def method 
    @value ||= calculate_value 
end 

Но так как это технически расширяется:

@value = @value || calculate_value 

Интересно об эффективности повторного выполнения той же уступки каждый раз.

Будет ли это лучше?

def method 
    @value ? @value : (@value = calculate_value) 
end 

Кроме того, это меняется в разных интерпретаторах? МРТ, Rubinius и т.д.

+1

Если значение calculate_value дорогое и может возвращать значение false или nil, вы должны подумать над чем-то вроде 'defined? @стоимость ? @value: (@value = calculate_value) '. –

+0

Интересный вопрос, тот, чей ответ я рад узнать. Не забудьте выбрать ответ, если таковые были полезны. –

ответ

6

Ваш пример

@value ||= calculate_value 

эквивалентно

@value || @value = calculate_value 

и не эквивалентно

@value = @value || calculate_value 

Поэтому ответ: Это очень эффективный. Он не переустанавливается каждый раз.

+0

вы можете это доказать?Например. котировки исходного кода Ruby или что-нибудь еще? Простой тест показывает, что (значение || = расчет_значение) работает на 30% медленнее, чем (значение || значение = расчет_значение). –

+3

Возможно, вы захотите прочитать эту статью Питера Купера (http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html) или некоторые потоки в Rubyforum (https://www.ruby-forum.com/topic/151660/) – spickermann

+1

Боюсь, что вы ошибаетесь. 'a + = 1' эквивалентно' a = a + 1'. Это же правило применяется и к другим операторам: 'a || = 1' расширяется до' a = a || 1'. – tompave

0

@spickermann, @tompave, извините за отправку его в качестве ответа, но мне нужно форматирование кода, чтобы опубликовать результаты тестов для разных случаев использования. Это выглядит как явный способ является самым быстрым @a || @a = calculated_value

> n = 10000000; 
> a = 1; 
> puts Benchmark.measure { n.times { a ||= 1 } } 
    0.570000 0.000000 0.570000 ( 0.569977) 
> puts Benchmark.measure { n.times { a ? a : a = 1 } } 
    0.560000 0.000000 0.560000 ( 0.562855) 
> puts Benchmark.measure { n.times { a || a = 1 } } 
    0.530000 0.000000 0.530000 ( 0.532715) 
> @a = 1; 
> puts Benchmark.measure { n.times { @a ||= 1 } } 
    0.870000 0.000000 0.870000 ( 0.869489) 
> puts Benchmark.measure { n.times { @a ? @a : @a = 1 } } 
    0.670000 0.000000 0.670000 ( 0.668910) 
> puts Benchmark.measure { n.times { @a || @a = 1 } } 
    0.610000 0.000000 0.610000 ( 0.613978) 

рубин 2.1.2p95 (2014-05-08 ревизии 45877) [x86_64-darwin13.0]

Всех 3 нотаций ведут себя так же от результата точки но последний из них является голосом с точки зрения производительности. К сожалению, все это эмпирически, и я не могу связать здесь какой-либо исходный код ruby.

+0

Извините, мой комментарий не был точным Вы рассматриваете только один случай альтернатив. – sawa

0

Я думаю, что любые различия между ||= и ... = ... || ... должны быть тонкими.

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

def method 
    alias method memoized_value 
    @value ||= calculate_value 
end 

def memoized_value; @value end 

Это предполагает, что calculate_value всегда возвращает truthy значение, и нет никакой другой части в коде, который изменяет значение так, что когда-то method называется, значение переменной остается truthy.

+0

интересно, но, например, методы 'alias' будут вызываться хотя бы один раз для каждого экземпляра, а IIRC он удаляет кеш метода Ruby. – tompave

+0

@tompave Я намеревался использовать два метода вам нужны эти пары столько переменных, которые вы хотите сделать memoization. – sawa

+0

да, но он будет уничтожать кеш метода каждый раз, когда создается псевдоним. – tompave