require 'active_record'
require 'benchmark'
p ActiveRecord::VERSION::STRING
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => ":memory:"
)
ActiveRecord::Base.connection.execute("CREATE TABLE active_record_models (id INTEGER UNIQUE, title STRING, text STRING)")
class ActiveRecordModel < ActiveRecord::Base; end
ActiveRecordModel.new
N = 100_000
Benchmark.bm { |x| x.report('new') { N.times { ActiveRecordModel.new } } }
def allocate_count
GC.disable
before = ObjectSpace.count_objects
yield
after = ObjectSpace.count_objects
after.each { |k,v| after[k] = v - before[k] }
GC.enable
after
end
p allocate_count { 100.times { {} } }
{ ... :T_HASH=>101, ... }
provider ruby {
probe hash__alloc(const char *, int);
};
$ dtrace -o probes.h -h -s probes.d
diff --git a/hash.c b/hash.c
index b49aff8..c40d94d 100644
--- a/hash.c
+++ b/hash.c
@@ -15,6 +15,7 @@
#include "ruby/st.h"
#include "ruby/util.h"
#include "ruby/encoding.h"
+#include "probes.h"
#include <errno.h>
#ifdef __APPLE__
@@ -221,6 +222,9 @@ hash_alloc(VALUE klass)
OBJSETUP(hash, klass, T_HASH);
RHASH_IFNONE(hash) = Qnil;
+ if(RUBY_HASH_ALLOC_ENABLED()) {
+ RUBY_HASH_ALLOC(rb_sourcefile(), rb_sourceline());
+ }
return (VALUE)hash;
}
ruby*:::hash-alloc
{
printf("%s:%d", copyinstr(arg0), arg1);
}
$ sudo dtrace -s x.d -c 'ruby -I lib test.rb'
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1528
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1529
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1534
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1535
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1525
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/persistence.rb:322
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1527
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1528
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1529
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1534
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1535
1 126930 hash_alloc:hash-alloc /Users/aaron/git/rails/activerecord/lib/active_record/base.rb:1525</code></pre>
<p>I was able to compare the output from this script on Rails 3.0 vs Rails 3.1. From this output, I was able to determine:</p>
<ul>
<li>The number of hash allocations</li>
<li>Where the hashes were allocated</li>
<li>Which allocations were new in Rails 3.1</li>
</ul>
<p>Armed with this information, I was able to eliminate some allocations and return speed in Rails 3.1 to that of Rails 3.0.</p>
<h3>DTrace vs gdb vs memprof</h3>
<p>I probably could have accomplished this task with <a href="https://github.com/ice799/memprof">memprof</a>, but it requires that I use 1.8.7 from rvm. Working with RVM on my machine is difficult (don't ask, let's just say I'm a "special needs" user), and I wanted to use 1.9. So memprof was out the window.</p>
<p>I was able to get the same information by scripting gdb. I set a breakpoint at the correct function, called <code>rb_sourcefile()</code> and <code>rb_sourceline()</code> and redirected to a file. The problem with gdb is that it seemed very slow, and scripting it was a pain (though I am not a gdb expert!).</p>
<p>DTrace was fast, relatively easy to use, and very scriptable. It made me happy!</p>
<h3>OMG!!!</h3>
<p>I would like to see dtrace probes officially added to ruby trunk. The ruby that ships with OS X has them built in, but they don't give us information like hash literal allocations. It looks like <a href="http://redmine.ruby-lang.org/issues/2565">they were in ruby trunk at one point, but were then reverted</a>. I would like to see them added again.</p>
<p>If you want to play with them on ruby trunk, <a href="https://gist.github.com/1055159">here is my full patch</a>. Make sure to run <code>make probes.h</code> before <code>make && make install</code>.</p>
<p>HAPPY WEDNESDAY!!!! <3 <3 <3 <3</p>
<small>(it feels good to blog on a non "professional" blog)</small>