benchmark-malloc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +67 -0
- data/Rakefile +8 -0
- data/benchmark-malloc.gemspec +34 -0
- data/lib/benchmark-malloc.rb +1 -0
- data/lib/benchmark/malloc.rb +130 -0
- data/lib/benchmark/malloc/allocation.rb +49 -0
- data/lib/benchmark/malloc/allocation_result.rb +24 -0
- data/lib/benchmark/malloc/allocation_set.rb +49 -0
- data/lib/benchmark/malloc/version.rb +7 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/unit/allocation/new_spec.rb +24 -0
- data/spec/unit/allocation_set/new_spec.rb +46 -0
- data/spec/unit/run_spec.rb +78 -0
- data/tasks/console.rake +11 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +34 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 95f4aeccd4fe1f05278d14d87023639aad34522818a4189d7b72479351ce2565
|
4
|
+
data.tar.gz: faf9e95804733e6de3a77e32c7f6965e27358bc157810857de5da55c074fb7df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1ada0b8d9a8b86ee935ec5ca3acb871fff6b8a110b154fdde435f793271e42a95907383303d8151856ce252c91b1ce605f14cf7c3802d0351967bfc935e6812f
|
7
|
+
data.tar.gz: 4b35be840c0e8a63730a5627e36efb53e19ba4fdae5d106544d37a8bf325bf6f6c35a90d3aafedf2ce0f84fb117e45a18b24ddfd0d1cd0842dc93a35fec81a54
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Piotr Murach
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Benchmark::Malloc
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/benchmark-malloc.svg)][gem]
|
4
|
+
[![Build Status](https://secure.travis-ci.org/piotrmurach/benchmark-malloc.svg?branch=master)][travis]
|
5
|
+
[![Build status](https://ci.appveyor.com/api/projects/status/cp102e33c2a7fx83?svg=true)][appveyor]
|
6
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/d8fbd4a0423fd78d8bee/maintainability)][codeclimate]
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/github/piotrmurach/benchmark-malloc/badge.svg?branch=master)][coverage]
|
8
|
+
[![Inline docs](http://inch-ci.org/github/piotrmurach/benchmark-malloc.svg?branch=master)][inchpages]
|
9
|
+
|
10
|
+
[gem]: http://badge.fury.io/rb/benchmark-malloc
|
11
|
+
[travis]: http://travis-ci.org/piotrmurach/benchmark-malloc
|
12
|
+
[appveyor]: https://ci.appveyor.com/project/piotrmurach/benchmark-malloc
|
13
|
+
[codeclimate]: https://codeclimate.com/github/piotrmurach/benchmark-malloc/maintainability
|
14
|
+
[coverage]: https://coveralls.io/github/piotrmurach/benchmark-malloc?branch=master
|
15
|
+
[inchpages]: http://inch-ci.org/github/piotrmurach/benchmark-malloc
|
16
|
+
|
17
|
+
> Trace memory allocations and collect stats.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'benchmark-malloc'
|
25
|
+
```
|
26
|
+
|
27
|
+
And then execute:
|
28
|
+
|
29
|
+
$ bundle
|
30
|
+
|
31
|
+
Or install it yourself as:
|
32
|
+
|
33
|
+
$ gem install benchmark-malloc
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
benc_malloc = Benchmark::Malloc.new
|
39
|
+
|
40
|
+
stats = bench_malloc.run { %w[foo bar baz].sort[1] }
|
41
|
+
|
42
|
+
stats.allocated.total_objects # => 3
|
43
|
+
|
44
|
+
stats.allocated.total_memory # => 120
|
45
|
+
```
|
46
|
+
|
47
|
+
## Development
|
48
|
+
|
49
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
50
|
+
|
51
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/benchmark-malloc. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
56
|
+
|
57
|
+
## License
|
58
|
+
|
59
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
60
|
+
|
61
|
+
## Code of Conduct
|
62
|
+
|
63
|
+
Everyone interacting in the Benchmark::Malloc project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/piotrmurach/benchmark-malloc/blob/master/CODE_OF_CONDUCT.md).
|
64
|
+
|
65
|
+
## Copyright
|
66
|
+
|
67
|
+
Copyright (c) 2019 Piotr Murach. See LICENSE for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "benchmark/malloc/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "benchmark-malloc"
|
7
|
+
spec.version = Benchmark::Malloc::VERSION
|
8
|
+
spec.authors = ["Piotr Murach"]
|
9
|
+
spec.email = ["me@piotrmurach.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Trace memory allocations and collect stats.}
|
12
|
+
spec.description = %q{Trace memory allocations and collect stats.}
|
13
|
+
spec.homepage = "https://github.com/piotrmurach/benchmark-malloc"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
if spec.respond_to?(:metadata)
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/piotrmurach/benchmark-malloc"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/piotrmurach/benchmark-malloc/CHANGELOG.md"
|
20
|
+
end
|
21
|
+
|
22
|
+
spec.files = Dir['{lib,spec}/**/*.rb']
|
23
|
+
spec.files += Dir['tasks/*', 'benchmark-malloc.gemspec']
|
24
|
+
spec.files += Dir['README.md', 'CHANGELOG.md', 'LICENSE.txt', 'Rakefile']
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.required_ruby_version = '>= 2.1.0'
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", ">= 1.17"
|
32
|
+
spec.add_development_dependency "rake"
|
33
|
+
spec.add_development_dependency "rspec", ">= 3.0"
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'benchmark/malloc'
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'objspace'
|
4
|
+
|
5
|
+
require_relative 'malloc/allocation'
|
6
|
+
require_relative 'malloc/allocation_set'
|
7
|
+
require_relative 'malloc/allocation_result'
|
8
|
+
require_relative 'malloc/version'
|
9
|
+
|
10
|
+
module Benchmark
|
11
|
+
class Malloc
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
attr_reader :generation
|
15
|
+
|
16
|
+
# It runs Ruby VM before tracing object allocations
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
attr_reader :warmup
|
20
|
+
|
21
|
+
def self.run(&work)
|
22
|
+
Malloc.new.run(&work)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(warmup: 0)
|
26
|
+
@warmup = warmup
|
27
|
+
@running = false
|
28
|
+
@alloc_path = ::File.join(__FILE__[0...-3], 'allocation.rb')
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api private
|
32
|
+
def check_running
|
33
|
+
unless @running
|
34
|
+
raise Error, "not started yet"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Start allocation tracing
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# malloc = Malloc.new
|
42
|
+
# malloc.start
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def start
|
46
|
+
if @running
|
47
|
+
raise Error, "already running"
|
48
|
+
end
|
49
|
+
|
50
|
+
GC.start
|
51
|
+
GC.disable
|
52
|
+
@generation = GC.count
|
53
|
+
@running = true
|
54
|
+
@existing_ids = []
|
55
|
+
ObjectSpace.each_object do |object|
|
56
|
+
@existing_ids << object.__id__
|
57
|
+
end
|
58
|
+
ObjectSpace.trace_object_allocations_start
|
59
|
+
end
|
60
|
+
|
61
|
+
# Stop allocation tracing if currently running
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# Malloc.stop
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def stop
|
68
|
+
check_running
|
69
|
+
|
70
|
+
ObjectSpace.trace_object_allocations_stop
|
71
|
+
allocated = collect_allocations
|
72
|
+
retained = []
|
73
|
+
@running = false
|
74
|
+
GC.enable
|
75
|
+
GC.start
|
76
|
+
|
77
|
+
ObjectSpace.each_object do |object|
|
78
|
+
next unless ObjectSpace.allocation_generation(object) == generation
|
79
|
+
next unless allocated.key?(object.__id__)
|
80
|
+
retained << allocated[object.__id__]
|
81
|
+
end
|
82
|
+
|
83
|
+
ObjectSpace.trace_object_allocations_clear
|
84
|
+
|
85
|
+
AllocationResult.new(AllocationSet.new(allocated.values),
|
86
|
+
AllocationSet.new(retained))
|
87
|
+
end
|
88
|
+
|
89
|
+
# Gather allocation stats of Ruby code inside of the block
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# malloc = Malloc.new
|
93
|
+
# malloc.run { ... }
|
94
|
+
#
|
95
|
+
# @return [Malloc::Result]
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
def run(&work)
|
99
|
+
start
|
100
|
+
warmup.times { yield }
|
101
|
+
|
102
|
+
begin
|
103
|
+
yield
|
104
|
+
rescue Exception
|
105
|
+
ObjectSpace.trace_object_allocations_stop
|
106
|
+
GC.enable
|
107
|
+
raise
|
108
|
+
else
|
109
|
+
stop
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# @api private
|
116
|
+
def collect_allocations
|
117
|
+
allocations = {}
|
118
|
+
ObjectSpace.each_object do |object|
|
119
|
+
next unless ObjectSpace.allocation_generation(object) == generation
|
120
|
+
next if ObjectSpace.allocation_sourcefile(object).nil?
|
121
|
+
next if ObjectSpace.allocation_sourcefile(object) == __FILE__
|
122
|
+
next if ObjectSpace.allocation_sourcefile(object) == @alloc_path
|
123
|
+
next if @existing_ids.include?(object.__id__)
|
124
|
+
|
125
|
+
allocations[object.__id__] = Allocation.new(object)
|
126
|
+
end
|
127
|
+
allocations
|
128
|
+
end
|
129
|
+
end # Malloc
|
130
|
+
end # Benchmark
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'objspace'
|
4
|
+
|
5
|
+
module Benchmark
|
6
|
+
class Malloc
|
7
|
+
class Allocation
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
# The allocated object
|
11
|
+
attr_reader :object
|
12
|
+
|
13
|
+
# The allocated object memory size
|
14
|
+
attr_reader :memsize
|
15
|
+
|
16
|
+
attr_reader :class_path
|
17
|
+
|
18
|
+
attr_reader :source_file
|
19
|
+
|
20
|
+
attr_reader :source_line
|
21
|
+
|
22
|
+
attr_reader :method_id
|
23
|
+
|
24
|
+
def initialize(object)
|
25
|
+
@object = object
|
26
|
+
@memsize = ObjectSpace.memsize_of(object)
|
27
|
+
@class_path = ObjectSpace.allocation_class_path(object)
|
28
|
+
@source_file = ObjectSpace.allocation_sourcefile(object)
|
29
|
+
@source_line = ObjectSpace.allocation_sourceline(object)
|
30
|
+
@method_id = ObjectSpace.allocation_method_id(object)
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract(*attributes)
|
34
|
+
attributes.map do |attr|
|
35
|
+
if @object.respond_to?(attr)
|
36
|
+
@object.public_send(attr)
|
37
|
+
else
|
38
|
+
public_send(attr)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def <=>(other)
|
44
|
+
@object <=> other.object &&
|
45
|
+
@memsize <=> other.memsize
|
46
|
+
end
|
47
|
+
end # Allocation
|
48
|
+
end # Malloc
|
49
|
+
end # Benchmark
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Benchmark
|
4
|
+
class Malloc
|
5
|
+
class AllocationResult
|
6
|
+
attr_reader :allocated
|
7
|
+
|
8
|
+
attr_reader :retained
|
9
|
+
|
10
|
+
def initialize(allocated, retained)
|
11
|
+
@allocated = allocated
|
12
|
+
@retained = retained
|
13
|
+
end
|
14
|
+
|
15
|
+
def total_allocated_objects
|
16
|
+
@allocated.total_objects
|
17
|
+
end
|
18
|
+
|
19
|
+
def total_retained_objects
|
20
|
+
@retained.total_objects
|
21
|
+
end
|
22
|
+
end # AllocationResult
|
23
|
+
end # Malloc
|
24
|
+
end # Benchmark
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Benchmark
|
4
|
+
class Malloc
|
5
|
+
class AllocationSet
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :allocations
|
9
|
+
|
10
|
+
def initialize(allocations)
|
11
|
+
@allocations = allocations
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
return to_enum(:each) unless block
|
16
|
+
@allocations.each(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api public
|
20
|
+
def total_objects
|
21
|
+
@allocations.size
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
def total_memory
|
26
|
+
@allocations.reduce(0) { |acc, alloc| acc + alloc.memsize }
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api public
|
30
|
+
def count_objects
|
31
|
+
@allocations.
|
32
|
+
map { |alloc| alloc.object.class }.
|
33
|
+
each_with_object(Hash.new(0)) { |name, h| h[name] += 1 }
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api public
|
37
|
+
def count_memory
|
38
|
+
@allocations.
|
39
|
+
map { |alloc| [alloc.object.class, alloc.memsize] }.
|
40
|
+
each_with_object(Hash.new(0)) { |(name, mem), h| h[name] += mem }
|
41
|
+
end
|
42
|
+
|
43
|
+
def filter(*class_names)
|
44
|
+
@allocations.
|
45
|
+
select { |alloc| class_names.include?(alloc.object.class) }
|
46
|
+
end
|
47
|
+
end # AllocationSet
|
48
|
+
end # Malloc
|
49
|
+
end # Benchmark
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if ENV['COVERAGE'] || ENV['TRAVIS']
|
4
|
+
require 'simplecov'
|
5
|
+
require 'coveralls'
|
6
|
+
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
Coveralls::SimpleCov::Formatter
|
10
|
+
]
|
11
|
+
|
12
|
+
SimpleCov.start do
|
13
|
+
command_name 'spec'
|
14
|
+
add_filter 'spec'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require "bundler/setup"
|
19
|
+
require "benchmark-malloc"
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
# Enable flags like --only-failures and --next-failure
|
23
|
+
config.example_status_persistence_file_path = ".rspec_status"
|
24
|
+
|
25
|
+
# Disable RSpec exposing methods globally on `Module` and `main`
|
26
|
+
config.disable_monkey_patching!
|
27
|
+
|
28
|
+
config.expect_with :rspec do |c|
|
29
|
+
c.syntax = :expect
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Benchmark::Malloc::Allocation do
|
4
|
+
it "gathers info about allocated object" do
|
5
|
+
object = Object.new
|
6
|
+
alloc = described_class.new(object)
|
7
|
+
|
8
|
+
expect(alloc.memsize).to be <= 40
|
9
|
+
expect(alloc.source_line).to eq(nil)
|
10
|
+
expect(alloc.method_id).to eq(nil)
|
11
|
+
expect(alloc.class_path).to eq(nil)
|
12
|
+
expect(alloc.source_file).to eq(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "extracts allocation info" do
|
16
|
+
object = Object.new
|
17
|
+
alloc = described_class.new(object)
|
18
|
+
|
19
|
+
extracted = alloc.extract(:class, :memsize)
|
20
|
+
|
21
|
+
expect(extracted[0]).to eq(Object)
|
22
|
+
expect(extracted[1]).to be <= 40
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Benchmark::Malloc::AllocationSet do
|
4
|
+
|
5
|
+
def make_allocation(object)
|
6
|
+
Benchmark::Malloc::Allocation.new(object)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "counts allocated objects" do
|
10
|
+
object_alloc = make_allocation(Object.new)
|
11
|
+
hash_alloc = make_allocation({Object.new => :foo})
|
12
|
+
string_alloc = make_allocation(:bar)
|
13
|
+
|
14
|
+
allocations = [object_alloc, hash_alloc, string_alloc]
|
15
|
+
alloc_set = described_class.new(allocations)
|
16
|
+
|
17
|
+
expect(alloc_set.count_objects).to eq({Hash => 1, Object => 1, Symbol => 1})
|
18
|
+
expect(alloc_set.total_objects).to eq(3)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "counts allocated memory" do
|
22
|
+
object_alloc = make_allocation(Object.new)
|
23
|
+
hash_alloc = make_allocation({Object.new => :foo})
|
24
|
+
string_alloc = make_allocation(:bar)
|
25
|
+
|
26
|
+
allocations = [object_alloc, hash_alloc, string_alloc]
|
27
|
+
alloc_set = described_class.new(allocations)
|
28
|
+
|
29
|
+
expect(alloc_set.count_memory[Hash]).to be <= 240
|
30
|
+
expect(alloc_set.count_memory[Object]).to be <= 40
|
31
|
+
expect(alloc_set.count_memory[Symbol]).to eq 0
|
32
|
+
|
33
|
+
expect(alloc_set.total_memory).to be < 300
|
34
|
+
end
|
35
|
+
|
36
|
+
it "filters allocated objects" do
|
37
|
+
object_alloc = make_allocation(Object.new)
|
38
|
+
hash_alloc = make_allocation({Object.new => :foo})
|
39
|
+
string_alloc = make_allocation(:bar)
|
40
|
+
|
41
|
+
allocations = [object_alloc, hash_alloc, string_alloc]
|
42
|
+
alloc_set = described_class.new(allocations)
|
43
|
+
|
44
|
+
expect(alloc_set.filter(Object)).to eq([object_alloc])
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Benchmark::Malloc do
|
4
|
+
it "traces only new object allocations" do
|
5
|
+
%i[foo bar baz].freeze
|
6
|
+
|
7
|
+
sample = described_class.run do
|
8
|
+
%i[foo bar baz].freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
expect(sample.allocated.total_objects).to eq(1)
|
12
|
+
expect(sample.allocated.total_memory).to be <= 40
|
13
|
+
end
|
14
|
+
|
15
|
+
it "traces block assigned instances" do
|
16
|
+
memory = described_class.new
|
17
|
+
|
18
|
+
sample = memory.run do
|
19
|
+
_new_object = Object.new
|
20
|
+
_new_array = [:baz]
|
21
|
+
_new_string = 'foo' + 'baz'
|
22
|
+
end
|
23
|
+
|
24
|
+
# allocated
|
25
|
+
expect(sample.allocated.total_objects).to be <= 5
|
26
|
+
expect(sample.allocated.total_memory).to be <= 200
|
27
|
+
|
28
|
+
expect(sample.allocated.count_objects[Object]).to eq(1)
|
29
|
+
expect(sample.allocated.count_objects[Array]).to eq(1)
|
30
|
+
expect(sample.allocated.count_objects[String]).to be <= 3
|
31
|
+
|
32
|
+
expect(sample.allocated.count_memory[Object]).to be <= 40
|
33
|
+
expect(sample.allocated.count_memory[String]).to be <= 120
|
34
|
+
expect(sample.allocated.count_memory[Array]).to be <= 40
|
35
|
+
|
36
|
+
# retained
|
37
|
+
expect(sample.retained.total_objects).to be <= 5
|
38
|
+
expect(sample.retained.total_memory).to be <= 200
|
39
|
+
|
40
|
+
expect(sample.retained.count_objects[Object]).to eq(1)
|
41
|
+
expect(sample.retained.count_objects[Array]).to eq(1)
|
42
|
+
expect(sample.retained.count_objects[String]).to be <= 3
|
43
|
+
|
44
|
+
expect(sample.retained.count_memory[Object]).to be <= 40
|
45
|
+
expect(sample.retained.count_memory[String]).to be <= 120
|
46
|
+
expect(sample.retained.count_memory[Array]).to be <= 40
|
47
|
+
end
|
48
|
+
|
49
|
+
it "traces large number of objects" do
|
50
|
+
result = described_class.run do
|
51
|
+
10.times { |i| [i.to_s, {}] }
|
52
|
+
end
|
53
|
+
|
54
|
+
expect(result.allocated.total_objects).to eq(10 * 3)
|
55
|
+
expect(result.allocated.total_memory).to be <= (3120)
|
56
|
+
expect(result.allocated.count_objects).to eq({
|
57
|
+
Array => 10, String => 10, Hash => 10})
|
58
|
+
|
59
|
+
# memory
|
60
|
+
expect(result.allocated.count_memory[Array]).to be <= 400
|
61
|
+
expect(result.allocated.count_memory[String]).to be <= 400
|
62
|
+
expect(result.allocated.count_memory[Hash]).to be <= 2400
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises when stopped without starting" do
|
66
|
+
expect {
|
67
|
+
described_class.new.stop
|
68
|
+
}.to raise_error(Benchmark::Malloc::Error, "not started yet")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "raises when started again" do
|
72
|
+
expect {
|
73
|
+
malloc = described_class.new
|
74
|
+
malloc.start
|
75
|
+
malloc.start
|
76
|
+
}.to raise_error(Benchmark::Malloc::Error, "already running")
|
77
|
+
end
|
78
|
+
end
|
data/tasks/console.rake
ADDED
data/tasks/coverage.rake
ADDED
data/tasks/spec.rake
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc 'Run all specs'
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
8
|
+
task.pattern = 'spec/{unit,integration}{,/*/**}/*_spec.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :spec do
|
12
|
+
desc 'Run unit specs'
|
13
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
14
|
+
task.pattern = 'spec/unit{,/*/**}/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run integration specs'
|
18
|
+
RSpec::Core::RakeTask.new(:integration) do |task|
|
19
|
+
task.pattern = 'spec/integration{,/*/**}/*_spec.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Run performance specs'
|
23
|
+
RSpec::Core::RakeTask.new(:perf) do |task|
|
24
|
+
task.pattern = 'spec/performance{,/*/**}/*_spec.rb'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
rescue LoadError
|
29
|
+
%w[spec spec:unit spec:integration spec:perf].each do |name|
|
30
|
+
task name do
|
31
|
+
$stderr.puts "In order to run #{name}, do `gem install rspec`"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: benchmark-malloc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Piotr Murach
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: Trace memory allocations and collect stats.
|
56
|
+
email:
|
57
|
+
- me@piotrmurach.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- CHANGELOG.md
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- benchmark-malloc.gemspec
|
67
|
+
- lib/benchmark-malloc.rb
|
68
|
+
- lib/benchmark/malloc.rb
|
69
|
+
- lib/benchmark/malloc/allocation.rb
|
70
|
+
- lib/benchmark/malloc/allocation_result.rb
|
71
|
+
- lib/benchmark/malloc/allocation_set.rb
|
72
|
+
- lib/benchmark/malloc/version.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/unit/allocation/new_spec.rb
|
75
|
+
- spec/unit/allocation_set/new_spec.rb
|
76
|
+
- spec/unit/run_spec.rb
|
77
|
+
- tasks/console.rake
|
78
|
+
- tasks/coverage.rake
|
79
|
+
- tasks/spec.rake
|
80
|
+
homepage: https://github.com/piotrmurach/benchmark-malloc
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata:
|
84
|
+
homepage_uri: https://github.com/piotrmurach/benchmark-malloc
|
85
|
+
source_code_uri: https://github.com/piotrmurach/benchmark-malloc
|
86
|
+
changelog_uri: https://github.com/piotrmurach/benchmark-malloc/CHANGELOG.md
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 2.1.0
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubygems_version: 3.0.3
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Trace memory allocations and collect stats.
|
106
|
+
test_files: []
|