stakwork / sphinx-mac

Sphinx app for mac desktop
MIT License
4 stars 16 forks source link

Fix multiple memory leaks to improve performance #320

Closed rodhan closed 4 months ago

rodhan commented 4 months ago

This PR fixes some simple (but tough to track down) memory leaks in the Sphinx Mac app that were causing memory usage to increase significantly every time the user switched between chats, to the point where performance was severely impacted, especially on Intel platform.

Fixes https://github.com/stakwork/sphinx-mac/issues/169 and https://github.com/stakwork/sphinx-mac/issues/160

To verify the fix, first build and run the app from the test-flight branch and switch between some chats while observing the Memory Use in the Xcode Debug Navigator. Now checkout this PR's branch, build and run and switch between the same chats in the same order. Notice that overall memory usage has decreased significantly, and the usage delta when switching between chats is much more reasonable.

Additional details

I first generated some fake Contacts and Chats for test purposes. I created three contacts with associated Chats: "Zero Chats", "1000 Text Only Messages", "1000 Image Attachments". Using this data I recorded some baseline data, looking at overall memory usage as well as some specific object counts, e.g.

Test:

  1. Open app (initial chat Zero Chats)
  2. Open 1000 Texts
  3. Open 1000 Attachments
  4. Return to Zero Chats
  5. Repeat steps 2-4 x 3
  6. Open Memory Graph in Xcode and take readings

Baseline (test-flight branch @ eed709f) Memory: 214MB (192MB on Intel MacBook) AspectFillNSImageView count: 475 NewOnlyTextMessageCollectionViewitem count: 90 VerticallyCenteredButtonCell count: 236 ...

Exploring the Xcode Memory graph we see multiple instances of classes like NewChatViewController are being kept alive even after there are no useful references to them.

CleanShot 2024-03-01 at 12 42 47@2x

We can see the issue in realtime using the Allocations tool in Instruments:

CleanShot 2024-02-29 at 21 40 40

Using this tool, with Record reference counting turned on we can eventually find the needle in the haystack, e.g. here we see a reference count increasing when we call makeCellProvider and we notice this is never balanced out with a release: CleanShot 2024-02-29 at 22 01 17@2x

That leads us to see that we need to fix the retain cycle in the closure in makeCellProvider. Once we do that (and fix the other issues in this PR), we can rerun the app and observe in Instruments that when we switch chats, the current chat objects are deallocated as expected: CleanShot 2024-02-29 at 21 36 57

Now when we rerun the tests detailed above switching between multiple chats, we record the following:

Final (rodhan/perf-improvements branch @ 8c01628) Memory: 120MB (was 214MB, Δ -94MB) (100MB on Intel MacBook (was 192MB, Δ -92MB)) AspectFillNSImageView count: 201 (was 475, Δ -274) NewOnlyTextMessageCollectionViewitem count: 30 (was 90, Δ -60) VerticallyCenteredButtonCell count: 122 (was 236, Δ -114) ...

This data is from a simple test run of only switching between chats 3 times. The improvement to the memory consumption and performance of the app over a longer period of hours or days or use will of course be much more significant.

Possible next steps/future work

  1. Build automation for running perf tests more easily to help with future improvements and identify perf regressions.
  2. Modify custom view structure to fix memory leaks in NIB loader code
  3. Continue to hunt down major memory leaks, focus on Tribes, web content, etc. Agree acceptable levels