s0nerik / fast_contacts

A faster way of accessing device's contacts list in Flutter
MIT License
15 stars 16 forks source link

Main isolate hangs upon receining a huge contacts list #6

Closed s0nerik closed 1 year ago

s0nerik commented 1 year ago

Due to decoding inefficiency, the main isolate hangs for a noticeable amount of time after receiving the method channel response. In my testing, I've observed a 160ms hang upon decoding 20k contacts on iPhone X.

Something has to be done to avoid this hang.

PcolBP commented 1 year ago
  1. Im thinking about delay? We can not pass custom data between isolates which is pain. But we can make it a little slower - will take a little more time to load contacts but won't freeze ui with huge chunk.

  2. Other thing that I'm thinking is stream? But there would have to be some mechanism to tell user that its all of contacts. We wouldn't then need to reallocate all the data in one step.

  3. Third option that Im thinking of is to portion data on smaller chunks and decode them in sync way? Something similar to first approach but with or without delay?

What do you think? Or am I thinking in bad way?

s0nerik commented 1 year ago

I thought about the same options as well. It looks like everything boils down to chunking the data processing in some way or another while still performing the <raw_data/map/etc> -> List<Contact> conversion on the main isolate.

The real question is how and where exactly to perform the data partitioning. In that regard, I see two possible approaches:

  1. Change the underlying method channel interface to require paginated data access on the platform side. This would make native platform plugins a bit more verbose and harder to understand but sounds promising to me. Also, requires no weird trickery with data, which is good as well.
  2. Keep the same method channel interface, but request data from the method channel within a separate isolate, returning the data to the main isolate as an encoded byte array using TransferableTypedData (which saves from copying between the isolates) and then decoding the data from TransferableTypedData chunk by chunk in Dart. This would require some trickery, but allows to not pollute the native plugins with extra paging-related code.

I already started working on the second approach, and even have a working prototype, but I want to compare these two in terms of performance/readability/hackiness.

I think it's safe to assume that this issue is gonna be solved in a couple of weeks.

s0nerik commented 1 year ago

The UI freezes issue is fixed as of v3.0.0.

It was achieved by requesting and decoding already-fetched contacts from the native platform in chunks. This means that the encoding-decoding roundtrip and related copying still happen, just doesn't freeze the UI now.

There is still some potential to gain an additional ~10-40% speed-up by eliminating the unnecessary data copying between the native and Dart sides. But this would require much more work in order to make malloced buffers available and usable to Dart, Swift and Java.