-
Notifications
You must be signed in to change notification settings - Fork 22
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
Increase activerecord benchmark size #297
Conversation
The Active Record benchmark doesn't do a whole lot. It load a single record without any association and just read a few fields. It also always fetch directly from the database. Here we add a single extra association, enable the query cache to remove IOs entirely, and access a few more attributes to exercise the casting code more Before: ``` interp: ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23] yjit: ruby 3.3.1 (2024-04-23 revision c56cd86388) +YJIT [arm64-darwin23] ------------ ----------- ---------- --------- ---------- ------------ ----------- bench interp (ms) stddev (%) yjit (ms) stddev (%) yjit 1st itr interp/yjit activerecord 29.4 2.8 13.2 2.7 1.55 2.23 ------------ ----------- ---------- --------- ---------- ------------ ----------- ``` After: ``` interp: ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23] yjit: ruby 3.3.1 (2024-04-23 revision c56cd86388) +YJIT [arm64-darwin23] ------------ ----------- ---------- --------- ---------- ------------ ----------- bench interp (ms) stddev (%) yjit (ms) stddev (%) yjit 1st itr interp/yjit activerecord 165.8 2.6 71.2 2.6 1.93 2.33 ------------ ----------- ---------- --------- ---------- ------------ ----------- ```
Alternatively, since Rails 7.2 should go out soon, we could wait and do the upgrade at the same time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to change the benchmarks over time, like in 03f091c, the benchmark got twice as fast due to changes to the benchmark, not YJIT or ruby, and it shows on the all-time view:
Thanks Jean this looks a lot nicer. Let's merge it. We want the headline benchmarks to be reasonably fair/representative 👍 |
The change looks good to me.
|
Those are fair points. I think it would make sense to generate a string as Benoit suggests. Wrt query cache, ideally we'd generate a lot of posts (time budget < 10s to generate), and then request random posts. Some of them would get cached, some not. |
This is generally true. But that's more the job of "rails-bench" or similar to simulate a full Rails request. If you simulate templating, you might as well also do the controller etc etc. Here the focus is Active Record itself. I think it make sense to only exercise Active Record APIs.
Similarly, IMO the goal is to show what's left to optimize. We can totally do the SQLite query I don't mind, but that's just gonna add some overhead that JITs can't do anything about. I guess it somewhat exercise C bindings, so why not.
I'd be concerned it would increase variance between runs. |
Right, agreed a controller etc is too much. I see it as like microbenchmark vs something representative of a real usage of ActiveRecord. Given this is not in microbenchmarks, I think a simple processing step is good.
It's part of real usages of ActiveRecord so I wouldn't remove it. And if the JIT can optimize anything around calling C methods it might be very much worthwhile (also there might be things to optimize in the sqlite3 C ext).
Also since the cache is global it would cause variance between iterations (not sure if that's what you meant by |
Is the cache part of sqlite itself? If so, the cache hit rate should actually be fairly high in a real server. I agree that there are concerns with variance. 🤔 What's the performance like with the cache on vs off? With respect to building a string to output, I do think it might make some sense to do that. If there is no output, a sufficiently advanced JIT may be able to eliminate the calls. |
It's this: https://guides.rubyonrails.org/caching_with_rails.html#sql-caching and per request, so normally it wouldn't hit but because we don't really have a request here it becomes effectively a global cache. |
Followup: #297 The idea was to totally skip the native code that can't be optimized much, but I suppose benchmarking the binding code kinda make sense anyway.
The Active Record benchmark doesn't do a whole lot. It load a single record without any association and just read a few fields. It also always fetch directly from the database.
Here we add a single extra association, enable the query cache to remove IOs entirely, and access a few more attributes to exercise the casting code more
Before:
After:
Profile before:
Profile after:
You can see that before the top leafs were mundane things such as allocating record, Sqlite3 and accessing various thread local state. After the change the top elements are more relevant things such as attribute access and casting, association mapping, etc.
NB: Given that
activerecord
is marked as headline, and this is quite a radical change, perhaps it can just be a new benchmark, calledactiverecord2
oractiverecord-read-heavy
or something?