Ruby on Rails Polymorphic Association Benchmarks

Polymorphic relationships in Ruby on Rails are great. If you don't know what they are, check them out here:

Understanding Polymorphic Associations

John and I were curious about the speed of these relations, since the linking between objects searches on both the ID of the foreign object, and a string which is the model name. So if you have two tables, ChildA and ChildB, your parent has a reference to child which is acutally the combination of child_id (the ID in the ChildA or ChildB table) and child_type (equal to "ChildA" or "ChildB").

The old-school way of doing this involves creating a lookup table and using integer IDs for type, instead of strings. So you'd have another table mapping "ChildA" to "1" and "ChildB" to "2", then when you do your query, you are matching against the number "1" and not the string "ChildA".

The down side of doing it that way is that you don't get to use Rails' snazzy polymorphism, which makes life a lot easier. So we decided to run some tests to see how much faster it would be, and therefore, if it was worth it.

I created a Rails application that sets up four tables in the database:

  • A table with an Integer ID and a String Type
  • A table with an Integer ID and a String Type and an index on the type
  • A table with an Integer ID and an Integer Type
  • A table with an Integer ID and an Integer Type and an index on the type

My benchmarking procedure is as follows:

  1. Setup the number of records, N
  2. Setup the number of types, T
  3. Create N records, such that for each type there is an entry with ID 1, 2, 3 and so on. So you have
    Type 1 : ID 1
    Type 2 : ID 1
    Type 1 : ID 2
    Type 2: ID 2
  4. Insert all of these records in a random order
  5. Retrieve the records in a different random order via
    Model.find(:first, :conditions=> {:id => id, :type => type})

I made use of the Ruby benchmarking library to time this process for each table.

I have some preliminary results from running the test quickly, which shows a 500% speedup for using an index, and a 5% speedup for using integers instead of strings. In my opinion, polymorphism in Rails is worth 5%. And adding an index is definitely worth 500%. I mean, what isn't worth 500%?

That was based on just 1,000 records. I am running it on 100,000 records now, but each table takes 1-2 hours to run.

If you'd like to play with my code, here is a link to it on GitHub: