Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
Optimize select_value, select_values, select_rows in Postgresql adapter. #14790
Reduces creating unused objects in select_value, select_values, and select_rows. The most dramatic reduction is in select_values which used to map(&:first) an array of single element arrays.
Dry up checking whether to exec with cache for and clear the result.
… whether to exec with cache for Postgresql adapter Reduces creating unused objects, with the most dramatic reduction in select_values which used to map(&:first) an array of single element arrays.
It is more about not creating needless garbage. The performance is always better because it is doing the same thing without steps that weren't being used, but how much greatly depends on the query.
In select_rows, the performance is the same because most the overhead is in the pg gem and postgres. The new version removes the construction of the ActiveRecord::Result instance and just returns result.values. The old version created ActiveRecord::Result with result.values and returned .rows (which is result.values) thus immediately throwing out the ActiveRecord::Result and returning the argument it passed into the constructor. But removing one constructor isn't going to be noticed performance wise compared with building a decent sized result set.
In select_value, it is also avoiding creating the ActiveRecord::Result and also values array, and just reading the first value of the first tuple directly off the PG::Result, modest gain of around 8% ips (before: 4729.1 (±3.1%) i/s after: 5139.9 (±3.3%) i/s) on a fast query (select value by PK), but again less garbage.
In select_values, it was doing ActiveRecord::Result.new(...).rows.map(&:first) now it just using the column_values of PG::Result which is a decent gain of about 18% on a select of ids (before 2085.1 (±4.3%) i/s after 2476.3 (±4.2%) i/s) on a smallish table (I did 365 rows). The more rows you have the more arrays it used to create.
To summarize, the biggest performance gain is select_values, it was also the most wasteful GC wise, but the performance benefit is dependent on the sql query. These methods were fast, now they are a little faster, but garbage tends to add up, so why create it when you don't need to?
And lastly, I DRYed up this pattern that was being repeated and I didn't want to add more repeats of it:
def execute_and_clear(sql, name, binds) result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) : exec_cache(sql, name, binds) ret = yield result result.clear ret end
I've been noticing some problems with leaking memory when creating lots of AR objects in a loop. Would this fix that problem? Using Rails 4.0.5 on Ruby 2.1 if it matters.
Basically, I'm doing something like:
This is what
@joevandyk this PR was not about fixing a memory leak!
If you experience memory leaks with ActiveRecord you should try to reproduce your issue with an self-contained script, see: Rails Guides and open a new issue here on GitHub.