Improve loadRecords performance in SQLiteNormalizedCache #1519
Conversation
…hen queryFields involves arguments
|
Looks like the tests are bit flakey. Also, I do not have permission to re-run the tests. Please advise. |
|
Yes, the tests are extremely flaky - I'll kick them. That's awesome that it seems like it helps performance a bunch - can you explain a bit about why this improves performance? Would also love to hear thoughts from @martijnwalraven, who's spent the last several weeks poking around in the cache |
|
@designatednerd I have updated the original PR description (so future readers find it in one place) explaining the root cause of the problem and how I solved it. Hope it explains well. |
Co-authored-by: TizianoCoroneo <tizianocoroneo@me.com>
fd31996 to
293dd3b
Compare
|
Awesome, thanks so much for adding the detailed explanation! Again, going to defer to @martijnwalraven for final approval but the explanation helps a ton. |
|
@bharath2020 Good catch! This looks all right to me, so I think we should merge it. I'll be working on larger changes to query execution and caching though, and I think we may be able to eliminate this step completely. The only reason the returned records have to be ordered by the queried keys is because that is currently a requirement of the way There are also a number of other performance issues that I hope to fix by these changes. If you have any time to work on this, it would be great to have more performance tests in place to test the changes against. I recently added a first test case for parsing, but we have no realistic tests of data loading. And to be honest, 0.8 seconds still seems pretty high to load 200 records from a local database, so I suspect there is more we can do. |
I will see what I can do.
Definitely, 0.8 seconds is too high. I did follow up after the fix to see where the time is being spent. At this time it is still not clear. Will keep looking at it as actively as it is kind of a blocker for us. |
…in SQLiteNormalizedCache.loadRecords
|
@designatednerd Looks like this PR is good to merge. Let me know If there is anything else I need to take care of. Thanks. |
|
waiting on last looks from @martijnwalraven - once I merge this I'll work on cutting a new version, I think it's time. |
Motivation:
We noticed the performance of
loadRecordson SQLiteNormalizedCache degrades especially when the query field contains arguments making the CacheKey string larger in memory size. Per our readings, loading a nominal 200 records from the SQLite store takes around 4.2x seconds, and the fix in this PR decreases time consumed byloadRecordsto 0.8 seconds an improvement of 4x the current time.Here is the screenshot of TimeProfile indicating the time spent in the == infix operator.
What is the root cause of the performance issue?
To answer that let us take a look at existing code in the
loadRecordsmethodHere are a few problems from the above code:
==.The performance is evident in queries that involve query fields with arguments as shown in the below example
For the query
searchEmployees(organizationId: "<16 char UUID>"), a sample CacheKey for a record would beQUERY_ROOT.searchEmployees(orignzationId: "<16 char UUID>").<EmployeeField>and this makesCacheKeycomparison even more time-consuming as the length of theCacheKeyincreases, which is what happened in our case.Solution:
Reduce the complexity of the code snippet to O(N) following the below steps:
[Record]returned byselectRecordsmethod into a hashmaprecordsIndexMapwith key beingCacheKeyand value is the index of the record in[Record]. This provides an O(1) time complexity to lookuprecordsIndexMapfor a givenCacheKeyto find the matchingRecordfrom the list of records[Record]lrecordsOrNilwhile doing a lookup withrecordsIndexMap(basically a hashmap of CacheKeys) making the entire code snippet's complexity O(N)thus bringing down the overall performance by the 2 fold magnitude