memo_wise 1.0.0 → 1.4.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/.github/PULL_REQUEST_TEMPLATE.md +2 -2
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +16 -1
- data/CHANGELOG.md +55 -2
- data/Gemfile.lock +30 -30
- data/README.md +74 -19
- data/benchmarks/Gemfile +4 -2
- data/benchmarks/benchmarks.rb +149 -132
- data/lib/memo_wise/internal_api.rb +183 -155
- data/lib/memo_wise/version.rb +1 -1
- data/lib/memo_wise.rb +241 -114
- data/memo_wise.gemspec +1 -0
- metadata +3 -4
- data/benchmarks/.ruby-version +0 -1
- data/benchmarks/Gemfile.lock +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b26ef1a44fd13498359282c6d47923ecf90bdfd4e8ac487d389dd1d5c9c5d59
|
4
|
+
data.tar.gz: 51d2ddc7b1d0e7d770afcf081f566059e67b07867c10803db693fd2ec848f4d1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca1c06be2c7d0368e7708349563a0a79f93922d491ffe43448793bd6364938ea063bc3910d67ca7ce04c6cb409d784d23d19d5e97fea6430fcb20ebbb0819ab3
|
7
|
+
data.tar.gz: 93fc8811a80bd7f4d88517e0c3cd21f3b6ef29daea6e0578e734abecdc0067f5f9451261f5c47ad80106332075a4072a4a3e7322fe6181f2d61782d63c42caa0
|
@@ -1,4 +1,4 @@
|
|
1
1
|
**Before merging:**
|
2
2
|
|
3
|
-
- [ ] Copy the latest benchmark results into the `README.md` and update this PR
|
4
|
-
- [ ] If this change merits an update to `CHANGELOG.md`, add an entry following Keep a Changelog [guidelines](https://keepachangelog.com/en/1.0.0/) with [semantic versioning](https://semver.org/)
|
3
|
+
- [ ] Copy the table printed at the end of the latest benchmark results into the `README.md` and update this PR
|
4
|
+
- [ ] If this change merits an update to `CHANGELOG.md`, add an entry following Keep a Changelog [guidelines](https://keepachangelog.com/en/1.0.0/) with [semantic versioning](https://semver.org/)
|
data/.github/workflows/main.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,2 +1,17 @@
|
|
1
1
|
inherit_gem:
|
2
|
-
panolint: rubocop.yml
|
2
|
+
panolint: panolint-rubocop.yml
|
3
|
+
|
4
|
+
Layout/LineLength:
|
5
|
+
Max: 120
|
6
|
+
|
7
|
+
Metrics/ModuleLength:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Metrics/PerceivedComplexity:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/DocumentDynamicEvalDefinition:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/MultipleComparison:
|
17
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -6,12 +6,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
|
-
|
9
|
+
|
10
|
+
- Nothing yet!
|
11
|
+
|
12
|
+
## [1.4.0] - 2021-12-10
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
|
16
|
+
- Fix several bugs related to classes inheriting memoized methods
|
17
|
+
from multiple modules or a parent class ([#241](https://github.com/panorama-ed/memo_wise/pull/241))
|
18
|
+
|
19
|
+
## [1.3.0] - 2021-11-22
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- Fix thread-safety issue in concurrent calls to zero-arg method in unmemoized
|
24
|
+
state which resulted in a `nil` value being accidentally returned in one thread
|
25
|
+
- Fix bugs related to child classes inheriting from parent classes that use
|
26
|
+
`MemoWise`
|
27
|
+
|
28
|
+
## [1.2.0] - 2021-11-10
|
29
|
+
|
30
|
+
### Updated
|
31
|
+
- Improved performance of all methods by using an outer Array instead of a Hash
|
32
|
+
- Improved performance for multi-argument methods and simplify internal data
|
33
|
+
structures
|
34
|
+
|
35
|
+
### Fixed
|
36
|
+
- Removed use of #hash due to potential of hash collisions
|
37
|
+
- Updated internal local variable names to avoid name collisions with method
|
38
|
+
arguments
|
39
|
+
|
40
|
+
### Breaking Changes
|
41
|
+
- None
|
42
|
+
|
43
|
+
## [1.1.0] - 2021-07-29
|
44
|
+
### Updated
|
45
|
+
- Improved performance across the board by:
|
46
|
+
- removing `Hash#fetch`
|
47
|
+
- using `Array#hash`
|
48
|
+
- avoiding multi-layer hash lookups for multi-argument methods
|
49
|
+
- optimizing for truthy results
|
50
|
+
- Add `dry-core` to benchmarks in README
|
51
|
+
|
52
|
+
### Fixed
|
53
|
+
- Fixed usage on module singleton classes
|
54
|
+
- Fixed usage on module which would be extended by other classes
|
55
|
+
|
56
|
+
### Breaking Changes
|
57
|
+
- None
|
10
58
|
|
11
59
|
## [1.0.0] - 2021-06-24
|
12
60
|
### Added
|
13
61
|
- Support for `.preset_memo_wise` on class methods
|
14
62
|
- Support for `.reset_memo_wise` on class methods
|
63
|
+
|
15
64
|
### Updated
|
16
65
|
- Improved performance for common cases by reducing array allocations
|
17
66
|
|
@@ -61,7 +110,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
61
110
|
- Panolint
|
62
111
|
- Dependabot setup
|
63
112
|
|
64
|
-
[Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v1.
|
113
|
+
[Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v1.4.0...HEAD
|
114
|
+
[1.4.0]: https://github.com/panorama-ed/memo_wise/compare/v1.3.0...v1.4.0
|
115
|
+
[1.3.0]: https://github.com/panorama-ed/memo_wise/compare/v1.2.0...v1.3.0
|
116
|
+
[1.2.0]: https://github.com/panorama-ed/memo_wise/compare/v1.1.0...v1.2.0
|
117
|
+
[1.1.0]: https://github.com/panorama-ed/memo_wise/compare/v1.0.0...v1.1.0
|
65
118
|
[1.0.0]: https://github.com/panorama-ed/memo_wise/compare/v0.4.0...v1.0.0
|
66
119
|
[0.4.0]: https://github.com/panorama-ed/memo_wise/compare/v0.3.0...v0.4.0
|
67
120
|
[0.3.0]: https://github.com/panorama-ed/memo_wise/compare/v0.2.0...v0.3.0
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/panorama-ed/panolint.git
|
3
|
-
revision:
|
3
|
+
revision: 5850a218b96da7dd196438fb65d41169cd6747e5
|
4
4
|
branch: main
|
5
5
|
specs:
|
6
|
-
panolint (0.1.
|
6
|
+
panolint (0.1.4)
|
7
7
|
brakeman (>= 4.8, < 6.0)
|
8
8
|
rubocop (>= 0.83, < 2.0)
|
9
9
|
rubocop-performance (~> 1.5)
|
@@ -14,20 +14,21 @@ GIT
|
|
14
14
|
PATH
|
15
15
|
remote: .
|
16
16
|
specs:
|
17
|
-
memo_wise (1.
|
17
|
+
memo_wise (1.4.0)
|
18
18
|
|
19
19
|
GEM
|
20
20
|
remote: https://rubygems.org/
|
21
21
|
specs:
|
22
|
-
activesupport (
|
22
|
+
activesupport (6.1.4.1)
|
23
23
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
24
|
-
i18n (>=
|
25
|
-
minitest (
|
26
|
-
tzinfo (~>
|
24
|
+
i18n (>= 1.6, < 2)
|
25
|
+
minitest (>= 5.1)
|
26
|
+
tzinfo (~> 2.0)
|
27
|
+
zeitwerk (~> 2.3)
|
27
28
|
ansi (1.5.0)
|
28
29
|
ast (2.4.2)
|
29
|
-
brakeman (5.
|
30
|
-
codecov (0.
|
30
|
+
brakeman (5.1.2)
|
31
|
+
codecov (0.6.0)
|
31
32
|
simplecov (>= 0.15, < 0.22)
|
32
33
|
concurrent-ruby (1.1.9)
|
33
34
|
diff-lcs (1.4.4)
|
@@ -36,15 +37,15 @@ GEM
|
|
36
37
|
ansi
|
37
38
|
rouge
|
38
39
|
slop (~> 3)
|
39
|
-
i18n (1.8.
|
40
|
+
i18n (1.8.11)
|
40
41
|
concurrent-ruby (~> 1.0)
|
41
42
|
minitest (5.14.4)
|
42
|
-
parallel (1.
|
43
|
-
parser (3.0.
|
43
|
+
parallel (1.21.0)
|
44
|
+
parser (3.0.3.1)
|
44
45
|
ast (~> 2.4.1)
|
45
46
|
rack (2.2.3)
|
46
47
|
rainbow (3.0.0)
|
47
|
-
rake (13.0.
|
48
|
+
rake (13.0.6)
|
48
49
|
redcarpet (3.5.1)
|
49
50
|
regexp_parser (2.1.1)
|
50
51
|
rexml (3.2.5)
|
@@ -62,44 +63,43 @@ GEM
|
|
62
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
63
64
|
rspec-support (~> 3.10.0)
|
64
65
|
rspec-support (3.10.0)
|
65
|
-
rubocop (1.
|
66
|
+
rubocop (1.23.0)
|
66
67
|
parallel (~> 1.10)
|
67
68
|
parser (>= 3.0.0.0)
|
68
69
|
rainbow (>= 2.2.2, < 4.0)
|
69
70
|
regexp_parser (>= 1.8, < 3.0)
|
70
71
|
rexml
|
71
|
-
rubocop-ast (>= 1.
|
72
|
+
rubocop-ast (>= 1.12.0, < 2.0)
|
72
73
|
ruby-progressbar (~> 1.7)
|
73
74
|
unicode-display_width (>= 1.4.0, < 3.0)
|
74
|
-
rubocop-ast (1.
|
75
|
-
parser (>=
|
76
|
-
rubocop-performance (1.
|
77
|
-
rubocop (>=
|
75
|
+
rubocop-ast (1.13.0)
|
76
|
+
parser (>= 3.0.1.1)
|
77
|
+
rubocop-performance (1.12.0)
|
78
|
+
rubocop (>= 1.7.0, < 2.0)
|
78
79
|
rubocop-ast (>= 0.4.0)
|
79
|
-
rubocop-rails (2.
|
80
|
+
rubocop-rails (2.12.4)
|
80
81
|
activesupport (>= 4.2.0)
|
81
82
|
rack (>= 1.1)
|
82
|
-
rubocop (>=
|
83
|
-
rubocop-rake (0.
|
84
|
-
rubocop
|
85
|
-
rubocop-rspec (2.2.0)
|
83
|
+
rubocop (>= 1.7.0, < 2.0)
|
84
|
+
rubocop-rake (0.6.0)
|
86
85
|
rubocop (~> 1.0)
|
87
|
-
|
86
|
+
rubocop-rspec (2.6.0)
|
87
|
+
rubocop (~> 1.19)
|
88
88
|
ruby-progressbar (1.11.0)
|
89
89
|
simplecov (0.18.5)
|
90
90
|
docile (~> 1.1)
|
91
91
|
simplecov-html (~> 0.11)
|
92
92
|
simplecov-html (0.12.3)
|
93
93
|
slop (3.6.0)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
unicode-display_width (2.0.0)
|
94
|
+
tzinfo (2.0.4)
|
95
|
+
concurrent-ruby (~> 1.0)
|
96
|
+
unicode-display_width (2.1.0)
|
98
97
|
values (1.8.0)
|
99
98
|
yard (0.9.26)
|
100
99
|
yard-doctest (0.1.17)
|
101
100
|
minitest
|
102
101
|
yard
|
102
|
+
zeitwerk (2.5.1)
|
103
103
|
|
104
104
|
PLATFORMS
|
105
105
|
ruby
|
@@ -117,4 +117,4 @@ DEPENDENCIES
|
|
117
117
|
yard-doctest (~> 0.1)
|
118
118
|
|
119
119
|
BUNDLED WITH
|
120
|
-
2.2.
|
120
|
+
2.2.32
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
* Support for [resetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#reset_memo_wise-instance_method) and [presetting](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise#preset_memo_wise-instance_method) memoized values
|
21
21
|
* Support for memoization on frozen objects
|
22
22
|
* Support for memoization of class and module methods
|
23
|
+
* Support for inheritance of memoized class and instance methods
|
23
24
|
* Full [documentation](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise) and [test coverage](https://codecov.io/gh/panorama-ed/memo_wise)!
|
24
25
|
|
25
26
|
## Installation
|
@@ -56,6 +57,15 @@ class Example
|
|
56
57
|
x
|
57
58
|
end
|
58
59
|
memo_wise :slow_value
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# maintains privacy of the memoized method
|
64
|
+
def private_slow_method(x)
|
65
|
+
sleep x
|
66
|
+
x
|
67
|
+
end
|
68
|
+
memo_wise :private_slow_method
|
59
69
|
end
|
60
70
|
|
61
71
|
ex = Example.new
|
@@ -102,28 +112,38 @@ For more usage details, see our detailed [documentation](#documentation).
|
|
102
112
|
|
103
113
|
## Benchmarks
|
104
114
|
|
105
|
-
Benchmarks
|
106
|
-
[`benchmark-ips`](https://github.com/evanphx/benchmark-ips). All benchmarks are
|
107
|
-
run on Ruby 3.0.1, except as indicated below for specific gems. Benchmarks are
|
108
|
-
run in GitHub Actions and updated in every PR that changes code.
|
115
|
+
Benchmarks are run in GitHub Actions, and the tables below are updated with every code change. **Values >1.00x represent how much _slower_ each gem’s memoized value retrieval is than the latest commit of `MemoWise`**, according to [`benchmark-ips`](https://github.com/evanphx/benchmark-ips) (2.9.2).
|
109
116
|
|
110
|
-
|
111
|
-
is than the latest commit of `memo_wise`.**
|
117
|
+
Results using Ruby 3.0.3:
|
112
118
|
|
113
|
-
|Method arguments|`
|
119
|
+
|Method arguments|`Dry::Core`\* (0.7.1)|`Memery` (1.4.0)|
|
120
|
+
|--|--|--|
|
121
|
+
|`()` (none)|1.36x|19.42x|
|
122
|
+
|`(a)`|2.47x|11.39x|
|
123
|
+
|`(a, b)`|0.44x|2.16x|
|
124
|
+
|`(a:)`|2.30x|22.89x|
|
125
|
+
|`(a:, b:)`|0.47x|4.54x|
|
126
|
+
|`(a, b:)`|0.47x|4.33x|
|
127
|
+
|`(a, *args)`|0.83x|2.09x|
|
128
|
+
|`(a:, **kwargs)`|0.76x|2.85x|
|
129
|
+
|`(a, *args, b:, **kwargs)`|0.61x|1.55x|
|
130
|
+
|
131
|
+
\* `Dry::Core`
|
132
|
+
[may cause incorrect behavior caused by hash collisions](https://github.com/dry-rb/dry-core/issues/63).
|
133
|
+
|
134
|
+
Results using Ruby 2.7.5 (because these gems raise errors in Ruby 3.x):
|
135
|
+
|
136
|
+
|Method arguments|`DDMemoize` (1.0.0)|`Memoist` (0.16.2)|`Memoized` (1.0.2)|`Memoizer` (1.0.3)|
|
114
137
|
|--|--|--|--|--|
|
115
|
-
|`()` (none)|
|
116
|
-
|`(a)`|
|
117
|
-
|`(a, b)`|
|
118
|
-
|`(a:)`|
|
119
|
-
|`(a:, b:)`|
|
120
|
-
|`(a, b:)`|
|
121
|
-
|`(a, *args)`|
|
122
|
-
|`(a:, **kwargs)`|
|
123
|
-
|`(a, *args, b:, **kwargs)`|
|
124
|
-
|
125
|
-
_\*Indicates a benchmark run on Ruby 2.7.3 because the gem raises errors in Ruby
|
126
|
-
3.0.1 due to its incorrect handling of keyword arguments._
|
138
|
+
|`()` (none)|36.84x|3.56x|1.68x|4.19x|
|
139
|
+
|`(a)`|27.50x|18.85x|13.97x|15.99x|
|
140
|
+
|`(a, b)`|3.27x|2.34x|1.85x|2.05x|
|
141
|
+
|`(a:)`|37.22x|30.09x|25.57x|27.28x|
|
142
|
+
|`(a:, b:)`|5.25x|4.38x|3.80x|4.02x|
|
143
|
+
|`(a, b:)`|5.08x|4.15x|3.56x|3.78x|
|
144
|
+
|`(a, *args)`|3.17x|2.32x|1.96x|2.01x|
|
145
|
+
|`(a:, **kwargs)`|2.87x|2.42x|2.10x|2.21x|
|
146
|
+
|`(a, *args, b:, **kwargs)`|2.05x|1.76x|1.63x|1.65x|
|
127
147
|
|
128
148
|
You can run benchmarks yourself with:
|
129
149
|
|
@@ -136,6 +156,24 @@ $ bundle exec ruby benchmarks.rb
|
|
136
156
|
If your results differ from what's posted here,
|
137
157
|
[let us know](https://github.com/panorama-ed/memo_wise/issues/new)!
|
138
158
|
|
159
|
+
## Thread Safety
|
160
|
+
|
161
|
+
MemoWise makes the following **thread safety** guarantees on all supported Ruby
|
162
|
+
versions:
|
163
|
+
|
164
|
+
1. **Before** a value has been memoized
|
165
|
+
|
166
|
+
* Contended calls from multiple threads...
|
167
|
+
* May each call the original method
|
168
|
+
* May return different valid results (when the method is nondeterministic,
|
169
|
+
like `rand`)
|
170
|
+
* Will memoize exactly one valid return value
|
171
|
+
|
172
|
+
2. **After** a value has been memoized
|
173
|
+
|
174
|
+
* Contended calls from multiple threads...
|
175
|
+
* Always return the same memoized value
|
176
|
+
|
139
177
|
## Documentation
|
140
178
|
|
141
179
|
### Documentation is Automatically Generated
|
@@ -185,6 +223,20 @@ memoization between your tests with something like:
|
|
185
223
|
after(:each) { helper.reset_memo_wise }
|
186
224
|
```
|
187
225
|
|
226
|
+
## Further Reading
|
227
|
+
|
228
|
+
We presented at RubyConf 2021:
|
229
|
+
|
230
|
+
- Achieving Fast Method Metaprogramming: Lessons from `MemoWise`
|
231
|
+
([slides](https://docs.google.com/presentation/d/1XgERQ0YHplwJKM3wNQwZn584d_9szYZp2WsDEXoY_7Y/edit?usp=sharing) /
|
232
|
+
[benchmarks](https://gist.github.com/JacobEvelyn/17b7b000e50151c30eaea928f1fcdc11))
|
233
|
+
|
234
|
+
And we've written more about `MemoWise` in a series of blog posts:
|
235
|
+
|
236
|
+
- [Introducing: MemoWise](https://medium.com/building-panorama-education/introducing-memowise-51a5f0523489)
|
237
|
+
- [Optimizing MemoWise Performance](https://ja.cob.land/optimizing-memowise-performance)
|
238
|
+
- [Esosteric Ruby in MemoWise](https://jemma.dev/blog/esoteric-ruby-in-memowise)
|
239
|
+
|
188
240
|
## Logo
|
189
241
|
|
190
242
|
`MemoWise`'s logo was created by [Luci Cooke](https://www.lucicooke.com/). The
|
@@ -214,11 +266,14 @@ Then carry out these steps:
|
|
214
266
|
|
215
267
|
1. Update `CHANGELOG.md`:
|
216
268
|
- Add an entry for the upcoming version _x.y.z_
|
269
|
+
- Add a link for this version's comparison to the bottom of `CHANGELOG.md`
|
217
270
|
- Move content from _Unreleased_ to the upcoming version _x.y.z_
|
271
|
+
- Change _Unreleased_ section to say `- Nothing yet!`
|
218
272
|
- Commit with title `Update CHANGELOG.md for x.y.z`
|
219
273
|
|
220
274
|
2. Update `lib/memo_wise/version.rb`
|
221
275
|
- Replace with upcoming version _x.y.z_
|
276
|
+
- Run `bundle install` to update `Gemfile.lock`
|
222
277
|
- Commit with title `Bump version to x.y.z`
|
223
278
|
|
224
279
|
3. `bundle exec rake release`
|
data/benchmarks/Gemfile
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
ruby ">= 2.7.
|
5
|
+
ruby ">= 2.7.5"
|
6
6
|
|
7
|
-
gem "benchmark-ips", "2.9.
|
7
|
+
gem "benchmark-ips", "2.9.2"
|
8
8
|
|
9
9
|
if RUBY_VERSION > "3"
|
10
|
+
gem "dry-core", "0.7.1"
|
10
11
|
gem "memery", "1.4.0"
|
11
12
|
else
|
13
|
+
gem "ddmemoize", "1.0.0"
|
12
14
|
gem "memoist", "0.16.2"
|
13
15
|
gem "memoized", "1.0.2"
|
14
16
|
gem "memoizer", "1.0.3"
|