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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +260 -123
- data/README.md +70 -40
- data/lib/memo_wise/internal_api.rb +39 -13
- data/lib/memo_wise/version.rb +1 -1
- data/lib/memo_wise.rb +30 -17
- metadata +4 -23
- data/.dokaz +0 -2
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -4
- data/.github/dependabot.yml +0 -20
- data/.github/workflows/main.yml +0 -57
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -17
- data/.ruby-version +0 -1
- data/.yardopts +0 -8
- data/CODE_OF_CONDUCT.md +0 -76
- data/Gemfile +0 -36
- data/Gemfile.lock +0 -124
- data/Rakefile +0 -3
- data/benchmarks/Gemfile +0 -21
- data/benchmarks/benchmarks.rb +0 -274
- data/bin/console +0 -8
- data/bin/setup +0 -8
- data/logo/logo.png +0 -0
- data/memo_wise.gemspec +0 -43
data/benchmarks/benchmarks.rb
DELETED
@@ -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
data/bin/setup
DELETED
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
|