question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Performance bottlneck interacting with statement cache in native

See original GitHub issue

Hi! I’m using SQLDelight on iOS. SQLDelight generates queries that properly setup a unique int identifiers for use with statement caching. I’m doing some performance testing of a date serialization function that we’re using for getting dates in and out of a sql column, and I noticed that SQLDelight interacting with the statement cache was the main bottleneck on inserts. Here’s a snippet of the query being used:

fun insert(Comment: Comment) {
        driver.execute(4,
                """INSERT OR REPLACE INTO Comment VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", 10) {
            bindString(1, Comment.field)
/* more bind code followed by notifyQueries*/
    }

We’re doing the following to bulk insert these objects:

queries.transaction {
    for (object in objects) {
        queries.insert(object)
    }
}

Heaviest trace: screen shot 2019-02-21 at 11 59 01 am

To insert 100k objects, it takes 17s on my machine. 9s of that is the above trace. Is there a more efficient way in SQLDelight to do this kind of bulk insert? Thanks!

Update: that trace is a bit misleading. That heavy stack is shared by inserts and fetches: 6s for inserts, 3s for fetches.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:34 (14 by maintainers)

github_iconTop GitHub Comments

2reactions
kpgalligancommented, Feb 25, 2019

To be clear, this will also make the Kotlin native sqlite/sqldelight performance way better than Android (presumably lack of jni). When multithreaded coroutines happens, I have some ideas about a super optimized mutation architecture, where all updates are sent to a single thread queue, and we take some of the sqlite thread sanity checks off, but that’s definitely not something we need to chat about soon.

2reactions
kpgalligancommented, Feb 25, 2019

Learned a lot about performance over the weekend. Here’s where we’re at.

A lot of the time is involved with the internal map and list accounting in stately. As each entry is added/removed, there’s an internal object “Node” that gets created. That’s for the linked list. Each one of those needs to be frozen, which was adding a lot of time, but also, for add/remove, you’re doing a lot of atomic reference assignment. It’s expensive.

Adding an object pool for Node sped that up dramatically. Object pool is kind of ugly, but Kotlin Native is not exactly optimized, so it is what it is.

After that, I was poking through the code some more. We remove the statement from the cache for both selects and mutations, but it only makes sense for selects. Keeping them in the cache for mutation statements means more significant performance increase. It also makes the object pool kind of pointless, at least in our case.

I’m taking one quick look now at another option. Keeping a weak ref to statements in a thread local. That’s about as fast as you’d be able to push it before sqlite itself becomes a significant portion of the proc time.

The speed diff depends largely on number of fields in table, as the slow down was a per-insert call. I ran a test with a 2-field table inserting 500k rows. Current lib is just under 40s, no thread local was about 10s, and with thread local the first shot was about 7s. 5x-ish. For tables with more cols, it’s kind of ~3x.

I just ran the original sample for 250k and the current sqldelight driver is about 17.5s and with all the tweaks it’s about 5.2s.

Making these changes does not change the sqldelight driver a whole lot, but it’s also not a huge priority to get into a release, but I’ll do a bit more poking then wrap a PR together to take a look at.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Reading and writing data to the cache - Apollo GraphQL Docs
You can read and write data directly to the Apollo Client cache, without communicating with your GraphQL server. You can interact with data...
Read more >
Dive into React Native performance - Engineering at Meta
The first on-disk response cache was reading the entire cache from the disk. By reading only the content required to fulfill a particular...
Read more >
Optimizing React Native performance - LogRocket Blog
These statements could cause serious performance issues in your ... Caching is another solution to image problems in a React Native app.
Read more >
React Native Performance: Do and Don't - Crowdbotics
The Image component in React Native handles caching images like web browsers, which is sometimes the cause of the above issues.
Read more >
How to improve the performance of a React Native app
Leaving the console statements in the source code when publishing React Native apps can cause some big bottlenecks in the JavaScript thread. One ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found