-
Notifications
You must be signed in to change notification settings - Fork 49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache transformed keys #402
Cache transformed keys #402
Conversation
Hi @skryukov, thank you for the patch! I didn't notice transforming keys is one of the bottlenecks. I highly appreciate it. diff --git a/lib/alba.rb b/lib/alba.rb
index afcf8de..e3626f2 100644
--- a/lib/alba.rb
+++ b/lib/alba.rb
@@ -117,6 +117,7 @@ module Alba
# When it's a Class or a Module, it should have some methods, see {Alba::DefaultInflector}
def inflector=(inflector)
@inflector = inflector_from(inflector)
+ reset_transform_keys
end
# @param block [Block] resource body
@@ -145,6 +146,7 @@ module Alba
# Configure Alba to symbolize keys
def symbolize_keys!
@symbolize_keys = true
+ @_transformed_keys = nil
end
# Configure Alba to stringify (not symbolize) keys
@@ -171,21 +173,19 @@ module Alba
def transform_key(key, transform_type:)
raise Alba::Error, 'Inflector is nil. You must set inflector before transforming keys.' unless inflector
- @_transformed_keys ||= Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
- @_transformed_keys[inflector][transform_type][@symbolize_keys][key] ||=
- begin
- key = key.to_s
-
- k = case transform_type
- when :camel then inflector.camelize(key)
- when :lower_camel then inflector.camelize_lower(key)
- when :dash then inflector.dasherize(key)
- when :snake then inflector.underscore(key)
- else raise Alba::Error, "Unknown transform type: #{transform_type}"
- end
-
- regularize_key(k)
- end
+ @_transformed_keys[transform_type][key] ||= begin
+ key = key.to_s
+
+ k = case transform_type
+ when :camel then inflector.camelize(key)
+ when :lower_camel then inflector.camelize_lower(key)
+ when :dash then inflector.dasherize(key)
+ when :snake then inflector.underscore(key)
+ else raise Alba::Error, "Unknown transform type: #{transform_type}"
+ end
+
+ regularize_key(k)
+ end
end
# Register types, used for both builtin and custom types
@@ -213,7 +213,7 @@ module Alba
@_on_error = :raise
@_on_nil = nil
@types = {}
- @_transformed_keys = nil
+ reset_transform_keys
register_default_types
end
@@ -320,6 +320,10 @@ module Alba
register_type(:"ArrayOf#{t}", check: ->(d) { d.is_a?(Array) && d.all? { _1.is_a?(t) } })
end
end
+
+ def reset_transform_keys
+ @_transformed_keys = Hash.new { |h, k| h[k] = {} }
+ end
end
reset! This reduces nesting of a cache hash and instead resets the cache after some methods ( |
I noticed an interesting fact that with YJIT enabled, |
Thanks for the review @okuramasafumi, I applied your patch (also called For me, running on Ruby 3.4.1 YJIT + OJ:
YJIT without Oj:
|
@skryukov Thank you, I cannot see your new commit on GitHub, maybe you didn't push it? |
Ooops, just pushed, sorry 😂 Regarding Oj: Yes, it now makes sense to compare JSON >= 2.9 with Oj. However, I believe the results will depend on the data. My colleague showed me some benchmarks where flat responses with simpler data types might still perform better when using Oj. |
@skryukov Great, thank you! |
0d3906e
to
82b54db
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #402 +/- ##
=======================================
Coverage 99.83% 99.83%
=======================================
Files 14 14
Lines 592 599 +7
Branches 151 153 +2
=======================================
+ Hits 591 598 +7
Misses 1 1 ☔ View full report in Codecov by Sentry. |
@skryukov Thank you! |
I noticed Alba was quite slow with the keys transformation feature enabled. After a quick review of the code, I decided to add a memoization mechanism directly in the main
Alba
module. This makes the code more transparent. While putting the memoization at the resource-level cache was also an option, it would still require checkingAlba.inflector
andAlba.symbolize_keys
, which would look less obvious.Additionally, I've updated the benchmark: it now supports Ruby 3.4, Rails and Representable serializers pass the output checks. I've updated the README (without the new results - I think it's better for @okuramasafumi to run them, especially interesting to see the latest
json
gem being faster thanOj.optimize_rails
😄). Finally, the script now displays the gem versions used at the end.Benchmark results (we can drop transformation from benchmark once this PR is reviewed).
Note that
alba_with_transformation
is faster since the "pure" version callsregularize_key
all the time.BEFORE
ips:
memory:
AFTER
ips:
memory: