pax_global_header00006660000000000000000000000064130437705730014523gustar00rootroot0000000000000052 comment=05c10b16cae9fd08ccc8f3abb5ede13b93b12865 benchmark-memory-0.1.2/000077500000000000000000000000001304377057300147635ustar00rootroot00000000000000benchmark-memory-0.1.2/.codeclimate.yml000066400000000000000000000003411304377057300200330ustar00rootroot00000000000000--- engines: duplication: enabled: true config: languages: - ruby reek: enabled: true rubocop: enabled: true ratings: paths: - Gemfile.lock - "**.rb" exclude_paths: - spec/**/* benchmark-memory-0.1.2/.gitignore000066400000000000000000000001271304377057300167530ustar00rootroot00000000000000/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ benchmark-memory-0.1.2/.hound.yml000066400000000000000000000000621304377057300166770ustar00rootroot00000000000000ruby: enabled: true config_file: .rubocop.yml benchmark-memory-0.1.2/.rspec000066400000000000000000000000101304377057300160670ustar00rootroot00000000000000--color benchmark-memory-0.1.2/.rubocop.yml000066400000000000000000000203031304377057300172330ustar00rootroot00000000000000AllCops: Include: - "Gemfile" - "Guardfile" - "Rakefile" - "*.gemspec" Exclude: - "vendor/**/*" - "db/schema.rb" TargetRubyVersion: 2.1 Style/AccessorMethodName: Description: Check the naming of accessor methods for get_/set_. Enabled: false Style/Alias: Description: Use alias_method instead of alias. StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method Enabled: false Style/Attr: Description: Checks for uses of Module#attr. StyleGuide: https://github.com/bbatsov/ruby-style-guide#attr Enabled: false Style/CollectionMethods: Description: Preferred collection methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size Enabled: true PreferredMethods: collect: map collect!: map! find: detect find_all: select inject: reduce Style/Documentation: Description: Document classes and non-namespace modules. Enabled: false Style/DotPosition: Description: Checks the position of the dot in multi-line method calls. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains Enabled: true EnforcedStyle: trailing SupportedStyles: - leading - trailing Style/DoubleNegation: Description: Checks for uses of double negation (!!). StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang Enabled: false Style/EachWithObject: Description: Prefer `each_with_object` over `inject` or `reduce`. Enabled: false Style/EmptyLiteral: Description: Prefer literals to Array.new/Hash.new/String.new. StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash Enabled: false Style/FileName: Description: Use snake_case for source file names. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files Enabled: true Exclude: [] Style/GuardClause: Description: Check for conditionals that can be replaced with guard clauses StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals Enabled: false MinBodyLength: 1 Style/HashSyntax: Description: Check the style of Hash syntax. Enabled: true EnforcedStyle: hash_rockets SupportedStyles: - hash_rockets - ruby19 - ruby19_no_mixed_keys Style/IfUnlessModifier: Description: Favor modifier if/unless usage when you have a single-line body. StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier Enabled: false MaxLineLength: 80 Style/InlineComment: Description: Avoid inline comments. Enabled: false Style/ModuleFunction: Description: Checks for usage of `extend self` in modules. StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function Enabled: false Style/OneLineConditional: Description: Favor the ternary operator(?:) over if/then/else/end constructs. StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator Enabled: false Style/OptionHash: Description: Do not use option hashes when you can use keyword arguments. Enabled: false Style/PercentLiteralDelimiters: Description: Use `%`-literal delimiters consistently StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces Enabled: false PreferredDelimiters: "%": "()" "%i": "()" "%q": "()" "%Q": "()" "%r": "{}" "%s": "()" "%w": "()" "%W": "()" "%x": "()" Style/PerlBackrefs: Description: Avoid Perl-style regex back references. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers Enabled: false Style/PredicateName: Description: Check the names of predicate methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark Enabled: true NamePrefix: - is_ - has_ - have_ NamePrefixBlacklist: - is_ Exclude: - spec/**/* Style/RaiseArgs: Description: Checks the arguments passed to raise/fail. StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages Enabled: true EnforcedStyle: exploded SupportedStyles: - compact - exploded Style/Send: Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send` may overlap with existing methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-public-send Enabled: false Style/SignalException: Description: Checks for proper usage of fail and raise. StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method Enabled: true EnforcedStyle: semantic SupportedStyles: - only_raise - only_fail - semantic Style/SingleLineBlockParams: Description: Enforces the names of some block params. StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks Enabled: false Methods: - reduce: - a - e - inject: - a - e Style/SingleLineMethods: Description: Avoid single-line methods. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods Enabled: false AllowIfMethodIsEmpty: true Style/SpaceInsideHashLiteralBraces: EnforcedStyle: no_space EnforcedStyleForEmptyBraces: no_space SupportedStyles: - space - no_space Style/SpecialGlobalVars: Description: Avoid Perl-style global variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms Enabled: false Style/StringLiterals: Description: Checks if uses of quotes match the configured preference. StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals Enabled: true EnforcedStyle: double_quotes SupportedStyles: - single_quotes - double_quotes Style/StringLiteralsInInterpolation: Description: Checks if uses of quotes inside expressions in interpolated strings match the configured preference. Enabled: true EnforcedStyle: single_quotes SupportedStyles: - single_quotes - double_quotes Style/TrailingCommaInLiteral: Description: Checks for trailing comma in parameter lists and literals. StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas Enabled: true EnforcedStyleForMultiline: comma SupportedStyles: - comma - no_comma Style/VariableInterpolation: Description: Do not interpolate global, instance and class variables directly in strings. StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate Enabled: false Style/WhenThen: Description: Use when x then ... for one-line cases. StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases Enabled: false Lint/AssignmentInCondition: Description: Do not use assignment in conditions. StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition Enabled: false AllowSafeAssignment: true Lint/EachWithObjectArgument: Description: Check for immutable argument given to each_with_object. Enabled: true Lint/HandleExceptions: Description: Do not suppress exception. StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions Enabled: false Lint/LiteralInCondition: Description: Checks of literals used in conditions. Enabled: false Lint/LiteralInInterpolation: Description: Checks for literals used in interpolation. Enabled: false Metrics/AbcSize: Description: A calculated magnitude based on number of assignments, branches, and conditions. Enabled: false Max: 15 Metrics/ClassLength: Description: Avoid classes longer than 100 lines of code. Enabled: false CountComments: false Max: 100 Metrics/ModuleLength: CountComments: false Max: 100 Description: Avoid modules longer than 100 lines of code. Enabled: false Metrics/CyclomaticComplexity: Description: A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Enabled: false Max: 6 Metrics/MethodLength: Description: Avoid methods longer than 10 lines of code. StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods Enabled: false CountComments: false Max: 10 Metrics/ParameterLists: Description: Avoid parameter lists longer than three or four parameters. StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params Enabled: false Max: 5 CountKeywordArgs: true Metrics/PerceivedComplexity: Description: A complexity metric geared towards measuring complexity for a human reader. Enabled: false Max: 7 benchmark-memory-0.1.2/.travis.yml000066400000000000000000000007031304377057300170740ustar00rootroot00000000000000cache: bundler language: ruby rvm: - 2.1.10 - 2.2.6 - 2.3.3 - ruby-head script: bundle exec rspec sudo: false before_install: gem install bundler -v 1.14.3 bundler_args: --without development matrix: allow_failures: - rvm: ruby-head include: - rvm: 2.4.0 after_script: - bundle exec codeclimate-test-reporter addons: code_climate: repo_token: 36f3969bdc1e5e821f38411648b9ba4217fa4518c9f58c30f0e9b0d1639b4f19 benchmark-memory-0.1.2/.yardopts000066400000000000000000000001621304377057300166300ustar00rootroot00000000000000--no-private --protected --markup markdown - CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md LICENSE.md README.md benchmark-memory-0.1.2/CHANGELOG.md000066400000000000000000000017631304377057300166030ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning 2.0.0][semver]. Any violations of this scheme are considered to be bugs. [semver]: http://semver.org/spec/v2.0.0.html ## [0.1.2] - 2017-01-30 ### Fixed - [#4](https://github.com/michaelherold/benchmark-memory/pull/4): Fix a small bug when StringIO wasn't properly required - [@rzane](https://github.com/rzane). ## [0.1.1] - 2016-06-10 ### Fixed - Printing comparisons for multiple entries. ## [0.1.0] - 2016-05-18 ### Added - Main `Benchmark.memory` method. - Holding results between invocations for measuring implementations on different versions of Ruby or different versions of libraries. - Quiet mode, with no command line output. [0.1.2]: https://github.com/michaelherold/benchmark-memory/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/michaelherold/benchmark-memory/compare/v0.1.0...v0.1.1 [0.1.0]: https://github.com/michaelherold/benchmark-memory/tree/v0.1.0 benchmark-memory-0.1.2/CODE_OF_CONDUCT.md000066400000000000000000000045321304377057300175660ustar00rootroot00000000000000# Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at michael.j.herold@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [http://contributor-covenant.org/version/1/3/0/][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/3/0/benchmark-memory-0.1.2/CONTRIBUTING.md000066400000000000000000000057761304377057300172330ustar00rootroot00000000000000# Contributing In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project. Here are some ways *you* can contribute: * Use alpha, beta, and pre-release versions. * Report bugs. * Suggest new features. * Write or edit documentation. * Write specifications. * Write code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace). * Refactor code. * Fix [issues][]. * Review patches. [issues]: https://github.com/michaelherold/benchmark-memory/issues ## Submitting an Issue We use the [GitHub issue tracker][issues] to track bugs and features. Before submitting a bug report or feature request, check to make sure it hasn't already been submitted. When submitting a bug report, please include a [Gist](https://gist.github.com) that includes a stack trace and any details that may be necessary to reproduce the bug, including your gem version, Ruby version, and operating system. Ideally, a bug report should include a pull request with failing specs. ## Submitting a Pull Request 1. [Fork the repository](http://help.github.com/fork-a-repo/). 2. [Create a topic branch](http://learn.github.com/p/branching.html). 3. Add specs for your unimplemented feature or bug fix. 4. Run `bundle exec rake spec`. If your specs pass, return to step 3. 5. Implement your feature or bug fix. 6. Run `bundle exec rake`. If your specs or any of the linters fail, return to step 5. 7. Open `coverage/index.html`. If your changes are not completely covered by your tests, return to step 3. 8. Add documentation for your feature or bug fix. 9. Run `bundle exec inch`. If your changes are below a B in documentation, go back to step 8. 10. Commit and push your changes. 11. [Submit a pull request](http://help.github.com/send-pull-requests/). ## Tools to help you succeed 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. When writing code, you can use the helper application [Guard][guard] to automatically run tests and coverage tools whenever you modify and save a file. This helps to eliminate the tedium of running tests manually and reduces the chance that you will accidentally forget to run the tests. To use Guard, run `bundle exec guard`. Before committing code, run `rake` to check that the code conforms to the style guidelines of the project, that all of the tests are green (if you're writing a feature; if you're only submitting a failing test, then it does not have to pass!), and that the changes are sufficiently documented. 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][rubygems]. [guard]: http://guardgem.org [rubygems]: https://rubygems.org benchmark-memory-0.1.2/Gemfile000066400000000000000000000006571304377057300162660ustar00rootroot00000000000000source "https://rubygems.org" gemspec group :development do gem "guard" gem "guard-bundler" gem "guard-inch" gem "guard-rspec", "~> 4.6" gem "guard-rubocop" gem "inch" gem "mutant-rspec" gem "rake", "~> 10" gem "rubocop", "0.40.0" gem "yard", "~> 0.8" group :test do gem "pry" end end group :test do gem "codeclimate-test-reporter", :require => false gem "rspec", "~> 3.4" gem "simplecov" end benchmark-memory-0.1.2/Guardfile000066400000000000000000000006311304377057300166100ustar00rootroot00000000000000guard :bundler do watch("Gemfile") watch("interactor-contracts.gemspec") end guard :inch do watch(/.+\.rb/) end guard :rspec, :cmd => "bundle exec rspec" do watch("spec/spec_helper.rb") { "spec" } watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } end guard :rubocop do watch(/.+\.rb$/) watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) } end benchmark-memory-0.1.2/LICENSE.md000066400000000000000000000020711304377057300163670ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Michael Herold Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. benchmark-memory-0.1.2/README.md000066400000000000000000000201251304377057300162420ustar00rootroot00000000000000# benchmark-memory [![Build Status](https://travis-ci.org/michaelherold/benchmark-memory.svg)][travis] [![Code Climate](https://codeclimate.com/github/michaelherold/benchmark-memory/badges/gpa.svg)][codeclimate] [![Inline docs](http://inch-ci.org/github/michaelherold/benchmark-memory.svg?branch=master)][inch] [codeclimate]: https://codeclimate.com/github/michaelherold/benchmark-memory [inch]: http://inch-ci.org/github/michaelherold/benchmark-memory [travis]: https://travis-ci.org/michaelherold/benchmark-memory benchmark-memory is a tool that helps you to benchmark the memory usage of different pieces of code. It leverages the power of [memory_profiler] to give you a metric of the total amount of memory allocated and retained by a block, as well as the number of objects and strings allocated and retained. [memory_profiler]: https://github.com/SamSaffron/memory_profiler ## Installation Add this line to your application's Gemfile: ```ruby gem "benchmark-memory" ``` And then execute: $ bundle Or install it yourself as: $ gem install benchmark-memory ## Usage Following the examples of the built-in Benchmark and Evan Phoenix's [benchmark-ips], the most common way of using benchmark-memory is through the `Benchmark.memory` wrapper. An example might look like this: ```ruby require "benchmark/memory" # First method under test def allocate_string "this string was dynamically allocated" end # Second method under test def give_frozen_string "this string is frozen".freeze end Benchmark.memory do |x| x.report("dynamic allocation") { allocate_string } x.report("frozen string") { give_frozen_string } x.compare! end ``` This example tests two methods that are defined inline. Note that you don't have to define them inline; you can just as easily use a method that you require before the benchmark or anything else that you can place in a block. When you run this example, you see the difference between the two reports: ```txt Calculating ------------------------------------- dynamic allocation 40.000 memsize ( 0.000 retained) 1.000 objects ( 0.000 retained) 1.000 strings ( 0.000 retained) frozen string 0.000 memsize ( 0.000 retained) 0.000 objects ( 0.000 retained) 0.000 strings ( 0.000 retained) Comparison: frozen string: 0 allocated dynamic allocation: 40 allocated - Infx more ``` Reading this output shows that the "dynamic allocation" example allocates one string that is not retained outside the scope of the block. The "frozen string" example, however, does not allocate anything because it reuses the frozen string that was created during the method definition. [benchmark-ips]: https://github.com/evanphx/benchmark-ips ## Options There are several options available when running a memory benchmark. ### Suppress all output (Quiet Mode) ```ruby Benchmark.memory(:quiet => true) ``` Passing a `:quiet` flag to the `Benchmark.memory` method suppresses the output of the benchmark. You might find this useful if you want to run a benchmark as part of your test suite, where outputting to `STDOUT` would be disruptive. ### Enable comparison ```ruby Benchmark.memory do |x| x.compare! end ``` Calling `#compare!` on the job within the setup block of `Benchmark.memory` enables the output of the comparison section of the benchmark. Without it, this section is suppressed and you only get the raw numbers output during calculation. ### Hold results between invocations ```ruby Benchmark.memory do |x| x.hold!("benchmark_results.json") end ``` Often when you want to benchmark something, you compare two implementations of the same method. This is cumbersome because you have to keep two implementations side-by-side and call them in the same manner. Alternatively, you may want to compare how a method performs on two different versions of Ruby. To make both of these scenarios easier, you can enable "holding" on the benchmark. By calling `#hold!` on the benchmark, you enable the benchmark to write to the given file to store its results in a file that can be read in between invocations of your benchmark. For example, imagine that you have a library that exposes a method called `Statistics.calculate_monthly_recurring_revenue` that you want to optimize for memory usage because it keeps causing your worker server to run out of memory. You make some changes to the method and commit them to an `optimize-memory` branch in Git. To test the two implementations, you could then write this benchmark: ```ruby require "benchmark/memory" require "stats" # require your library file here data = [] # set up the data that it will call here Benchmark.memory do |x| x.report("original") { Stats.monthly_recurring_revenue(data) } x.report("optimized") { Stats.monthly_recurring_revenue(data) } x.compare! x.hold("bm_recurring_revenue.json") end ``` Note that the method calls are the same for both tests and that we have enabled result holding in the "bm_recurring_revenue.json" file. You could then run the following (assuming you saved your benchmark as `benchmark_mrr.rb`: ```sh $ git checkout master $ ruby benchmark_mrr.rb $ git checkout optimize-memory $ ruby benchmark_mrr.rb ``` The first invocation of `ruby benchmark_mrr.rb` runs the benchmark in the "original" entry using your code in your `master` Git branch. The second invocation runs the benchmark in the "optimized" entry using the code in your `optimize-memory` Git branch. These two results are collated and then compared to show you the difference between the two. When enabling holding, the benchmark writes to the file passed into the `#hold!` method. After you run all of the entries in the benchmark, the benchmark automatically cleans up its log by deleting the file. ## Supported Ruby Versions This library aims to support and is [tested against][travis] the following Ruby versions: * Ruby 2.1 * Ruby 2.2 * Ruby 2.3 If something doesn't work on one of these versions, it's a bug. This library may inadvertently work (or seem to work) on other Ruby versions, however, support will only be provided for the versions listed above. If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. ## Versioning This library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations of this scheme should be reported as bugs. Specifically, if a minor or patch version is released that breaks backward compatibility, that version should be immediately yanked and/or a new version should be immediately released that restores compatibility. Breaking changes to the public API will only be introduced with new major versions. As a result of this policy, you can (and should) specify a dependency on this gem using the [Pessimistic Version Constraint][pessimistic] with two digits of precision. For example: spec.add_dependency "benchmark-memory", "~> 0.1" [pessimistic]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint [semver]: http://semver.org/spec/v2.0.0.html ## Acknowledgments This library wouldn't be possible without two projects and the people behind them: * Sam Saffron's [memory_profiler] does all of the measurement of the memory allocation and retention in the benchmarks. * I based much of the code around Evan Phoenix's [benchmark-ips] project, since it has a clean base from which to work and a logical organization. I also wanted to go for feature- and DSL-parity with it because I really like the way it works. [benchmark-ips]: https://github.com/evanphx/benchmark-ips [memory_profiler]: https://github.com/SamSaffron/memory_profiler ## License The gem is available as open source under the terms of the [MIT License][license]. [license]: http://opensource.org/licenses/MIT. benchmark-memory-0.1.2/Rakefile000066400000000000000000000010611304377057300164260ustar00rootroot00000000000000require "bundler" Bundler.setup Bundler::GemHelper.install_tasks require "inch/rake" Inch::Rake::Suggest.new(:inch) require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) require "rubocop/rake_task" RuboCop::RakeTask.new(:rubocop) require "yard/rake/yardoc_task" YARD::Rake::YardocTask.new(:yard) task :mutant do command = [ "bundle exec mutant", "--include lib", "--require interactor-contracts", "--use rspec", "Interactor::Contracts*", ].join(" ") system command end task :default => [:spec, :rubocop, :yard, :inch] benchmark-memory-0.1.2/benchmark-memory.gemspec000066400000000000000000000014671304377057300216000ustar00rootroot00000000000000# coding: utf-8 require File.expand_path("../lib/benchmark/memory/version", __FILE__) Gem::Specification.new do |spec| spec.name = "benchmark-memory" spec.version = Benchmark::Memory::VERSION spec.authors = ["Michael Herold"] spec.email = ["michael.j.herold@gmail.com"] spec.summary = "Benchmark-style memory profiling for Ruby 2.1+" spec.description = spec.summary spec.homepage = "https://github.com/michaelherold/benchmark-memory" spec.license = "MIT" spec.files = %w(CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile) spec.files += %w(benchmark-memory.gemspec) spec.files += Dir["lib/**/*.rb"] spec.require_paths = ["lib"] spec.add_dependency "memory_profiler", "~> 0.9" spec.add_development_dependency "bundler", "~> 1.12" end benchmark-memory-0.1.2/bin/000077500000000000000000000000001304377057300155335ustar00rootroot00000000000000benchmark-memory-0.1.2/bin/console000077500000000000000000000005251304377057300171250ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require "benchmark/memory" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start benchmark-memory-0.1.2/bin/setup000077500000000000000000000002031304377057300166140ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here benchmark-memory-0.1.2/lib/000077500000000000000000000000001304377057300155315ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark-memory.rb000066400000000000000000000000741304377057300213170ustar00rootroot00000000000000# rubocop:disable Style/FileName require "benchmark/memory" benchmark-memory-0.1.2/lib/benchmark/000077500000000000000000000000001304377057300174635ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory.rb000066400000000000000000000014411304377057300213200ustar00rootroot00000000000000require "benchmark/memory/errors" require "benchmark/memory/job" require "benchmark/memory/version" # Performance benchmarking library module Benchmark # Benchmark memory usage in code to benchmark different approaches. # @see https://github.com/michaelherold/benchmark-memory module Memory # Measure memory usage in report blocks. # # @param quiet [Boolean] A flag to toggle benchmark output. # # @return [Report] def memory(quiet: false) unless block_given? fail( ConfigurationError, "You did not give a test block to your call to `Benchmark.memory`" ) end job = Job.new(:quiet => quiet) yield job job.run job.run_comparison job.full_report end end extend Benchmark::Memory end benchmark-memory-0.1.2/lib/benchmark/memory/000077500000000000000000000000001304377057300207735ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/errors.rb000066400000000000000000000001731304377057300226350ustar00rootroot00000000000000module Benchmark module Memory Error = Class.new(StandardError) ConfigurationError = Class.new(Error) end end benchmark-memory-0.1.2/lib/benchmark/memory/held_results.rb000066400000000000000000000053331304377057300240210ustar00rootroot00000000000000require "forwardable" require "benchmark/memory/held_results/entry_serializer" module Benchmark module Memory # Collate results that should be held until the next run. class HeldResults extend Forwardable # Instantiate a new set of held results on a path. # # @param path [String, IO] The path to write held results to. def initialize(path = nil) @path = path @results = {} end # @return [String, IO] The path to write held results to. attr_accessor :path # @return [Hash{String => Measurement}] Held results from previous runs. attr_reader :results # Allow Hash-like access to the results without asking for them. def_delegator :@results, :[] # Add a result to the held results. # # @param entry [Report::Entry] The entry to hold. # # @return [void] def add_result(entry) with_hold_file("a") do |file| file.write EntrySerializer.new(entry) file.write "\n" end end # Check whether any results have been stored. # # @return [Boolean] def any? if @path.is_a?(String) File.exist?(@path) else @path.size > 0 # rubocop:disable Style/ZeroLengthPredicate end end # Clean up the results after all results have been collated. # # @return [void] def cleanup if @path.is_a?(String) && File.exist?(@path) File.delete(@path) end end # Check whether to hold results. # # @return [Boolean] def holding? !!@path end # Check whether an entry has been added to the results. # # @param entry [#label] The entry to check. # # @return [Boolean] def include?(entry) holding? && any? && results.key?(entry.label) end # Load results from the serialized output. # # @return [void] def load return unless holding? && any? results = with_hold_file do |file| file.map { |line| EntrySerializer.load(line) } end @results = Hash[results.map do |result| [result.label, result.measurement] end] end private # Execute a block on the hold file. # # @param access_mode [String] The mode to use when opening the file. # @param _block [Proc] The block to execute on each line of the file. # # @return [void] def with_hold_file(access_mode = "r", &_block) return unless @path if @path.is_a?(String) File.open(@path, access_mode) do |f| yield f end else yield @path end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/held_results/000077500000000000000000000000001304377057300234705ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/held_results/entry_serializer.rb000066400000000000000000000016521304377057300274130ustar00rootroot00000000000000require "benchmark/memory/held_results/serializer" require "benchmark/memory/held_results/measurement_serializer" require "benchmark/memory/report/entry" module Benchmark module Memory class HeldResults # Serialize entrys for holding between runs. class EntrySerializer < Serializer # Convert a JSON hash into an Entry. # # @param hash [Hash] A JSON document hash. # # @return [Report::Entry] def load(hash) @object = Report::Entry.new( hash["item"], MeasurementSerializer.load(hash["measurement"]) ) self end # Convert the entry to a Hash. # # @return [Hash] The entry as a Hash. def to_h { :item => object.label, :measurement => MeasurementSerializer.new(object.measurement).to_h, } end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/held_results/measurement_serializer.rb000066400000000000000000000021621304377057300305740ustar00rootroot00000000000000require "benchmark/memory/held_results/serializer" require "benchmark/memory/held_results/metric_serializer" require "benchmark/memory/measurement" module Benchmark module Memory class HeldResults # Serialize measurements for holding between runs. class MeasurementSerializer < Serializer # Convert a JSON hash into a Measurement. # # @param hash [Hash] A JSON document hash. # # @return [Measurement] def load(hash) @object = Measurement.new( :memory => MetricSerializer.load(hash["memory"]), :objects => MetricSerializer.load(hash["objects"]), :strings => MetricSerializer.load(hash["strings"]) ) self end # Convert the measurement to a Hash. # # @return [Hash] The measurement as a Hash. def to_h { :memory => MetricSerializer.new(object.memory).to_h, :objects => MetricSerializer.new(object.objects).to_h, :strings => MetricSerializer.new(object.strings).to_h, } end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/held_results/metric_serializer.rb000066400000000000000000000016171304377057300275360ustar00rootroot00000000000000require "benchmark/memory/held_results/serializer" require "benchmark/memory/measurement/metric" module Benchmark module Memory class HeldResults # Serialize metrics for holding between runs. class MetricSerializer < Serializer # Convert a JSON hash into a Metric. # # @param hash [Hash] A JSON document hash. # # @return [Measurement::Metric] # def load(hash) @object = Measurement::Metric.new( hash["type"], hash["allocated"], hash["retained"] ) self end # Convert the metric to a Hash. # # @return [Hash] The metric as a Hash. def to_h { :allocated => object.allocated, :retained => object.retained, :type => object.type, } end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/held_results/serializer.rb000066400000000000000000000033171304377057300261720ustar00rootroot00000000000000require "json" module Benchmark module Memory class HeldResults # Serialize objects for holding between runs. class Serializer # Load an object from a JSON document. # # @param json [String] A JSON document as a string. # # @return [Object] The object converted from the JSON document. def self.load(json) json = JSON.parse(json) if json.is_a?(String) new.load(json).object end # Instantiate a new serializer. # # @param object [Object] The object to serialize. def initialize(object = nil) @object = object end # @return [Object] The object to serialize. attr_reader :object # Convert a JSON document into an object. # # @param _hash [Hash] A JSON document hash. # # @return [Object] # @raise [NotImplementedError] # If the inheriting subclass didn't implement. def load(_hash) fail( NotImplementedError, "You must implement a concrete version in a subclass" ) end # Convert the object to a Hash. # # @return [Hash] The object as a Hash. # @raise [NotImplementedError] # If the inheriting subclass didn't implement. def to_h fail( NotImplementedError, "You must implement a concrete version in a subclass" ) end # Convert the object to a JSON document. # # @return [String] The object as a JSON document. def to_json JSON.generate(to_h) end alias_method :to_s, :to_json end end end end benchmark-memory-0.1.2/lib/benchmark/memory/helpers.rb000066400000000000000000000021361304377057300227640ustar00rootroot00000000000000module Benchmark module Memory # Helper methods for formatting output. module Helpers # Right-justifies to a length of 20 or adds a line of padding when longer. # # @param label [#to_s] The label to justify. # # @return [String] The justified label. def rjust(label) label = label.to_s if label.size > 20 "#{label}\n#{' ' * 20}" else label.rjust(20) end end # Scale a value into human-understandable terms. # # @param value [Integer, Float] The value to scale. # # @return [String] The scaled value. def scale(value) scale = Math.log10(value) scale = 0 if scale.infinite? scale = (scale / 3).to_i suffix = case scale when 1 then "k" when 2 then "M" when 3 then "B" when 4 then "T" when 5 then "Q" else scale = 0 " " end format("%10.3f#{suffix}", value.to_f / (1000**scale)) end module_function :scale end end end benchmark-memory-0.1.2/lib/benchmark/memory/job.rb000066400000000000000000000066701304377057300221030ustar00rootroot00000000000000require "forwardable" require "benchmark/memory/job/task" require "benchmark/memory/job/io_output" require "benchmark/memory/job/null_output" require "benchmark/memory/held_results" require "benchmark/memory/report" module Benchmark module Memory # Encapsulate the memory measurements of reports. class Job extend Forwardable # Instantiate a job for containing memory performance reports. # # @param output [#puts] The output to use for showing the job results. # @param quiet [Boolean] A flag for stopping output. # # @return [Job] def initialize(output: $stdout, quiet: false) @compare = false @full_report = Report.new @held_results = HeldResults.new @quiet = quiet @output = quiet? ? NullOutput.new : IOOutput.new(output) @tasks = [] end # @return [Report] the full report of all measurements in the job. attr_reader :full_report # @return [Array] the measurement tasks to run. attr_reader :tasks def_delegator :@held_results, :holding? # Check whether the job should do a comparison. # # @return [Boolean] def compare? @compare end # Enable output of a comparison of the different tasks. # # @return [void] def compare! @compare = true end # Enable holding results to compare between separate runs. # # @param held_path [String, IO] The location to save the held results. # # @return [void] def hold!(held_path) @held_results.path = held_path end # Add a measurement entry to the job to measure the specified block. # # @param label [String] The label for the measured code. # @param block [Proc] Code the measure. # # @raise [ArgumentError] if no code block is specified. def report(label = "", &block) unless block_given? fail ArgumentError, "You did not specify a block for the item" end tasks.push Task.new(label, block) end # Run the job and outputs its full report. # # @return [Report] def run @output.put_header @held_results.load tasks.each do |task| held = run_task(task) if held @output.put_hold_notice break end end full_report end # Run a task. # # @param task [Task] # # @return [Boolean] A flag indicating whether to hold or not. def run_task(task) if @held_results.include?(task) measurement = @held_results[task.label] full_report.add_entry(task, measurement) return false else measurement = task.call entry = full_report.add_entry(task, measurement) @output.put_entry(entry) if task == tasks.last @held_results.cleanup false else @held_results.add_result(entry) @held_results.holding? end end end # Run a comparison of the entries and puts it on the output. # # @return [void] def run_comparison if compare? && full_report.comparable? @output.put_comparison(full_report.comparison) end end # Check whether the job is set to quiet. # # @return [Boolean] def quiet? @quiet end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/000077500000000000000000000000001304377057300215455ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/job/io_output.rb000066400000000000000000000024211304377057300241200ustar00rootroot00000000000000require "benchmark/memory/job/io_output/comparison_formatter" require "benchmark/memory/job/io_output/entry_formatter" module Benchmark module Memory class Job # Output the results of jobs into an IO. class IOOutput # Instantiate a new output that writes to an IO. # # @param io [#puts] The IO to write on. def initialize(io) @io = io end # Put the entry onto the output. # # @return [void] def put_entry(entry) @io.puts EntryFormatter.new(entry) end # Put the comparison onto the output. # # @return [void] def put_comparison(comparison) @io.puts @io.puts "Comparison:" @io.puts ComparisonFormatter.new(comparison) end # Put the header onto the output. # # @return [void] def put_header @io.puts "Calculating -------------------------------------" end # Put a notice that the execution is holding for another run. # # @return [void] def put_hold_notice @io.puts @io.puts "Pausing here -- run Ruby again to " \ "measure the next benchmark..." end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/io_output/000077500000000000000000000000001304377057300235745ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/job/io_output/comparison_formatter.rb000066400000000000000000000035531304377057300303640ustar00rootroot00000000000000require "benchmark/memory/helpers" require "benchmark/memory/job/io_output/metric_formatter" module Benchmark module Memory class Job class IOOutput # Format a comparison for use with the IOOutput. class ComparisonFormatter include Helpers # Instantiate a formatter to output an comparison into an IO. # # @param comparison [Report::Comparison] The comparison to format. def initialize(comparison) @comparison = comparison end # @return [Report::Comparison] The comparison to format. attr_reader :comparison # Format comparison to a string to put on the output. # # @return [String] def to_s return "" unless comparison.possible? output = StringIO.new best, *rest = comparison.entries rest = Array(rest) add_best_summary(best, output) rest.each do |entry| add_comparison(entry, best, output) end output.string end private def add_best_summary(best, output) output << summary_message("%20s: %10i allocated\n", best) end def add_comparison(entry, best, output) output << summary_message("%20s: %10i allocated - ", entry) output << comparison_between(entry, best) output << "\n" end def comparison_between(entry, best) ratio = entry.allocated_memory.to_f / best.allocated_memory.to_f if ratio.abs > 1 format("%.2fx more", ratio) else "same" end end def summary_message(message, entry) format(message, entry.label, entry.allocated_memory) end end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/io_output/entry_formatter.rb000066400000000000000000000020521304377057300273440ustar00rootroot00000000000000require "stringio" require "benchmark/memory/helpers" require "benchmark/memory/job/io_output/metric_formatter" module Benchmark module Memory class Job class IOOutput # Format entries for use with the IOOutput. class EntryFormatter include Helpers # Instantiate a formatter to output an entry into an IO. # # @param entry [Entry] The entry to format. def initialize(entry) @entry = entry end # @return [Entry] The entry to format. attr_reader :entry # Format entry to a string to put on the output. # # @return [String] def to_s output = StringIO.new output << rjust(entry.label) entry.measurement.each_with_index.map do |metric, index| output << " " * 20 unless index == 0 output << MetricFormatter.new(metric) output << "\n" end output.string end end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/io_output/metric_formatter.rb000066400000000000000000000015411304377057300274700ustar00rootroot00000000000000require "benchmark/memory/helpers" module Benchmark module Memory class Job class IOOutput # Format metrics for use with the IOOutput. class MetricFormatter include Helpers # Instantiate a formatter to output a metric into an IO. # # @param metric [Measurement::Metric] The metric to format. def initialize(metric) @metric = metric end # @return [Metric] The metric to format. attr_reader :metric # Format metric to a string to put on the output. # # @return [String] def to_s [ format("%s %s", scale(metric.allocated), metric.type), format("(%s retained)", scale(metric.retained)), ].join(" ") end end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/null_output.rb000066400000000000000000000007311304377057300244650ustar00rootroot00000000000000module Benchmark module Memory class Job # Swallow all output from a job. class NullOutput # Swallow entry output. # # @return [void] def put_entry(entry) end # Swallow comparison output. # # @return [void] def put_comparison(comparison) end # Swallow header output. # # @return [void] def put_header end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/job/task.rb000066400000000000000000000024751304377057300230440ustar00rootroot00000000000000require "memory_profiler" require "benchmark/memory/measurement" module Benchmark module Memory class Job # Hold a labelled job for later measurement. class Task # Instantiate a job task for later measurement. # # @param label [#to_s] The label for the benchmark. # @param action [#call] The code to be measured. # # @raise [ArgumentError] if the action does not respond to `#call`. def initialize(label, action) unless action.respond_to?(:call) fail( ArgumentError, "Invalid action (#{@action.inspect} does not respond to call)" ) end @label = label @action = action end # @return [#call] The code to be measured. attr_reader :action # @return [#to_s] The label for the benchmark. attr_reader :label # Call the action and report on its memory usage. # # @return [Measurement] the memory usage measurement of the code. def call result = while_measuring_memory_usage { action.call } Measurement.from_result(result) end private def while_measuring_memory_usage(&block) MemoryProfiler.report({}, &block) end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/measurement.rb000066400000000000000000000043041304377057300236460ustar00rootroot00000000000000require "forwardable" require "benchmark/memory/measurement/metric" module Benchmark module Memory # Encapsulate the combined metrics of an action. class Measurement include Comparable include Enumerable extend Forwardable # Create a Measurement from a MemoryProfiler::Results object. # # @param result [MemoryProfiler::Results] # The results of a MemoryProfiler report. def self.from_result(result) memory = Metric.new( :memsize, result.total_allocated_memsize, result.total_retained_memsize ) objects = Metric.new( :objects, result.total_allocated, result.total_retained ) strings = Metric.new( :strings, result.strings_allocated.size, result.strings_retained.size ) new(:memory => memory, :objects => objects, :strings => strings) end # Instantiate a Measurement of memory usage. # # @param memory [Metric] The memory usage of an action. # @param objects [Metric] The object allocations of an action. # @param strings [Metric] The string allocations of an action. def initialize(memory:, objects:, strings:) @memory = memory @objects = objects @strings = strings @metrics = [@memory, @objects, @strings] end # @return [Metric] The memory allocation metric. attr_reader :memory # @return [Array] The metrics for the measurement. attr_reader :metrics # @return [Metric] The object allocation metric. attr_reader :objects # @return [Metric] The string allocation metric. attr_reader :strings # Enumerate through the metrics when enumerating a measurement. def_delegator :metrics, :each # Compare two measurements for sorting purposes. # # @param other [Measurement] The other measurement # # @return [Integer] def <=>(other) memory <=> other.memory end # Total amount of allocated memory for the measurement. # # @return [Integer] def allocated_memory memory.allocated end end end end benchmark-memory-0.1.2/lib/benchmark/memory/measurement/000077500000000000000000000000001304377057300233205ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/measurement/metric.rb000066400000000000000000000022441304377057300251320ustar00rootroot00000000000000require "benchmark/memory/helpers" module Benchmark module Memory class Measurement # Describe the ratio of allocated vs. retained memory in a measurement. class Metric include Comparable # Instantiate a Metric of allocated vs. retained memory. # # @param type [Symbol] The type of memory allocated in the metric. # @param allocated [Integer] The amount allocated in the metric. # @param retained [Integer] The amount retained in the metric. def initialize(type, allocated, retained) @type = type @allocated = allocated @retained = retained end # @return [Integer] The amount allocated in the metric. attr_reader :allocated # @return [Integer] The amount retained in the metric. attr_reader :retained # @return [Symbol] The type of memory allocated in the metric. attr_reader :type # Sort by the total allocated. # # @param other [Metric] The other metric. # # @return [Integer] def <=>(other) allocated <=> other.allocated end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/report.rb000066400000000000000000000021471304377057300226370ustar00rootroot00000000000000require "benchmark/memory/report/comparison" require "benchmark/memory/report/entry" module Benchmark module Memory # Hold the results of a set of benchmarks. class Report # Instantiate a report to hold entries of tasks and measurements. # # @return [Report] def initialize @entries = [] end # @return [Array] The entries in the report. attr_reader :entries # Add an entry to the report. # # @param task [Job::Task] The task to report about. # @param measurement [Measurement] The measurements from the task. # # @return [Entry] the newly created entry. def add_entry(task, measurement) entry = Entry.new(task.label, measurement) entries.push(entry) entry end # Return true if the report is comparable. # # @return [Boolean] def comparable? comparison.possible? end # Compare the entries within a report. # # @return [Comparison] def comparison @comparison ||= Comparison.new(entries) end end end end benchmark-memory-0.1.2/lib/benchmark/memory/report/000077500000000000000000000000001304377057300223065ustar00rootroot00000000000000benchmark-memory-0.1.2/lib/benchmark/memory/report/comparison.rb000066400000000000000000000011141304377057300250020ustar00rootroot00000000000000module Benchmark module Memory class Report # Compare entries against each other. class Comparison # Instantiate a new comparison. # # @param entries [Array] The entries to compare. def initialize(entries) @entries = entries.sort_by(&:measurement) end # @return [Array] The entries to compare. attr_reader :entries # Check if the comparison is possible # # @return [Boolean] def possible? entries.size > 1 end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/report/entry.rb000066400000000000000000000014451304377057300240000ustar00rootroot00000000000000require "forwardable" module Benchmark module Memory class Report # An entry in a report about a benchmark. class Entry # Instantiate a new entry. # # @param label [#to_s] The entry label. # @param measurement [Measurement] The measurements for the entry. def initialize(label, measurement) @label = label @measurement = measurement end # @return [#to_s] The entry label. attr_reader :label # @return [Measurement] The measurements for the entry. attr_reader :measurement # Get the total amount of memory allocated in the entry. # # @return [Integer] def allocated_memory measurement.memory.allocated end end end end end benchmark-memory-0.1.2/lib/benchmark/memory/version.rb000066400000000000000000000001101304377057300227750ustar00rootroot00000000000000module Benchmark module Memory VERSION = "0.1.2".freeze end end benchmark-memory-0.1.2/spec/000077500000000000000000000000001304377057300157155ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/000077500000000000000000000000001304377057300176475ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/000077500000000000000000000000001304377057300211575ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/held_results/000077500000000000000000000000001304377057300236545ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/held_results/entry_serializer_spec.rb000066400000000000000000000033401304377057300306050ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::HeldResults::EntrySerializer do describe ".load" do it "converts JSON documents into entries" do document = '{"item":"my super cool test",' \ '"measurement":' \ '{"memory":' \ '{"allocated":3078619,"retained":1539309,"type":"memsize"},' \ '"objects":{"allocated":2936123,"retained":0,"type":"objects"},' \ '"strings":{"allocated":100,"retained":99,"type":"strings"}}}' entry = described_class.load(document) result = described_class.new(entry).to_s expect(result).to eq(document) end end describe "#to_s" do it "converts the entry into a JSON document" do result = described_class.new(create_entry).to_s expected_result = '{"item":"my super cool test",' \ '"measurement":' \ '{"memory":' \ '{"allocated":3078619,"retained":1539309,"type":"memsize"},' \ '"objects":{"allocated":2936123,"retained":0,"type":"objects"},' \ '"strings":{"allocated":100,"retained":99,"type":"strings"}}}' expect(result).to eq(expected_result) end end def create_entry Benchmark::Memory::Report::Entry.new( "my super cool test", create_measurement ) end def create_measurement memsize = Benchmark::Memory::Measurement::Metric.new( :memsize, 3_078_619, 1_539_309 ) objects = Benchmark::Memory::Measurement::Metric.new( :objects, 2_936_123, 0 ) strings = Benchmark::Memory::Measurement::Metric.new( :strings, 100, 99 ) Benchmark::Memory::Measurement.new( :strings => strings, :objects => objects, :memory => memsize ) end end benchmark-memory-0.1.2/spec/benchmark/memory/helpers_spec.rb000066400000000000000000000013071304377057300241610ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Helpers do describe "#scale" do it "scales values into human terms" do scale = ->(value) { Benchmark::Memory::Helpers.scale(value) } expect(scale.call(1)).to eq(" 1.000 ") expect(scale.call(123)).to eq(" 123.000 ") expect(scale.call(1_234)).to eq(" 1.234k") expect(scale.call(1_234_567)).to eq(" 1.235M") expect(scale.call(1_234_567_890)).to eq(" 1.235B") expect(scale.call(1_234_567_890_123)).to eq(" 1.235T") expect(scale.call(1_234_567_890_123_456)).to eq(" 1.235Q") end end end benchmark-memory-0.1.2/spec/benchmark/memory/job/000077500000000000000000000000001304377057300217315ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/job/io_output/000077500000000000000000000000001304377057300237605ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/job/io_output/comparison_formatter_spec.rb000066400000000000000000000040721304377057300315570ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Job::IOOutput::ComparisonFormatter do describe "#to_s" do it "is blank when the comparison is not possible" do comp = comparison([]) formatter = described_class.new(comp) expect(formatter.to_s).to be_empty end it "outputs a comparison of the entries" do entries = [create_high_entry, create_medium_entry, create_low_entry] comp = comparison(entries) formatter = described_class.new(comp) output = formatter.to_s expect(output).not_to be_empty expect(output.split("\n").size).to eq(entries.length) end it "gives a multiplier when entries aren't equal" do comp = comparison([create_high_entry, create_low_entry]) formatter = described_class.new(comp) expect(formatter.to_s).to match(/4.00x/) end it "says 'same' when entries are equal" do comp = comparison([create_low_entry, create_low_entry]) formatter = described_class.new(comp) expect(formatter.to_s).to match(/same/) end end def comparison(entries) Benchmark::Memory::Report::Comparison.new(entries) end def create_high_entry Benchmark::Memory::Report::Entry.new( "high", create_measurement(10_000, 5_000) ) end alias_method :create_entry, :create_high_entry def create_low_entry Benchmark::Memory::Report::Entry.new( "low", create_measurement(2_500, 1_250) ) end def create_medium_entry Benchmark::Memory::Report::Entry.new( "medium", create_measurement(5_000, 2_500) ) end def create_measurement(allocated, retained) memsize = Benchmark::Memory::Measurement::Metric.new( :memsize, allocated, retained ) objects = Benchmark::Memory::Measurement::Metric.new( :objects, 2_936_123, 0 ) strings = Benchmark::Memory::Measurement::Metric.new( :strings, 100, 99 ) Benchmark::Memory::Measurement.new( :strings => strings, :objects => objects, :memory => memsize ) end end benchmark-memory-0.1.2/spec/benchmark/memory/job/io_output_spec.rb000066400000000000000000000016331304377057300253220ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Job::IOOutput do describe "#put_entry" do it "outputs onto the passed IO" do entry = create_entry io = StringIO.new output = described_class.new(io) output.put_entry(entry) expect(io.string).not_to be_empty end end def create_entry Benchmark::Memory::Report::Entry.new( "my super cool test", create_measurement ) end def create_measurement memsize = Benchmark::Memory::Measurement::Metric.new( :memsize, 3_078_619, 1_539_309 ) objects = Benchmark::Memory::Measurement::Metric.new( :objects, 2_936_123, 0 ) strings = Benchmark::Memory::Measurement::Metric.new( :strings, 100, 99 ) Benchmark::Memory::Measurement.new( :strings => strings, :objects => objects, :memory => memsize ) end end benchmark-memory-0.1.2/spec/benchmark/memory/job/task_spec.rb000066400000000000000000000010141304377057300242260ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Job::Task do describe ".new" do it "raises an ArgumentError when the action is not a callable" do expect do described_class.new("label", nil) end.to raise_error(ArgumentError) end end describe "#call" do it "returns a measurement of the memory usage in the action" do action = -> {} entry = described_class.new("empty proc", action) expect(entry.call).to be_a Benchmark::Memory::Measurement end end end benchmark-memory-0.1.2/spec/benchmark/memory/job_spec.rb000066400000000000000000000054771304377057300233050ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Job do describe "#report" do it "raises an ArgumentError when no block is specified" do job = create_job expect { job.report("riddle me this") }.to raise_error(ArgumentError) end it "adds a task to the list of tasks in the job" do job = create_job expect { job.report("riddle me that") {} }.to( change(job.tasks, :count).by(1) ) end end describe "#run" do it "adds an entry to the report for each task" do job = create_job job.report("with you") {} job.report("my brown eyed girl") {} expect { job.run }.to change(job.full_report.entries, :count).by(2) end context "holding" do it "only executes one task for each call to run" do hold_buffer = StringIO.new job, output = create_job_and_output job.report("with you") {} job.report("my brown eyed girl") {} job.hold!(hold_buffer) expect { job.run }.to change(job.full_report.entries, :count).by(1) output_string = output.string expect(output_string).to match(/with you/) expect(output_string).not_to match(/brown eyed girl/) expect(output_string).to match(/Pausing/) # Reset for the second run hold_buffer.rewind job, output = create_job_and_output job.report("with you") {} job.report("my brown eyed girl") {} job.hold!(hold_buffer) expect { job.run }.to change(job.full_report.entries, :count).by(2) output_string = output.string expect(output_string).not_to match(/with you/) expect(output_string).to match(/brown eyed girl/) expect(output_string).not_to match(/Pausing/) end end end describe "#run_comparison" do it "does not run if there are no entries" do job, output = create_job_and_output job.run job.run_comparison expect(output.string).not_to match(/Comparison/) end it "runs when there are entries and the job is configured to compare" do job, output = create_job_and_output job.report("with you") {} job.report("my brown eyed girl") {} job.compare! job.run job.run_comparison expect(output.string).to match(/Comparison/) end end describe "#quiet?" do it "prevents any output from being written" do job, output = create_job_and_output(:quiet => true) job.report("with you") {} job.report("my brown eyed girl") {} job.compare! job.run job.run_comparison expect(output.string).to be_empty end end def create_job_and_output(quiet: false) output = StringIO.new job = Benchmark::Memory::Job.new(:output => output, :quiet => quiet) [job, output] end def create_job create_job_and_output.first end end benchmark-memory-0.1.2/spec/benchmark/memory/measurement/000077500000000000000000000000001304377057300235045ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/measurement/metric_spec.rb000066400000000000000000000006271304377057300263330ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Measurement::Metric do describe "#to_s" do it "is of consistent length" do examples = [0, 100, 1_000, 1_000_000, 1_000_000_000, 1_000_000_000_000] lengths = examples.map do |i| metric = described_class.new(:fake, i * 2, i) metric.to_s.length end expect(lengths.min).to eq(lengths.max) end end end benchmark-memory-0.1.2/spec/benchmark/memory/report/000077500000000000000000000000001304377057300224725ustar00rootroot00000000000000benchmark-memory-0.1.2/spec/benchmark/memory/report/comparison_spec.rb000066400000000000000000000022461304377057300262070ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Report::Comparison do describe "#entries" do it "is sorted from smallest allocation to largest" do high_entry = create_high_entry low_entry = create_low_entry comparison = described_class.new([high_entry, low_entry]) expect(comparison.entries).to eq([low_entry, high_entry]) end end def create_high_entry Benchmark::Memory::Report::Entry.new( "high", create_measurement(10_000, 5_000) ) end alias_method :create_entry, :create_high_entry def create_low_entry Benchmark::Memory::Report::Entry.new( "low", create_measurement(2_500, 1_250) ) end def create_measurement(allocated, retained) memsize = Benchmark::Memory::Measurement::Metric.new( :memsize, allocated, retained ) objects = Benchmark::Memory::Measurement::Metric.new( :objects, 2_936_123, 0 ) strings = Benchmark::Memory::Measurement::Metric.new( :strings, 100, 99 ) Benchmark::Memory::Measurement.new( :strings => strings, :objects => objects, :memory => memsize ) end end benchmark-memory-0.1.2/spec/benchmark/memory/report_spec.rb000066400000000000000000000015021304377057300240270ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory::Report do it "is initialized with a blank list of entries" do report = described_class.new expect(report.entries).to be_empty end describe "#add_entry" do it "adds an entry to the list of entries" do report = described_class.new task = Benchmark::Memory::Job::Task.new("do nothing", -> {}) measurement = create_measurement expect { report.add_entry(task, measurement) }.to( change(report.entries, :count).by(1) ) end end def create_measurement metrics = { :memory => create_metric, :objects => create_metric, :strings => create_metric, } Benchmark::Memory::Measurement.new(metrics) end def create_metric Benchmark::Memory::Measurement::Metric.new(:fake, 0, 0) end end benchmark-memory-0.1.2/spec/benchmark/memory_spec.rb000066400000000000000000000012501304377057300225140ustar00rootroot00000000000000require "spec_helper" RSpec.describe Benchmark::Memory do it "has a version number" do expect(Benchmark::Memory::VERSION).not_to be nil end it "exposes .memory on Benchmark" do expect(Benchmark).to respond_to(:memory) end describe ".memory" do around(:each) do |spec| old_stdout = $stdout $stdout = StringIO.new spec.run $stdout = old_stdout end it "raises an error when not given a block" do expect { Benchmark.memory }.to raise_error( Benchmark::Memory::ConfigurationError ) end it "returns a report" do expect(Benchmark.memory {}).to be_a(Benchmark::Memory::Report) end end end benchmark-memory-0.1.2/spec/spec_helper.rb000066400000000000000000000013251304377057300205340ustar00rootroot00000000000000if ENV["COVERAGE"] || ENV["CI"] require "simplecov" SimpleCov.start do add_filter "/spec/" end end require "benchmark/memory" require "pry" require "rspec" RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.syntax = :expect expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end config.filter_run :focus config.run_all_when_everything_filtered = true config.disable_monkey_patching! config.default_formatter = "doc" if config.files_to_run.one? config.profile_examples = 10 if ENV["PROFILE"] config.order = :random Kernel.srand config.seed end