Skip to content

Instantly share code, notes, and snippets.

@schneems
Created March 16, 2015 21:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schneems/38aaa586f25de6bc1916 to your computer and use it in GitHub Desktop.
Save schneems/38aaa586f25de6bc1916 to your computer and use it in GitHub Desktop.
$LOADED_FEATURES
=> ["enumerator.so", "rational.so", "complex.so", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/enc/encdb.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/enc/trans/transdb.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/unicode_normalize.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/rbconfig.rb", "thread.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/thread.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/compatibility.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/defaults.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/deprecate.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/errors.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/version.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/requirement.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/platform.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/basic_specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/stub_specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/util/stringio.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/exceptions.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_gem.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/e2mmap.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/init.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/workspace.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/inspector.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/context.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/extend-command.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/output-method.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/notifier.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/slex.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/ruby-token.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/ruby-lex.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/src_encoding.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/magic-file.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/readline.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/input-method.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/locale.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/path_support.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/dependency.rb"]
require 'benchmark'
puts irb(main):004:0* $LOADED_FEATURES
=> ["enumerator.so", "rational.so", "complex.so", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/enc/encdb.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/enc/trans/transdb.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/unicode_normalize.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/rbconfig.rb", "thread.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/thread.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/compatibility.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/defaults.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/deprecate.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/errors.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/version.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/requirement.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/platform.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/basic_specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/stub_specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/util/stringio.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/specification.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/exceptions.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_gem.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/monitor.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/e2mmap.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/init.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/workspace.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/inspector.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/context.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/extend-command.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/output-method.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/notifier.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/slex.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/ruby-token.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/ruby-lex.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/src_encoding.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/magic-file.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-darwin14/readline.bundle", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/input-method.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb/locale.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/irb.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/path_support.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/rubygems/dependency.rb", "/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/benchmark.rb"]

You can see the last item is now

"/Users/richardschneeman/.rubies/ruby-2.2.1/lib/ruby/2.2.0/benchmark.rb"

Inside of the make_gems method is a call to save_loaded_features. The source for this is very simple:

def save_loaded_features
  old_loaded_features = $LOADED_FEATURES.dup
  yield
ensure
  $LOADED_FEATURES.replace old_loaded_features
end

This method copies all previously loaded features and stores them. Once the block is done executing it replaces any loaded features with the previously loaded ones. This is used so that gems can be required multiple times in different calls to the make_gems method.

The new_spec method makes dummy specifications for easy testing. It takes the name of the package, the current version of the package, and all of the dependencies as a hash.

We create a pkg0 dependency that depends on itself and others:

{"pkg0"=>">= 0", "pkg1"=>">= 0", "pkg2"=>">= 0", "pkg3"=>">= 0", "pkg4"=>">= 0", "pkg5"=>">= 0", "pkg6"=>">= 0", "pkg7"=>">= 0"}

Finally we create a pkg_base that depends on pkg0:

base = new_spec "pkg_base", "1", {"pkg0" => ">= 0"}

The new_spec method creates and adds gems to disk and it also loads them into memory. Since we want to benchmark the loading process we can unload all gems in memory:

Gem::Specification.reset

Next we call install_specs and pass in the pkg_base spec and an array of specs

[#<Gem::Specification:0x3fd3d8b52fd4 pkg0-0>, #<Gem::Specification:0x3fd3d8b4a7d0 pkg0-1>, #<Gem::Specification:0x3fd3d8b72168 pkg0-2>, #<Gem::Specification:0x3fd3d8b63dd4 pkg0-3>, #<Gem::Specification:0x3fd3d8b8ad80 pkg1-0>, #<Gem::Specification:0x3fd3d8b825a4 pkg1-1>, #<Gem::Specification:0x3fd3d8b7a930 pkg1-2>, #<Gem::Specification:0x3fd3d8b06b0c pkg1-3>, #<Gem::Specification:0x3fd3d8b06bd4 pkg2-0>, #<Gem::Specification:0x3fd3d8b8e9e4 pkg2-1>, #<Gem::Specification:0x3fd3d8b8a04c pkg2-2>, #<Gem::Specification:0x3fd3d8b835bc pkg2-3>, #<Gem::Specification:0x3fd3d8b7e314 pkg3-0>, #<Gem::Specification:0x3fd3d8b7a7dc pkg3-1>, #<Gem::Specification:0x3fd3d8b769c0 pkg3-2>, #<Gem::Specification:0x3fd3d8b72104 pkg3-3>, #<Gem::Specification:0x3fd3d8b6e9c8 pkg4-0>, #<Gem::Specification:0x3fd3d8b6a8f0 pkg4-1>, #<Gem::Specification:0x3fd3d8b66ef8 pkg4-2>, #<Gem::Specification:0x3fd3d8b63938 pkg4-3>, #<Gem::Specification:0x3fd3d8b5ed20 pkg5-0>, #<Gem::Specification:0x3fd3d8b57a34 pkg5-1>, #<Gem::Specification:0x3fd3d8b528b8 pkg5-2>, #<Gem::Specification:0x3fd3d8b4bc48 pkg5-3>, #<Gem::Specification:0x3fd3d8b4a3c0 pkg6-0>, #<Gem::Specification:0x3fd3d8b46914 pkg6-1>, #<Gem::Specification:0x3fd3d8b43714 pkg6-2>, #<Gem::Specification:0x3fd3d8b42684 pkg6-3>, #<Gem::Specification:0x3fd3d8b3ee58 pkg7-0>, #<Gem::Specification:0x3fd3d8b3bac8 pkg7-1>, #<Gem::Specification:0x3fd3d8b3aaec pkg7-2>, #<Gem::Specification:0x3fd3d8b37c98 pkg7-3>]

The install_specs method simulates gem install for the purposes of testing. Finally we call Specification#activate on the base gem. Here are the docs:

# Activate this spec, registering it as a loaded spec and adding
# it's lib paths to $LOAD_PATH. Returns true if the spec was
# activated, false if it was previously activated. Freaks out if
# there are conflicts upon activation.

Essentially this is the same as calling require 'pkg_base'.

Tracing allocations

To see the objects created in memory we're using koichi's allocation tracer gem. Then inside of the test_memory method the make_gems method is being called and inside the block being passed to the make_gems method this code is being run:

r = ObjectSpace::AllocationTracer.trace do
  assert_raises(LoadError) { require 'no_such_file_foo' }
end

In this code we are telling allocation tracer to trace what happens when we manually require a file that doesn't exist. Since we're doing this in a test case we can assert that a LoadError was raised.

Each time an object is allocated it will be registered in the allocation tracer. Later we can review the count of objects that were created by summing them:

puts :TOTAL => ObjectSpace::AllocationTracer.allocated_count_table.values.inject(:+)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment