以下のissueをfixするPR. Major performance regression when preloading has_many_through association · Issue #12537 · rails/rails rails/rails#12537
commitはこれ https://github.com/yuroyoro/rails/commit/b348115f1d0cc8d3aa363568b6692ad312941a91
I profiled the benchmark code that preloads has_many_through association,
and found that most of time spent by
calling CollectionAssociation#reader method in associated_records_by_owner.
Here is a result of profiling associated_records_by_owner method.
- https://gist.github.com/yuroyoro/b68f673c2ac24f0fe29a
- (dumpfile) https://www.dropbox.com/s/lqsqr86nrfrqvlq/stackprof-cpu-prelading-4.2-2015-03-10-12-40-24.dump?dl=0
CollectionAssociation#reader creates new CollectionProxy object if it doesn't exists.
But, instantiate CollectionProxy is expensive operation.
Because creating the Relation object by calling Association#scope is expensive,
and merging it into CollectionProxy is also.
In this case, the association's records are already loaded by the preloader.
It just read records by Association#target that returns loaded records,
instead of reader method.
Here are benchmark results.
================================================================================
3.2.21 - postgresql
================================================================================
Calculating -------------------------------------
limit 100 has_many 13.000 i/100ms
limit 100 has_many_through
5.000 i/100ms
limit 100 double has_many_through
3.000 i/100ms
limit 1000 has_many 1.000 i/100ms
limit 1000 has_many_through
1.000 i/100ms
limit 1000 double has_many_through
1.000 i/100ms
-------------------------------------------------
limit 100 has_many 133.936 (± 3.7%) i/s - 676.000
limit 100 has_many_through
55.424 (± 7.2%) i/s - 275.000
limit 100 double has_many_through
33.253 (± 6.0%) i/s - 168.000
limit 1000 has_many 16.665 (± 6.0%) i/s - 84.000
limit 1000 has_many_through
6.633 (± 0.0%) i/s - 34.000
limit 1000 double has_many_through
3.664 (± 0.0%) i/s - 19.000
================================================================================
4.2.0 - postgresql
================================================================================
Calculating -------------------------------------
limit 100 has_many 10.000 i/100ms
limit 100 has_many_through
1.000 i/100ms
limit 100 double has_many_through
1.000 i/100ms
limit 1000 has_many 1.000 i/100ms
limit 1000 has_many_through
1.000 i/100ms
limit 1000 double has_many_through
1.000 i/100ms
-------------------------------------------------
limit 100 has_many 106.059 (± 7.5%) i/s - 530.000
limit 100 has_many_through
15.979 (± 6.3%) i/s - 79.000
limit 100 double has_many_through
8.896 (±11.2%) i/s - 45.000
limit 1000 has_many 13.351 (± 7.5%) i/s - 66.000
limit 1000 has_many_through
1.664 (± 0.0%) i/s - 9.000 in 5.438504s
limit 1000 double has_many_through
0.936 (± 0.0%) i/s - 5.000 in 5.352846s
================================================================================
4.2.1.rc3 (After) - postgresql
================================================================================
Calculating -------------------------------------
limit 100 has_many 10.000 i/100ms
limit 100 has_many_through
3.000 i/100ms
limit 100 double has_many_through
2.000 i/100ms
limit 1000 has_many 1.000 i/100ms
limit 1000 has_many_through
1.000 i/100ms
limit 1000 double has_many_through
1.000 i/100ms
-------------------------------------------------
limit 100 has_many 117.193 (± 6.8%) i/s - 590.000
limit 100 has_many_through
41.624 (± 7.2%) i/s - 207.000
limit 100 double has_many_through
25.446 (± 7.9%) i/s - 126.000
limit 1000 has_many 14.516 (± 6.9%) i/s - 72.000
limit 1000 has_many_through
4.742 (± 0.0%) i/s - 24.000
limit 1000 double has_many_through
2.816 (± 0.0%) i/s - 14.000