-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
coming from here
Proposal
Refactor heavy operations when user starts to type as main thread hangs on 4x and 6x cpu throttling
Problem
As soon as the user starts to type, the main thread gets blocked for a noticeable amount of time and user can’t perform any other action. The reason the main thread gets blocked is due to heavy JS operations. Using Chrome’s profiler on new HT accounts, we can see that there are many pieces that comes together and halts the main thread. Details of them are below:
- Right now, we are updating a flag hasDraft in reports_ collection and as a result SidebarLinksData gets re-rendered. The main issue arises in Report.setReportWithDraft as it’s calling Onyx.merge as a result of which chatReports gets recreated. This whole process is what’s mainly causing the issue in blocking the main thread. If we comment this Report.setReportWithDraft everything seems to work fine.
- We also have UnreadIndicatorUpdater which listens to reports_ collection then updates the read count on relevant platforms, without checking if it’s already read or not. And since, we are updating hasDraft in above step, reports_ collection gets updated and this is fired, each time hasDraft is toggled or each time reports_ is updated.
- getOrdererdReportIDs still have some heavy operations including JSON.Stringify . In the code, we are passing in allReportDict and it’s basically all the key/ value pair for reports and stringifying them along with other 5 parameters is a heavy task and it takes around 25-30 ms itself.
- JS utility functions like lodashGet really gets slower when CPU has limited availability, in our case testing this with CPU throttling.
- String.trim appears to be consuming more milliseconds than necessary. It is used to trim the whitespaces around message. In my case on new HT accounts, I wasn’t able to see any message on the screen because of maybe lots of data but this function was still being called and took around 33 ms.
- There’s also some interesting thing about String literals or template literals, they tend to consume more milliseconds than the plain old concatenation using a plus operator :thinking_face:
- In getOrderedReportIDs we are looping over reportsToDisplay two times and this can be combined into one.
## Solution
There’s a lot going on in only on initial letter typing. To get near to an ideal performance, I tried to apply the following solutions to fix the above findings:
- We can have a separate object in Onyx which would contain the draftReportIDs , so this way we can avoid using Onyx.merge on a heavy collection and use this lighter one. Also, we can avoid re-rendering the SidebarLinksData totally with this approach, since we won’t be updating chatReports now and to keep the draftReportIDs lighter we can add reportID to it if there’s a draft and remove the reportID , when there’s no draft. And since, it will be an object we won’t have to iterate over and we can just get the draft status using draftReportIDs[reportID] .
- The simplest solution is to check whether the previous read count is equal to the upcoming one, if it’s equal we will not update unread status.
- Instead of passing in whole allReportDict we can pass only the keys, which is basically the reportID to JSON.Stringify and doing so reduces the time by 15-20 ms.
- Only issue I saw with this was now drafts are not sorted in LHN each time we start to type. This is something we can fix later.
- Replacing loadshGet with typically accessing properties only in places which were taking noticeable milliseconds.
- For String.trim I decided to apply this method only to those messages who has some data and not every other message.
- On latest HT accounts, this was the specific case and doing so resulted in saved milliseconds.
- For String literals, I replaced only the places which were consuming more milliseconds. I replaced them with typical way of concatenating two strings.
- We are looping over reportsToDisplay twice, so I refactored and combined the operations in one loop.
If we look at before and after results, we save about 400 milliseconds 👏
Testing Environment:
Web at 6x CPU throttling on profiling build with production mode
Macbook M1 Pro
Note:
I haven’t tested this on Mobile and Desktop platforms, so this should also be tested when implementing this proposal.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status