Hoopla!

now with extra whiz-bang!

Hoopla!

Ruby Speedup: Memoize those Methods

April 04, 2008 · 0 comments

My company, adPickles, needs to scale big. According to our estimates the site will have to serve pages into the series of tubes faster than the trucks will be able to carry them. Yeah. Something like 3-17 trucks per tube*. That fast.

So we’ve been keeping an eye on various types of caching. One of my favorites is simple method memoization of the kind we’ve all seen before:

1
2
3
4

def last_user
  @last_user ||= User.something_Im_too_lazy_to_make_up
end

Which is great. As long as you don’t need that value to be updated for the life of the object it’s in that’s a solid way to prevent the value from being calculated twice.

But it can get pretty ugly when you have a multi-line method:
1
2
3
4
5
6
7

def current_advertising_balance
  return @current_advertising_balance if @current_advertising_balance
  amount_owed = Invoice.procces.something(:complicated) + OtherThing
  amount_paid = Payment.procces.something(:complicated) + OtherThing
  @current_advertising_balance ||= amount_owed - amount_paid
end

It gets the job done just fine but it’s a hack. You’re using @current_advertising_balance in three (3!) places in the same method.

I just came across this easier way. I don’t know where I found this but I haven’t been using it until lately and I’m really starting to like it. Check this out:
1
2
3
4
5
6
7
8

def current_advertising_balance
  @current_advertising_balance = begin
    amount_owed = Invoice.procces.something(:complicated) + OtherThing
    amount_paid = Payment.procces.something(:complicated) + OtherThing
    amount_owed - amount_paid
  end
end
Whoah. I know. But begin…end is always used to catch errors, right? Well, apparently it’s also a great way to encapsulate any block of code. It has virtually no overhead, doesn’t pollute the namespace, and is easy to read. And you get your error handling for free:
1
2
3
4
5
6
7
8
9
10
11
12

def current_advertising_balance
  @current_advertising_balance = begin
    amount_owed = Invoice.procces.something(:complicated) + OtherThing
    amount_paid = Payment.procces.something(:complicated) + OtherThing
    amount_owed - amount_paid
  rescue
    0.0
  ensure
    Advertiser.mark_that_we_calculated_balance
  end
end
  • 1 truck per tube is equivalent to 1K requests per second

→ 0 comments Tags: