From 7d0a032887b55aeee92124dc027ab69a63807cb3 Mon Sep 17 00:00:00 2001 From: kputnam Date: Tue, 17 Sep 2019 12:27:04 -0500 Subject: [PATCH] Remove dependency on memory_profiler --- Gemfile | 1 - lib/stupidedi/position/no_position.rb | 4 + lib/stupidedi/reader/substring.rb | 17 ++- spec/lib/stupidedi/reader/pointer_spec.rb | 5 +- spec/lib/stupidedi/reader/substring_spec.rb | 12 +- spec/spec_helper.rb | 5 - spec/support/matchers/allocation_matchers.rb | 8 +- spec/support/memprof.rb | 117 +++++++++++++++++++ 8 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 spec/support/memprof.rb diff --git a/Gemfile b/Gemfile index feca90135..908d73267 100644 --- a/Gemfile +++ b/Gemfile @@ -17,5 +17,4 @@ group :development do gem "benchmark-ips"# "" gem "simplecov"# "" gem "simplecov-inline-html" if RUBY_VERSION >= "2.4" - gem "memory_profiler" if RUBY_VERSION >= "2.3.8" end diff --git a/lib/stupidedi/position/no_position.rb b/lib/stupidedi/position/no_position.rb index de23fff4a..f909fd7fe 100644 --- a/lib/stupidedi/position/no_position.rb +++ b/lib/stupidedi/position/no_position.rb @@ -39,6 +39,10 @@ def inspect def advance(input) self end + + def ===(other) + equal?(other) + end end end end diff --git a/lib/stupidedi/reader/substring.rb b/lib/stupidedi/reader/substring.rb index 8481b437a..ac213e7e0 100644 --- a/lib/stupidedi/reader/substring.rb +++ b/lib/stupidedi/reader/substring.rb @@ -80,8 +80,11 @@ def match?(pattern, offset=0, anchorless: false) # # @return [Integer] def =~(pattern) - m, offset = _match(pattern, 0) - m and m.begin(0) + offset + if result = _match(pattern, 0) + m = result[0] + offset = result[1] + m and m.begin(0) + offset + end end # @group Search @@ -108,8 +111,11 @@ def index(other, offset=0, anchorless: false) return nil if offset > @length if other_ = Regexp.try_convert(other) - m, offset = _match(other_, offset, anchorless) - m and m.begin(0) + offset + if result = _match(other_, offset, anchorless) + m = result[0] + offset = result[1] + m and m.begin(0) + offset + end elsif other_ = String.try_convert(other) n = @storage.index(other_, @offset + offset) n - @offset if n and n + other_.length <= @offset + @length @@ -151,7 +157,8 @@ def rindex(other, offset=@length-1) def count(other) other = _convert(String, other) length = other.length - count, offset = 0, @offset + count = 0 + offset = @offset while true offset = @storage.index(other, offset) diff --git a/spec/lib/stupidedi/reader/pointer_spec.rb b/spec/lib/stupidedi/reader/pointer_spec.rb index af99331dd..c3fac3368 100644 --- a/spec/lib/stupidedi/reader/pointer_spec.rb +++ b/spec/lib/stupidedi/reader/pointer_spec.rb @@ -26,8 +26,9 @@ def suffix(pointer) specify { expect(pointer("abc")).to be_a(Stupidedi::Reader::Pointer) } allocation do - storage = "X" - expect{ pointer(storage) }.to allocate(Stupidedi::Reader::Substring => 1) + ignore = nil + storage = [1,1,1] + expect{ ignore = pointer(storage) }.to allocate(Stupidedi::Reader::Pointer => 1) end end diff --git a/spec/lib/stupidedi/reader/substring_spec.rb b/spec/lib/stupidedi/reader/substring_spec.rb index d8300e526..9f41b5e35 100644 --- a/spec/lib/stupidedi/reader/substring_spec.rb +++ b/spec/lib/stupidedi/reader/substring_spec.rb @@ -774,12 +774,12 @@ def matchp(num_calls) let(:regexp_o) { /..+/ } let(:regexp_x) { /xxx/ } - #it "works like String#=~" do - # substrings(lower.length) do |idx, len| - # expect(lower_ptr[idx, len] =~ regexp_o).to eq(lower[idx, len] =~ regexp_o) - # expect(lower_ptr[idx, len] =~ regexp_x).to eq(lower[idx, len] =~ regexp_x) - # end - #end + it "works like String#=~" do + substrings(lower.length) do |idx, len| + expect(lower_ptr[idx, len] =~ regexp_o).to eq(lower[idx, len] =~ regexp_o) + expect(lower_ptr[idx, len] =~ regexp_x).to eq(lower[idx, len] =~ regexp_x) + end + end allocation do a = lower_ptr.drop(3).take(3) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 203bcb12a..de357a24c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,11 +12,6 @@ rescue LoadError end -begin - require "memory_profiler" -rescue LoadError -end - require "stupidedi" require "ostruct" require "pp" diff --git a/spec/support/matchers/allocation_matchers.rb b/spec/support/matchers/allocation_matchers.rb index d41b150a3..50304e304 100644 --- a/spec/support/matchers/allocation_matchers.rb +++ b/spec/support/matchers/allocation_matchers.rb @@ -18,7 +18,7 @@ def initialize(op, limits) @op, @limits = op, Hash[limits.map{|k,v| [k.to_s, v] }] - raise "can't load memory_profiler" unless defined? MemoryProfiler + raise "can't load memory_profiler" unless defined? MemProf end def description @@ -79,9 +79,9 @@ def measure(target) opts = {trace: @limits.keys.map{|c| Object.const_get(c)}} opts.delete(:trace) if @strict - report = MemoryProfiler.report(opts, &target) - result = report.allocated_objects_by_class - result.inject({}){|memo, x| memo.update(x[:data] => x[:count]) } + report = MemProf.report(opts, &target) + result = report.allocations_by_class + result.inject({}){|memo, (k,v)| memo.update(k.name => v[:count]) } end end end diff --git a/spec/support/memprof.rb b/spec/support/memprof.rb new file mode 100644 index 000000000..3259b7e30 --- /dev/null +++ b/spec/support/memprof.rb @@ -0,0 +1,117 @@ +require "objspace" + +class MemProf + def initialize(options) + @trace = options[:trace] + @ignore = options[:ignore] + @allow = options[:allow] + @retain = false + end + + def run(&block) + start + begin + yield + rescue Exception + ObjectSpace.trace_object_allocations_stop + GC.enable if @retain + raise + else + stop + end + end + + private + + def start + if @retain + GC.start + GC.start + GC.start + GC.disable + end + + @generation = GC.count + ObjectSpace.trace_object_allocations_start + end + + def stop + ObjectSpace.trace_object_allocations_stop + + allocated = allocations + retained = Hash.new + + if @retain + GC.enable + GC.start + GC.start + GC.start + + ObjectSpace.each_object do |o| + next if ObjectSpace.allocation_generation(o) != @generation + match = allocations[o.__id__] + retained[o.__id__] = match if match + end + end + + ObjectSpace.trace_object_allocations_clear + Results.new(allocated, retained) + end + + def allocations + results = Hash.new.compare_by_identity + klass = Kernel.instance_method(:class) + + ObjectSpace.each_object do |o| + next if ObjectSpace.allocation_generation(o) != @generation + + file = ObjectSpace.allocation_sourcefile(o).to_s + next if @ignore and @ignore.match?(file) + next if @allow and not @allow.match?(file) + + klass_ = klass.bind(o).call + next if @trace and not @trace.match?(klass_) + + line = ObjectSpace.allocation_sourceline(o) + method = ObjectSpace.allocation_method_id(o) + bytes = ObjectSpace.memsize_of(o) + + results[o.__id__] = Stat.new(klass_, file, line, [file, line], method, bytes) + end + + results + end +end + +MemProf::Stat = Struct.new(:klass, :file, :line, :location, :method, :bytes) + +class MemProf::Results + def initialize(allocated, retained) + @allocated = allocated.values + @retained = retained.values + end + + def allocations_by_class + Hash[@allocated.group_by(&:klass).map do |klass, stats| + [klass, {data: klass, count: stats.length, bytes: stats.sum(&:bytes)}] + end] + end + + def allocations_by_method + Hash[@allocated.group_by(&:method).map do |klass, stats| + [klass, {data: klass, count: stats.length, bytes: stats.sum(&:bytes)}] + end] + end + + def allocations_by_location + Hash[@allocated.group_by(&:location).map do |klass, stats| + [klass, {data: klass, count: stats.length, bytes: stats.sum(&:bytes)}] + end] + end +end + +class << MemProf + def report(options={}, &block) + new(options).run(&block) + end +end