memo_wise 1.1.0 → 1.5.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/.rubocop.yml +13 -1
- data/CHANGELOG.md +46 -6
- data/Gemfile.lock +29 -29
- data/README.md +72 -39
- data/benchmarks/Gemfile +2 -2
- data/benchmarks/benchmarks.rb +103 -179
- data/lib/memo_wise/internal_api.rb +110 -202
- data/lib/memo_wise/version.rb +1 -1
- data/lib/memo_wise.rb +95 -137
- data/memo_wise.gemspec +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d94c6c36ed049177dffb78f2bc5b0196d06e7b334643770163b7140ee2e77e2
|
4
|
+
data.tar.gz: 2be9bd8578a7357aabe70d9f748cb57fedf50708028ca7c3ed55815814b7ddc2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 848080fdd9596f0a0f477464ddbc2009ab2c5f3caec56d3fa3c97dd275aca7687c94793af8124a7dd567a1c33abcdf3474cb2e13ab6b8b91f47302a0059b6916
|
7
|
+
data.tar.gz: e79de4347bbc5cd0701095574dfb7faafb320dc3f6e1de1cb0978ebd1fcdce5e65ca0c8fa2e0079d72385acabea582caac41a0c6c553078f69d1337006422e0e
|
@@ -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/.rubocop.yml
CHANGED
@@ -1,5 +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
|
3
12
|
|
4
13
|
Style/DocumentDynamicEvalDefinition:
|
5
14
|
Enabled: false
|
15
|
+
|
16
|
+
Style/MultipleComparison:
|
17
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -6,24 +6,59 @@ 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
|
+
|
10
|
+
- Nothing yet!
|
11
|
+
|
12
|
+
## [1.5.0] - 2021-12-17
|
13
|
+
|
14
|
+
- Remove optimization for truthy results to fix race condition bugs
|
15
|
+
- Switch to a simpler internal data structure to fix several classes of bugs
|
16
|
+
that the previous few versions were unable to sufficiently address
|
17
|
+
|
18
|
+
## [1.4.0] - 2021-12-10
|
19
|
+
|
20
|
+
### Fixed
|
21
|
+
|
22
|
+
- Fix several bugs related to classes inheriting memoized methods
|
23
|
+
from multiple modules or a parent class ([#241](https://github.com/panorama-ed/memo_wise/pull/241))
|
24
|
+
|
25
|
+
## [1.3.0] - 2021-11-22
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- Fix thread-safety issue in concurrent calls to zero-arg method in unmemoized
|
30
|
+
state which resulted in a `nil` value being accidentally returned in one thread
|
31
|
+
- Fix bugs related to child classes inheriting from parent classes that use
|
32
|
+
`MemoWise`
|
33
|
+
|
34
|
+
## [1.2.0] - 2021-11-10
|
35
|
+
|
9
36
|
### Updated
|
10
|
-
-
|
37
|
+
- Improved performance of all methods by using an outer Array instead of a Hash
|
38
|
+
- Improved performance for multi-argument methods and simplify internal data
|
39
|
+
structures
|
40
|
+
|
11
41
|
### Fixed
|
12
|
-
-
|
42
|
+
- Removed use of #hash due to potential of hash collisions
|
43
|
+
- Updated internal local variable names to avoid name collisions with method
|
44
|
+
arguments
|
45
|
+
|
13
46
|
### Breaking Changes
|
14
47
|
- None
|
15
48
|
|
16
49
|
## [1.1.0] - 2021-07-29
|
17
50
|
### Updated
|
18
|
-
-
|
51
|
+
- Improved performance across the board by:
|
19
52
|
- removing `Hash#fetch`
|
20
53
|
- using `Array#hash`
|
21
54
|
- avoiding multi-layer hash lookups for multi-argument methods
|
22
55
|
- optimizing for truthy results
|
23
56
|
- Add `dry-core` to benchmarks in README
|
57
|
+
|
24
58
|
### Fixed
|
25
|
-
-
|
26
|
-
-
|
59
|
+
- Fixed usage on module singleton classes
|
60
|
+
- Fixed usage on module which would be extended by other classes
|
61
|
+
|
27
62
|
### Breaking Changes
|
28
63
|
- None
|
29
64
|
|
@@ -31,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
31
66
|
### Added
|
32
67
|
- Support for `.preset_memo_wise` on class methods
|
33
68
|
- Support for `.reset_memo_wise` on class methods
|
69
|
+
|
34
70
|
### Updated
|
35
71
|
- Improved performance for common cases by reducing array allocations
|
36
72
|
|
@@ -80,7 +116,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
80
116
|
- Panolint
|
81
117
|
- Dependabot setup
|
82
118
|
|
83
|
-
[Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v1.
|
119
|
+
[Unreleased]: https://github.com/panorama-ed/memo_wise/compare/v1.5.0...HEAD
|
120
|
+
[1.5.0]: https://github.com/panorama-ed/memo_wise/compare/v1.4.0...v1.5.0
|
121
|
+
[1.4.0]: https://github.com/panorama-ed/memo_wise/compare/v1.3.0...v1.4.0
|
122
|
+
[1.3.0]: https://github.com/panorama-ed/memo_wise/compare/v1.2.0...v1.3.0
|
123
|
+
[1.2.0]: https://github.com/panorama-ed/memo_wise/compare/v1.1.0...v1.2.0
|
84
124
|
[1.1.0]: https://github.com/panorama-ed/memo_wise/compare/v1.0.0...v1.1.0
|
85
125
|
[1.0.0]: https://github.com/panorama-ed/memo_wise/compare/v0.4.0...v1.0.0
|
86
126
|
[0.4.0]: https://github.com/panorama-ed/memo_wise/compare/v0.3.0...v0.4.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.5.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.1.
|
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,11 +37,11 @@ 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)
|
@@ -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,8 @@
|
|
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
|
24
|
+
* Documented and tested [thread-safety guarantees](#thread-safety)
|
23
25
|
* Full [documentation](https://rubydoc.info/github/panorama-ed/memo_wise/MemoWise) and [test coverage](https://codecov.io/gh/panorama-ed/memo_wise)!
|
24
26
|
|
25
27
|
## Installation
|
@@ -56,6 +58,15 @@ class Example
|
|
56
58
|
x
|
57
59
|
end
|
58
60
|
memo_wise :slow_value
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# maintains privacy of the memoized method
|
65
|
+
def private_slow_method(x)
|
66
|
+
sleep x
|
67
|
+
x
|
68
|
+
end
|
69
|
+
memo_wise :private_slow_method
|
59
70
|
end
|
60
71
|
|
61
72
|
ex = Example.new
|
@@ -102,44 +113,38 @@ For more usage details, see our detailed [documentation](#documentation).
|
|
102
113
|
|
103
114
|
## Benchmarks
|
104
115
|
|
105
|
-
Benchmarks
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|`(
|
117
|
-
|`(a)`|
|
118
|
-
|`(a
|
119
|
-
|`(a
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|`(
|
135
|
-
|`(a)`|
|
136
|
-
|`(a, b)`|
|
137
|
-
|`(a:)`|baseline|25.88x|21.74x|17.68x|19.17x|
|
138
|
-
|`(a:, b:)`|baseline|5.72x|4.57x|3.90x|4.42x|
|
139
|
-
|`(a, b:)`|baseline|5.63x|5.06x|4.20x|4.18x|
|
140
|
-
|`(a, *args)`|baseline|5.13x|3.89x|3.25x|3.31x|
|
141
|
-
|`(a:, **kwargs)`|baseline|3.23x|2.55x|2.33x|2.47x|
|
142
|
-
|`(a, *args, b:, **kwargs)`|baseline|2.51x|2.00x|1.84x|1.88x|
|
116
|
+
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).
|
117
|
+
|
118
|
+
Results using Ruby 3.0.3:
|
119
|
+
|
120
|
+
|Method arguments|`Dry::Core`\* (0.7.1)|`Memery` (1.4.0)|
|
121
|
+
|--|--|--|
|
122
|
+
|`()` (none)|1.06x|11.96x|
|
123
|
+
|`(a)`|1.99x|10.19x|
|
124
|
+
|`(a, b)`|0.39x|1.94x|
|
125
|
+
|`(a:)`|1.80x|19.36x|
|
126
|
+
|`(a:, b:)`|0.43x|4.26x|
|
127
|
+
|`(a, b:)`|0.39x|3.97x|
|
128
|
+
|`(a, *args)`|0.83x|1.85x|
|
129
|
+
|`(a:, **kwargs)`|0.77x|2.94x|
|
130
|
+
|`(a, *args, b:, **kwargs)`|0.59x|1.57x|
|
131
|
+
|
132
|
+
\* `Dry::Core`
|
133
|
+
[may cause incorrect behavior caused by hash collisions](https://github.com/dry-rb/dry-core/issues/63).
|
134
|
+
|
135
|
+
Results using Ruby 2.7.5 (because these gems raise errors in Ruby 3.x):
|
136
|
+
|
137
|
+
|Method arguments|`DDMemoize` (1.0.0)|`Memoist` (0.16.2)|`Memoized` (1.0.2)|`Memoizer` (1.0.3)|
|
138
|
+
|--|--|--|--|--|
|
139
|
+
|`()` (none)|24.30x|2.57x|1.19x|2.98x|
|
140
|
+
|`(a)`|21.68x|14.63x|11.13x|12.71x|
|
141
|
+
|`(a, b)`|3.18x|2.36x|1.86x|2.06x|
|
142
|
+
|`(a:)`|30.62x|24.52x|21.44x|22.61x|
|
143
|
+
|`(a:, b:)`|5.25x|4.40x|3.80x|4.04x|
|
144
|
+
|`(a, b:)`|4.91x|4.06x|3.55x|3.83x|
|
145
|
+
|`(a, *args)`|3.10x|2.31x|1.96x|1.98x|
|
146
|
+
|`(a:, **kwargs)`|2.87x|2.40x|2.09x|2.20x|
|
147
|
+
|`(a, *args, b:, **kwargs)`|2.08x|1.82x|1.67x|1.70x|
|
143
148
|
|
144
149
|
You can run benchmarks yourself with:
|
145
150
|
|
@@ -152,6 +157,24 @@ $ bundle exec ruby benchmarks.rb
|
|
152
157
|
If your results differ from what's posted here,
|
153
158
|
[let us know](https://github.com/panorama-ed/memo_wise/issues/new)!
|
154
159
|
|
160
|
+
## Thread Safety
|
161
|
+
|
162
|
+
MemoWise makes the following **thread safety** guarantees on all supported Ruby
|
163
|
+
versions:
|
164
|
+
|
165
|
+
1. **Before** a value has been memoized
|
166
|
+
|
167
|
+
* Contended calls from multiple threads...
|
168
|
+
* May each call the original method
|
169
|
+
* May return different valid results (when the method is nondeterministic,
|
170
|
+
like `rand`)
|
171
|
+
* Will memoize exactly one valid return value
|
172
|
+
|
173
|
+
2. **After** a value has been memoized
|
174
|
+
|
175
|
+
* Contended calls from multiple threads...
|
176
|
+
* Always return the same memoized value
|
177
|
+
|
155
178
|
## Documentation
|
156
179
|
|
157
180
|
### Documentation is Automatically Generated
|
@@ -203,10 +226,17 @@ after(:each) { helper.reset_memo_wise }
|
|
203
226
|
|
204
227
|
## Further Reading
|
205
228
|
|
206
|
-
We
|
229
|
+
We presented at RubyConf 2021:
|
230
|
+
|
231
|
+
- Achieving Fast Method Metaprogramming: Lessons from `MemoWise`
|
232
|
+
([slides](https://docs.google.com/presentation/d/1XgERQ0YHplwJKM3wNQwZn584d_9szYZp2WsDEXoY_7Y/edit?usp=sharing) /
|
233
|
+
[benchmarks](https://gist.github.com/JacobEvelyn/17b7b000e50151c30eaea928f1fcdc11))
|
234
|
+
|
235
|
+
And we've written more about `MemoWise` in a series of blog posts:
|
207
236
|
|
208
237
|
- [Introducing: MemoWise](https://medium.com/building-panorama-education/introducing-memowise-51a5f0523489)
|
209
238
|
- [Optimizing MemoWise Performance](https://ja.cob.land/optimizing-memowise-performance)
|
239
|
+
- [Esosteric Ruby in MemoWise](https://jemma.dev/blog/esoteric-ruby-in-memowise)
|
210
240
|
|
211
241
|
## Logo
|
212
242
|
|
@@ -237,11 +267,14 @@ Then carry out these steps:
|
|
237
267
|
|
238
268
|
1. Update `CHANGELOG.md`:
|
239
269
|
- Add an entry for the upcoming version _x.y.z_
|
270
|
+
- Add a link for this version's comparison to the bottom of `CHANGELOG.md`
|
240
271
|
- Move content from _Unreleased_ to the upcoming version _x.y.z_
|
272
|
+
- Change _Unreleased_ section to say `- Nothing yet!`
|
241
273
|
- Commit with title `Update CHANGELOG.md for x.y.z`
|
242
274
|
|
243
275
|
2. Update `lib/memo_wise/version.rb`
|
244
276
|
- Replace with upcoming version _x.y.z_
|
277
|
+
- Run `bundle install` to update `Gemfile.lock`
|
245
278
|
- Commit with title `Bump version to x.y.z`
|
246
279
|
|
247
280
|
3. `bundle exec rake release`
|