Faster permitted_scalar_filter #33758
@schneems Thanks for taking the time to investigate all these optimizations. I'd like to see us contextualize the microbenchmarks with something higher level. Even if it's literally just the dreaded Hello World that runs through an entire Rails request with template rendering and such. That would give us some sense of scale.
Because these optimizations aren't free; code that's more verbose/less idiomatic/uses multiple exits/whatever should pay for its cost with real, measurable benefits for actual applications.
Thanks again for looking into this! I'm sure there are optimizations that sit in hot-paths were the trade-off of worse code is worth the gain
In fact, I'd go as far as to say it'd be really awesome if we could get a general test app running that we can run any kind of change against. Not just these optimizations, but things we fear might be performance regressions. And a way to measure major versions against each other for improvements.
The memory savings are 0.013919 mb and on codetriage it takes about 0.95 mb of memory for a request. So, in this case, it's a 1.4 % reduction in memory per request. I couldn't get that to show up on a benchmark by itself in any kind of statistically significant way via benchmark.ips or benchmark.bmbm.
This location was bad enough to show up on derailed's top offenders for memory allocation before the change.
In general I'm happy to. Feel free to call me in where you're curious. My specific benchmark is a giant, concurrent end-to-end test based on Discourse - so it's only good for finding larger deltas in runtime and it can be finicky to set up. But when it works, it's a pretty realistic assessment. I'm running it against this change right now, but it takes at least a couple of hours.
Pretty certainly. I'm working on an intermediate benchmark now - still a Rails app, but one with a lot fewer dependencies and less non-Ruby time. With luck, that will be a good "something in the middle." For microbenchmarks, you'd probably want an approach kind of like what the Ruby repo does internally - a lot of tiny benchmarks that just hit a few methods each. ActiveSupport would be really easy to do this with and I expect it would work really well. It's also pretty similar to what @schneems did when writing this, just checked into the tree to be rerunnable.
It doesn't look like 'try' is much of the end-to-end performance. Specifically, with half a million HTTP requests for each version, I see the version with @schneems' change running 0.08% faster for the median request (like, less than a tenth of one percent.) But my variance on that measurement is around 3.2 reqs/second (std dev around 1.79), on a difference of about 0.14 requests/second. Basically it's far too small a measurement to be meaningful at this test size and variance.
I adopted the new suggestions. Thanks! I had to add a delegation of
In the interest of seeing any difference, I decided to benchmark this on a "hello world" app.
First I made a dev app that points to my local rails codebase:
Then I added a controller action that hits this codepath:
I then hit the app using derailed benchmarks with and without this patch.
memory profiling "hello world" app
For memory I ran:
Raw speed "hello world app"
For raw speed I hit this endpoint 10,000 times:
From everything i've seen in profiling % of memory allocation decrease is roughly a 1:1 correlation with execution speed. Profiling memory allocation is also way easier and much more consistent with less variability.
In terms of how much faster this makes an app entirely depends on the app. For codetriage it's about a 1% bump, for discourse 0.8% (ish) for an app that practically does nothing a 20% bump provided that you're "permit" -ing lots of keys. Mostly it's a question of how much memory is the app in question allocating.
I can pretty confidently say that this code is faster. I cannot with any certainty try to say exactly what the impact is.
When running with code triage and derailed benchmarks and focusing on this file: Before 16199 /Users/rschneeman/Documents/projects/rails/actionpack/lib/action_controller/metal/strong_parameters.r After 2280 /Users/rschneeman/Documents/projects/rails/actionpack/lib/action_controller/metal/strong_parameters.rb