memo_wise 1.7.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,274 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "benchmark/ips"
4
-
5
- require "tempfile"
6
-
7
- github_memo_wise_path = Gem.loaded_specs["memo_wise"].full_gem_path
8
-
9
- # This string is both used for temp filepaths necessary to separate the GitHub
10
- # version of MemoWise and the local version, and used for the reported results
11
- GITHUB_MAIN = "MemoWise_GitHubMain"
12
-
13
- # We download a the main branch of MemoWise on GitHub into a tmp directory to
14
- # compare against the local version when we run benchmarks
15
- Dir.mktmpdir do |directory|
16
- Dir["#{github_memo_wise_path}/lib/**/*.rb"].each do |file|
17
- Tempfile.open([File.basename(file)[0..-4], ".rb"], directory) do |tempfile|
18
- tempfile.write(File.read(file).gsub("MemoWise", GITHUB_MAIN))
19
- tempfile.rewind
20
- require tempfile.path
21
- end
22
- end
23
- end
24
-
25
- require_relative "../lib/memo_wise"
26
-
27
- # Some gems do not yet work in Ruby 3 so we only require them if they're loaded
28
- # in the Gemfile.
29
- %w[memery memoist memoized memoizer ddmemoize dry-core].
30
- each { |gem| require gem if Gem.loaded_specs.key?(gem) }
31
-
32
- # The VERSION constant does not get loaded above for these gems.
33
- %w[memoized memoizer].
34
- each { |gem| require "#{gem}/version" if Gem.loaded_specs.key?(gem) }
35
-
36
- # The Memoizable module from dry-core needs to be required manually
37
- require "dry/core/memoizable" if Gem.loaded_specs.key?("dry-core")
38
-
39
- class BenchmarkSuiteWithoutGC
40
- def warming(*)
41
- run_gc
42
- end
43
-
44
- def running(*)
45
- run_gc
46
- end
47
-
48
- def warmup_stats(*); end
49
-
50
- def add_report(*); end
51
-
52
- private
53
-
54
- def run_gc
55
- GC.enable
56
- GC.start
57
- GC.disable
58
- end
59
- end
60
- suite = BenchmarkSuiteWithoutGC.new
61
-
62
- BenchmarkGem = Struct.new(:klass, :activation_code, :memoization_method) do
63
- def benchmark_name
64
- "#{klass} (#{klass::VERSION})"
65
- end
66
- end
67
-
68
- # We alphabetize this list for easier readability, but shuffle the list before
69
- # using it to minimize the chance that our benchmarks are affected by ordering.
70
- # NOTE: Some gems do not yet work in Ruby 3 so we only test with them if they've
71
- # been `require`d.
72
- BENCHMARK_GEMS = [
73
- BenchmarkGem.new(MemoWise_GitHubMain, "prepend #{GITHUB_MAIN}", :memo_wise),
74
- BenchmarkGem.new(MemoWise, "prepend MemoWise", :memo_wise),
75
- (BenchmarkGem.new(DDMemoize, "DDMemoize.activate(self)", :memoize) if defined?(DDMemoize)),
76
- (BenchmarkGem.new(Dry::Core, "include Dry::Core::Memoizable", :memoize) if defined?(Dry::Core)),
77
- (BenchmarkGem.new(Memery, "include Memery", :memoize) if defined?(Memery)),
78
- (BenchmarkGem.new(Memoist, "extend Memoist", :memoize) if defined?(Memoist)),
79
- (BenchmarkGem.new(Memoized, "include Memoized", :memoize) if defined?(Memoized)),
80
- (BenchmarkGem.new(Memoizer, "include Memoizer", :memoize) if defined?(Memoizer))
81
- ].compact.shuffle
82
-
83
- # Use metaprogramming to ensure that each class is created in exactly the
84
- # the same way.
85
- BENCHMARK_GEMS.each do |benchmark_gem|
86
- eval <<~HEREDOC, binding, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
87
- class #{benchmark_gem.klass}Example
88
- #{benchmark_gem.activation_code}
89
-
90
- def no_args
91
- 100
92
- end
93
- #{benchmark_gem.memoization_method} :no_args
94
-
95
- def one_positional_arg(a)
96
- 100
97
- end
98
- #{benchmark_gem.memoization_method} :one_positional_arg
99
-
100
- def positional_args(a, b)
101
- 100
102
- end
103
- #{benchmark_gem.memoization_method} :positional_args
104
-
105
- def one_keyword_arg(a:)
106
- 100
107
- end
108
- #{benchmark_gem.memoization_method} :one_keyword_arg
109
-
110
- def keyword_args(a:, b:)
111
- 100
112
- end
113
- #{benchmark_gem.memoization_method} :keyword_args
114
-
115
- def positional_and_keyword_args(a, b:)
116
- 100
117
- end
118
- #{benchmark_gem.memoization_method} :positional_and_keyword_args
119
-
120
- def positional_and_splat_args(a, *args)
121
- 100
122
- end
123
- #{benchmark_gem.memoization_method} :positional_and_splat_args
124
-
125
- def keyword_and_double_splat_args(a:, **kwargs)
126
- 100
127
- end
128
- #{benchmark_gem.memoization_method} :keyword_and_double_splat_args
129
-
130
- def positional_splat_keyword_and_double_splat_args(a, *args, b:, **kwargs)
131
- 100
132
- end
133
- #{benchmark_gem.memoization_method} :positional_splat_keyword_and_double_splat_args
134
- end
135
- HEREDOC
136
- end
137
-
138
- N_RESULT_DECIMAL_DIGITS = 2
139
-
140
- # Each method within these benchmarks is initially run once to memoize the
141
- # result value, so our benchmark only tests memoized retrieval time.
142
- benchmark_lambdas = [
143
- lambda do |x, instance, benchmark_gem|
144
- instance.no_args
145
-
146
- x.report("#{benchmark_gem.benchmark_name}: ()") do
147
- instance.no_args
148
- end
149
- end,
150
- lambda do |x, instance, benchmark_gem|
151
- instance.one_positional_arg(1)
152
-
153
- x.report("#{benchmark_gem.benchmark_name}: (a)") do
154
- instance.one_positional_arg(1)
155
- end
156
- end,
157
- lambda do |x, instance, benchmark_gem|
158
- instance.positional_args(1, 2)
159
-
160
- x.report("#{benchmark_gem.benchmark_name}: (a, b)") do
161
- instance.positional_args(1, 2)
162
- end
163
- end,
164
- lambda do |x, instance, benchmark_gem|
165
- instance.one_keyword_arg(a: 1)
166
-
167
- x.report("#{benchmark_gem.benchmark_name}: (a:)") do
168
- instance.one_keyword_arg(a: 1)
169
- end
170
- end,
171
- lambda do |x, instance, benchmark_gem|
172
- instance.keyword_args(a: 1, b: 2)
173
-
174
- x.report("#{benchmark_gem.benchmark_name}: (a:, b:)") do
175
- instance.keyword_args(a: 1, b: 2)
176
- end
177
- end,
178
- lambda do |x, instance, benchmark_gem|
179
- instance.positional_and_keyword_args(1, b: 2)
180
-
181
- x.report("#{benchmark_gem.benchmark_name}: (a, b:)") do
182
- instance.positional_and_keyword_args(1, b: 2)
183
- end
184
- end,
185
- lambda do |x, instance, benchmark_gem|
186
- instance.positional_and_splat_args(1, 2)
187
-
188
- x.report("#{benchmark_gem.benchmark_name}: (a, *args)") do
189
- instance.positional_and_splat_args(1, 2)
190
- end
191
- end,
192
- lambda do |x, instance, benchmark_gem|
193
- instance.keyword_and_double_splat_args(a: 1, b: 2)
194
-
195
- x.report("#{benchmark_gem.benchmark_name}: (a:, **kwargs)") do
196
- instance.keyword_and_double_splat_args(a: 1, b: 2)
197
- end
198
- end,
199
- lambda do |x, instance, benchmark_gem|
200
- instance.positional_splat_keyword_and_double_splat_args(1, 2, b: 3, a: 4)
201
-
202
- x.report("#{benchmark_gem.benchmark_name}: (a, *args, b:, **kwargs)") do
203
- instance.positional_splat_keyword_and_double_splat_args(1, 2, b: 3, a: 4)
204
- end
205
- end
206
- ]
207
-
208
- # We benchmark different cases separately, to ensure that slow performance in
209
- # one method or code path isn't hidden by fast performance in another.
210
- benchmark_jsons = benchmark_lambdas.map do |benchmark|
211
- json_file = Tempfile.new
212
-
213
- Benchmark.ips do |x|
214
- x.config(suite: suite) # rubocop:disable Style/HashSyntax
215
- BENCHMARK_GEMS.each do |benchmark_gem|
216
- instance = Object.const_get("#{benchmark_gem.klass}Example").new
217
-
218
- benchmark.call(x, instance, benchmark_gem)
219
- end
220
-
221
- x.compare!
222
- x.json! json_file.path
223
- end
224
-
225
- JSON.parse(json_file.read)
226
- end
227
-
228
- [true, false].each do |github_comparison|
229
- benchmark_jsons.each_with_index do |benchmark_json, i|
230
- # We print a comparison table after we run each benchmark to copy into our
231
- # README.md
232
-
233
- # MemoWise will not appear in the comparison table, but we will use it to
234
- # compare against other gems' benchmarks
235
- memo_wise = benchmark_json.find { |json| json["name"].split.first == "MemoWise" }
236
- benchmark_json -= [memo_wise]
237
-
238
- github_main = benchmark_json.find { |json| json["name"].split.first == GITHUB_MAIN }
239
- benchmark_json = github_comparison ? [github_main] : benchmark_json - [github_main]
240
-
241
- # Sort benchmarks by gem name to alphabetize our final output table.
242
- benchmark_json.sort_by! { |json| json["name"] }
243
-
244
- # Print headers based on the first benchmark_json
245
- if i.zero?
246
- benchmark_headers = benchmark_json.map do |benchmark_gem|
247
- # Gem name is of the form:
248
- # "MemoWise (1.1.0): ()"
249
- # We use this mapping to get a header of the form
250
- # "`MemoWise` (1.1.0)
251
- gem_name_parts = benchmark_gem["name"].split
252
- "`#{gem_name_parts[0]}` #{gem_name_parts[1][...-1]}"
253
- end.join("|")
254
- puts "|Method arguments|#{benchmark_headers}|"
255
- puts "#{'|--' * (benchmark_json.size + 1)}|"
256
- end
257
-
258
- output_str = benchmark_json.map do |bgem|
259
- # "%.2f" % 12.345 => "12.34" (instead of "12.35")
260
- # See: https://bugs.ruby-lang.org/issues/12548
261
- # 1.00.round(2).to_s => "1.0" (instead of "1.00")
262
- #
263
- # So to round and format correctly, we first use Float#round and then %
264
- "%.#{N_RESULT_DECIMAL_DIGITS}fx" %
265
- (memo_wise["central_tendency"] / bgem["central_tendency"]).round(N_RESULT_DECIMAL_DIGITS)
266
- end.join("|")
267
-
268
- name = memo_wise["name"].partition(": ").last
269
- puts "|`#{name}`#{' (none)' if name == '()'}|#{output_str}|"
270
- end
271
-
272
- # Output a blank line between sections
273
- puts ""
274
- end
data/bin/console DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "bundler/setup"
5
- require "memo_wise"
6
-
7
- require "irb"
8
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/logo/logo.png DELETED
Binary file
data/memo_wise.gemspec DELETED
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/memo_wise/version"
4
-
5
- Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
6
- spec.name = "memo_wise"
7
- spec.version = MemoWise::VERSION
8
- spec.summary = "The wise choice for Ruby memoization"
9
- spec.homepage = "https://github.com/panorama-ed/memo_wise"
10
- spec.license = "MIT"
11
-
12
- spec.authors = [
13
- "Panorama Education",
14
- "Jacob Evelyn",
15
- "Jemma Issroff",
16
- "Marc Siegel",
17
- ]
18
-
19
- spec.email = [
20
- "engineering@panoramaed.com",
21
- "jacobevelyn@gmail.com",
22
- "jemmaissroff@gmail.com",
23
- "marc@usainnov.com",
24
- ]
25
-
26
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
27
-
28
- # Specify which files should be added to the gem when it is released.
29
- # The `git ls-files -z` loads the files in the RubyGem that have been added
30
- # into git.
31
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
- `git ls-files -z`.split("\x0").reject do |f|
33
- f.match(%r{^(test|spec|features)/})
34
- end
35
- end
36
- spec.require_paths = ["lib"]
37
-
38
- spec.metadata = {
39
- "rubygems_mfa_required" => "true",
40
- "changelog_uri" => "https://github.com/panorama-ed/memo_wise/blob/main/CHANGELOG.md",
41
- "source_code_uri" => "https://github.com/panorama-ed/memo_wise"
42
- }
43
- end