High ActiveRecord CPU Utilization

I originally posted this on comp.lang.ruby, but in case someone has a
different perspective here who doesn’t read that newsgroup, here it is:


When running a test that primarily involves loading up a few MySQL
tables with ActiveRecord objects, I was surprised to see the Ruby CPU
utilization at 93% and the MySQL CPU utilization at 7%. I would expect
this workload to be heavier on MySQL than that.

I would think inserts (particularly with updating several foreign key
indices) would tax the database more than Ruby.

Has this been other folks’ experience? Is running in the test
environment incredibly different than production with respect to CPU
utilization? I suppose my next step is to run in production to see what
kind of results I get.

I’m running the test from the root of my Rails project via:

ruby test/unit/foo.rb

Here’s part of the profiler output:

time name
7.96 ActiveRecord::ConnectionAdapters::Quoting.quote
5.61 ActiveRecord::Base#read_attribute
5.15 ActiveRecord::Base#column_for_attribute
4.25 ActiveRecord::Base#connection
3.74 Hash#[]
3.58 Array#each
3.30 ActiveRecord::ConnectionAdapters::MysqlAdapter#quote
3.16 ActiveRecord::ConnectionAdapters::Column#type_cast
2.84 Module#===
2.65 ActiveRecord::Base#clone_attribute_value
2.29 ActiveRecord::Base#write_attribute
2.24 Kernel.class
2.22 Hash#each
2.08 String#to_s
2.03 ActiveRecord::Base#quote_value
1.59 Kernel.send
1.55 Array#include?
1.52 Kernel.==
1.48 ActiveRecord::Base#unserializable_attribute?
1.39 Class#read_inheritable_attribute
1.34 Kernel.clone
1.29 ActiveRecord::Callbacks.notify
1.15 ActiveRecord::Base#method_missing
1.08 ActiveRecord::Base#columns_hash
1.08 ActiveRecord::Base#respond_to?
1.08 ActiveRecord::Callbacks.callback
0.99 Kernel.eval
0.95 Symbol#===
0.90 Observable.notify_observers
0.88 ActiveRecord::ConnectionAdapters::Column#text?
0.85 Hash#[]=
0.85 Class#inheritable_attributes
0.83 Kernel.kind_of?
0.81 ActiveRecord::Base#convert_number_column_value

[and from my followup post]

I just moved my test code into a controller and ran it via:

mongrel_rails start -e production

Similar CPU characteristics except that Mongrel wasn’t able to fully
utilize my dual core CPU (I suppose because of the serialization of
Rails code due to lack of thread safetyness).

So the unit test (1093 records -> table1, 1093 records -> table2, 1
record -> table3) took 5.5 seconds to complete and the identical test in
a controller with Mongrel in production mode took 27.4 seconds!

Yeah, I know I can have a cluster of Mongrel processes, and that’s how I
run for real, but I’m still a little bummed with these results :frowning:

I’ve switched my company’s development from 100% Java to 100% Ruby, and
I still believe that was a good decision because of productivity gains
and joy, but I do miss some of the runtime performance of Java and the
ease with which I could spin up a thread to do some background process.
I’m glad BackgrounDRB has been provided, but it’s not quite the same.

Hopefully future versions of Ruby/Rails will provide some more runtime
performance and concurrency - I’d be glad if I could just fork in Rails
without trouble, but I don’t think that’s the case.

For now, I don’t have more customers than a Core 2 Duo can handle, so
it’s not exactly on the critical path for me yet :slight_smile: In fact, I’m glad
MySQL isn’t on the critical path because overcoming that seems much more
difficult than having a bunch of Apache/Mongrel processes running.