Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster2: Cache the method, ivar, and arity and the ancestry memoized methods #38

Merged
merged 2 commits into from
Dec 14, 2015

Conversation

jrafanie
Copy link
Contributor

We can limit calls to memoized_ivar_for/unmemoized_method_for and
related methods for things we've already calculated initially on the
memoize :some_method call.

Fixes prime_cache not priming inherited memoized methods, broken by #36
Fixes (un)memoize_all not clearing/priming subclass identifiers

flush_cache now:             121143.3 i/s
flush_cache PR 34:            16079.4 i/s - 7.53x slower
flush_cache master:            3718.0 i/s - 32.58x slower

flush_cache with args now:    43484.0 i/s
flush_cache with args PR 34:  17488.5 i/s - 2.49x slower
flush_cache with args master: 17279.3 i/s - 2.52x slower

prime_cache now:              55946.9 i/s
prime_cache PR 34:            12665.0 i/s - 4.42x slower
prime_cache master:            6057.2 i/s - 9.24x slower

prime_cache with args now:    30015.3 i/s
prime_cache with args PR 34:  14012.1 i/s - 2.14x slower
prime_cache with args master: 13914.9 i/s - 2.16x slower

For a class with 40 memoized methods, total allocations is also
greatly reduced where each method below is called 1,000 times:

method PR 34 master now
prime_cache with args 363421 164000 45004
prime_cache no args 630535 168000 41000
flush_cache with args 643361 164000 5000
flush_cache no args 907607 168000 1000

We can limit calls to memoized_ivar_for/unmemoized_method_for and
related methods for things we've already calculated initially on the
memoize :some_method call.

Fixes prime_cache not priming inherited memoized methods, broken by matthewrudy#36
Fixes (un)memoize_all not clearing/priming subclass identifiers

flush_cache now:             121143.3 i/s
flush_cache PR 34:            16079.4 i/s - 7.53x slower
flush_cache master:            3718.0 i/s - 32.58x slower

flush_cache with args now:    43484.0 i/s
flush_cache with args PR 34:  17488.5 i/s - 2.49x slower
flush_cache with args master: 17279.3 i/s - 2.52x slower

prime_cache now:              55946.9 i/s
prime_cache PR 34:            12665.0 i/s - 4.42x slower
prime_cache master:            6057.2 i/s - 9.24x slower

prime_cache with args now:    30015.3 i/s
prime_cache with args PR 34:  14012.1 i/s - 2.14x slower
prime_cache with args master: 13914.9 i/s - 2.16x slower

For a class with 40 memoized methods, total allocations is also
greatly reduced where each method below is called 1,000 times:

method | PR 34 | master | now
------ | ----- | ----- | ---
prime_cache with args | 363421 | 164000 | 45004
prime_cache no args | 630535 | 168000 | 41000
flush_cache with args | 643361 | 164000 | 5000
flush_cache no args | 907607 | 168000 | 1000

SCRIPT:
```ruby
def log_all_allocations(key = :line)
  require 'allocation_tracer'

  trace_keys = %i{path type class} if [:path, :file].include?(key)
  trace_keys = %i{line path type class} if key == :line

  ObjectSpace::AllocationTracer.setup(trace_keys)
  return_from_yield = nil
  result = ObjectSpace::AllocationTracer.trace do
    return_from_yield = yield
    nil
  end

  puts "Total: #{result.values.inject(0) { |count, v| count += v[0]}}"
  return_from_yield
end

require './lib/memoist'
require 'set'

$methods = Set.new
class Person
  extend Memoist
  1.upto(20) do |n|
    method = define_method("test_#{n}".to_sym) {}
    memoize(method)
    $methods.add(method)
  end

  1.upto(20) do |n|
    method = define_method("test_#{n}?".to_sym) {}
    memoize(method)
    $methods.add(method)
  end
end

puts "prime_cache with args"
p = Person.new
log_all_allocations(:line) do
  1_000.times do
    p.prime_cache(*$methods)
  end
end

puts "prime_cache no args"
p = Person.new
log_all_allocations(:line) do
  1_000.times do
    p.prime_cache
  end
end

puts "flush_cache with args"
p = Person.new
log_all_allocations(:line) do
  1_000.times do
    p.flush_cache(*$methods)
  end
end

puts "flush_cache no args"
p = Person.new
log_all_allocations(:line) do
  1_000.times do
    p.flush_cache
  end
end
```
@jrafanie jrafanie changed the title [WIP] Faster2: Cache the method, ivar, and arity and the ancestry memoized methods Faster2: Cache the method, ivar, and arity and the ancestry memoized methods Dec 10, 2015
@jrafanie
Copy link
Contributor Author

@matthewrudy I know this makes things more complicated but at the same time, the memoize :name, :identifier => :student methods and ivars were not being primed/flushed previously. This PR fixes that along with making everything faster. It does make it harder to test since any call to memoize will pollute a class that other tests might be using so you have to make sure you clean it up or use a unique class.

@matthewrudy
Copy link
Owner

@jrafanie i should never have added that identifier option, really

@matthewrudy
Copy link
Owner

But the tests pass, and you seem to be making good progress with the object count.

So let's merge it.
And I'll do a gem release in a couple of days.

matthewrudy added a commit that referenced this pull request Dec 14, 2015
Faster2: Cache the method, ivar, and arity and the ancestry memoized methods
@matthewrudy matthewrudy merged commit c41b6c8 into matthewrudy:master Dec 14, 2015
@jrafanie
Copy link
Contributor Author

Thanks @matthewrudy, I agree, the identifier feature makes things harder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants