DEV Community

Augusts Bautra
Augusts Bautra

Posted on • Edited on

4 1

Memoization in Ruby

Memoization is a simple and powerful performance optimisation technique that all Ruby developers should have full command of.
Here I'll go over three memoisation variants, in order of increasing difficulty:

  1. Naive ||=-based memozation
  2. Intermediate defined?-based memoization
  3. Advanced memoization for methods with parameters

1. ||=

A popular Ruby idiom, pronounced "or-equals", where assignment only happens if the lefthand side is falsey. Use this where you know that any potential assigned values will never be falsey.

def response
  # it is a common practice to store the memoized value in
  # an instance variable (ivar) of the same name as the method.
  @response ||= expensive_operation
end
Enter fullscreen mode Exit fullscreen mode

2. defined?-based memoization

A less known pattern that is capable of memoizing falsey values. Also useful if expensive_operation is a multiline statement.

def response
  return @response if defined?(@response)

  # NB, this is a regular assignment because
  # line above already did the cache check.
  @response = expensive_operation
end
Enter fullscreen mode Exit fullscreen mode

3. Memoization for methods with parameters

Sometimes it's useful to memoize several values, depending on the arguments given to a method. This approach can only be used if the values the method returns (and that are about to be memoized) are idempotent i.e. remain the same on repeat calls.

It's a good practice to name the ivar in plural of the method name.

Notice that memo_key must be something unique based on the arguments given. Using Object#hash is a general approach, but there can be others, for example, args can be of a known type and have a unique identifier returned by .id...

def response(arg1, arg2)  
  @responses ||= {}
  memo_key = "#{arg1.hash}_#{arg2.hash}"

  return @responses[memo_key] if @responses.key?(memo_key)

  @responses[memo_key] = begin
    puts "Doing expensive operation"
    expensive_operation
  end
end

> response(:a, :b)
Doing expensive operation
=> something
> response(:a, :b)
=> something

> response(false, false)
Doing expensive operation
=> something_else
> response(false, false)
=> something_else
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More