Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Slow sidebar performance in 3.9.20 when "Persist tree cache" is unchecked #3434

Closed
mesvam opened this issue Jan 23, 2024 · 31 comments
Closed
Labels
bug has-workaround help wanted stale no reaction got for a long term

Comments

@mesvam
Copy link

mesvam commented Jan 23, 2024

In the 3.9.20 patch notes, I see

Use on-memory storage for the cache of sidebar contents, to prevent broating of Firefox's session file. This means that inititalization on the browser startup takes more time, and you can take the old behavior back by a new advanced option.

This behavior change seems to have a noticeable impact on TST performance in certain scenarios. Poor performance includes things such as:

  • after switching tabs via Ctrl+Tab, the active tab shown in the sidebar takes a few seconds to update
  • opening and closing tabs takes a few seconds to update
  • interactions with the sidebar take a few seconds to have an effect on the browser
  • high CPU usage of the Firefox extension subprocess

Performance degradation is only noticeable when a large number of tabs are open and also seems to require that "Optimize tree restoration with cache" is checked

Steps to reproduce

  1. Ensure "Optimize tree restoration with cache" is checked in the options
  2. Ensure "Persist tree cache" is unchecked in the options
  3. Have a large number of tabs open (lets say 300+)
  4. Interact with the browser or TST sidebar

Environment

  • Platform (OS): Windows 11
  • Version of Firefox: 121.0.1
  • Version (or revision) of Tree Style Tab: 3.9.20
@piroor
Copy link
Owner

piroor commented Jan 23, 2024

The change you pointed means TST switched the backend of the data store from browser.sessions to browser.storage.session. Here is the implementation of browser.storage.session: https://searchfox.org/mozilla-central/rev/c130c69b7b863d5e28ab9524b65c27c7a9507c48/toolkit/components/extensions/ExtensionStorage.sys.mjs#486 It is based on a WeakMap, and does serializing/deserializing.

Serializing/deserializing helper is defined at:
https://searchfox.org/mozilla-central/rev/c130c69b7b863d5e28ab9524b65c27c7a9507c48/toolkit/components/extensions/ExtensionStorage.sys.mjs#430
https://searchfox.org/mozilla-central/rev/c130c69b7b863d5e28ab9524b65c27c7a9507c48/toolkit/components/extensions/ExtensionStorage.sys.mjs#462

On the other hand, old backend browser.sessions.setWindowValue uses JSON.stringify: it may be optimized well.
https://searchfox.org/mozilla-central/rev/c130c69b7b863d5e28ab9524b65c27c7a9507c48/browser/components/extensions/parent/ext-sessions.js#275

Thus I guess that this difference possibly causes slowing down on your environment. A performance profile with the debug tool (Browser Toolbox => Performance => "Firefox" profile => "WebExtensions" process) looks to be needed for more inspection.

@piroor
Copy link
Owner

piroor commented Jan 23, 2024

And, of course TST 3.9.20 contains more other changes, so slowing down possibly happens due to such other changes. Collecting performance profile is a required step anyway.

@sigprof
Copy link

sigprof commented Jan 23, 2024

Another problem when the tree cache is enabled is large memory consumption (once I noticed the WebExtension process having about 10G RSS). Also collecting the memory report on about:memory becomes very slow.

Top of the memory report for the extension process after enabling the cache and opening and closing a couple of empty tabs:

extension (pid 151295)

Explicit Allocations

2,236.34 MB (100.0%) -- explicit
├──1,723.68 MB (77.08%) -- window-objects
│  ├────752.15 MB (33.63%) -- top(moz-extension://503eba12-aee8-4e53-ab83-c0b0dd5f40a8/background/background.html, id=62)
│  │    ├──731.35 MB (32.70%) -- js-zone(0x7f9d84044b00)
│  │    │  ├──718.99 MB (32.15%) -- strings
│  │    │  │  ├──170.95 MB (07.64%) -- string(length=89622771, copies=1, "<ul data-window-id="1" id="window-1" class="tabs" role="listbox" aria-multiselectable="true" aria-activedescendant="tab-3014"><tab-item id="tab-2" data-tab-id="2" data-window-id="1" class="contextual-identity-firefox-default tab pinned unread faviconized last-row in-visible-area animation-ready complete not-activated-since-load" data-current-uri="https://discord.com/channels/985244428430110771/985915953944596500" data-current-favicon-uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4Xu1dB3hUxfY/c3c3CelUQ5OAlIAgoOEPKAIKKDaKiCQEFZQEzMPCszzBFis87ICIgIpICUpRQOUhKmBEmhh6QKRIgFCT7G6STXb33v+ZC0FI23bv3Vtmvm+/XcidmXN+M/O7U86cQ4AlTSMw8sn82LIiS7zbxDchAmkCPNQDwsfRb0IgFgiJ5YGEE0GIJESIFZUVuEgBBEtFxfHv+bxAXATADkAc+G8rCEIBD3AWyyrAfHn4f3k8B3kmN5crED530azos5oG0ODCY1uzpHYERmYIYWXHixIEzt0e3JAgENJeIBDP8UIL/B0VTPkJIGkQ8jcnwEGewBHCwW4Q+N1uU0zOVzMIEglLakaAEYDKWmdouhBpEYq6ud18Ig6uTvgmvh7f7PFVvbFVJvoV4hAgTpT5AHawHAFgk9lEtpVykdsYKair1RgBBLk9hqbb4yxOoTdP+B4gkN44cFprbbB7C+FFUtgLRMjiBC7LaSHrvpoRmedtfvac9A" (truncated))
│  │    │  │  │  ├──170.95 MB (07.64%) ── malloc-heap/two-byte
│  │    │  │  │  └────0.00 MB (00.00%) ── gc-heap/two-byte
│  │    │  │  ├──170.94 MB (07.64%) -- string(length=89621113, copies=1, "<ul data-window-id="1" id="window-1" class="tabs" role="listbox" aria-multiselectable="true" aria-activedescendant="tab-3006"><tab-item id="tab-2" data-tab-id="2" data-window-id="1" class="contextual-identity-firefox-default tab pinned unread faviconized last-row in-visible-area animation-ready complete not-activated-since-load" data-current-uri="https://discord.com/channels/985244428430110771/985915953944596500" data-current-favicon-uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4Xu1dB3hUxfY/c3c3CelUQ5OAlIAgoOEPKAIKKDaKiCQEFZQEzMPCszzBFis87ICIgIpICUpRQOUhKmBEmhh6QKRIgFCT7G6STXb33v+ZC0FI23bv3Vtmvm+/XcidmXN+M/O7U86cQ4AlTSMw8sn82LIiS7zbxDchAmkCPNQDwsfRb0IgFgiJ5YGEE0GIJESIFZUVuEgBBEtFxfHv+bxAXATADkAc+G8rCEIBD3AWyyrAfHn4f3k8B3kmN5crED530azos5oG0ODCY1uzpHYERmYIYWXHixIEzt0e3JAgENJeIBDP8UIL/B0VTPkJIGkQ8jcnwEGewBHCwW4Q+N1uU0zOVzMIEglLakaAEYDKWmdouhBpEYq6ud18Ig6uTvgmvh7f7PFVvbFVJvoV4hAgTpT5AHawHAFgk9lEtpVykdsYKair1RgBBLk9hqbb4yxOoTdP+B4gkN44cFprbbB7C+FFUtgLRMjiBC7LaSHrvpoRmedtfvac9A" (truncated))
│  │    │  │  │  ├──170.94 MB (07.64%) ── malloc-heap/two-byte
│  │    │  │  │  └────0.00 MB (00.00%) ── gc-heap/two-byte
│  │    │  │  ├──170.94 MB (07.64%) -- string(length=89620448, copies=1, "<ul data-window-id="1" id="window-1" class="tabs" role="listbox" aria-multiselectable="true" aria-activedescendant="tab-1"><tab-item id="tab-2" data-tab-id="2" data-window-id="1" class="contextual-identity-firefox-default tab pinned unread faviconized last-row in-visible-area animation-ready complete not-activated-since-load" data-current-uri="https://discord.com/channels/985244428430110771/985915953944596500" data-current-favicon-uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4Xu1dB3hUxfY/c3c3CelUQ5OAlIAgoOEPKAIKKDaKiCQEFZQEzMPCszzBFis87ICIgIpICUpRQOUhKmBEmhh6QKRIgFCT7G6STXb33v+ZC0FI23bv3Vtmvm+/XcidmXN+M/O7U86cQ4AlTSMw8sn82LIiS7zbxDchAmkCPNQDwsfRb0IgFgiJ5YGEE0GIJESIFZUVuEgBBEtFxfHv+bxAXATADkAc+G8rCEIBD3AWyyrAfHn4f3k8B3kmN5crED530azos5oG0ODCY1uzpHYERmYIYWXHixIEzt0e3JAgENJeIBDP8UIL/B0VTPkJIGkQ8jcnwEGewBHCwW4Q+N1uU0zOVzMIEglLakaAEYDKWmdouhBpEYq6ud18Ig6uTvgmvh7f7PFVvbFVJvoV4hAgTpT5AHawHAFgk9lEtpVykdsYKair1RgBBLk9hqbb4yxOoTdP+B4gkN44cFprbbB7C+FFUtgLRMjiBC7LaSHrvpoRmedtfvac9AgwA" (truncated))
│  │    │  │  │  ├──170.94 MB (07.64%) ── malloc-heap/two-byte
│  │    │  │  │  └────0.00 MB (00.00%) ── gc-heap/two-byte
│  │    │  │  ├──161.12 MB (07.20%) ++ (356 tiny)
│  │    │  │  └───45.04 MB (02.01%) -- string(length=248989, copies=189, "data:image/x-icon;base64,AAABAAcAUEoAAAEAIAAgYAAAdgAAABAQAAABACAAaAQAAJZgAAAgIAAAAQAgAKgQAAD+ZAAAOTkAAAEAIAC0NAAApnUAAEhIAAABACAAiFQAAFqqAABycgAAAQAgAFjSAADi/gAAgIAAAAEAIAAoCAEAOtEBACgAAABQAAAAlAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN6tAB/YqgAh3qwArN+sAP/frAD/36wA/9+sAP/frAD/36sA0t6tAB/ZqgAbqqoAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/gAAC3rEAF96tAB/frAC236wA/9+sAP/frAD/36wA/9+sAP/fqwCu26gAI96tAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOGtADvfrABf36wA/9+sAP/frAD/36wA/9+sAP/frAD/36wA/9+sAP/frAD/36wA/9+rAOffrABu5K4AEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCsAGvfrADX36wA/9+sAP/frAD/36wA/9+sAP/frA" (truncated))
│  │    │  │      ├──45.04 MB (02.01%) ── malloc-heap/latin1
│  │    │  │      └───0.00 MB (00.00%) ── gc-heap/latin1
│  │    │  └───12.37 MB (00.55%) ++ (13 tiny)
│  │    └───20.79 MB (00.93%) ++ active/window(moz-extension://503eba12-aee8-4e53-ab83-c0b0dd5f40a8/background/background.html)
│  ├────638.10 MB (28.53%) -- top(moz-extension://503eba12-aee8-4e53-ab83-c0b0dd5f40a8/sidebar/sidebar.html?style=photon&reloadMaskImage=true, id=84)
│  │    ├──323.78 MB (14.48%) -- js-zone(0x7f9d81be0200)
│  │    │  ├──318.20 MB (14.23%) -- strings
│  │    │  │  ├──170.94 MB (07.64%) -- string(length=89621113, copies=1, "<ul data-window-id="1" id="window-1" class="tabs" role="listbox" aria-multiselectable="true" aria-activedescendant="tab-3006"><tab-item id="tab-2" data-tab-id="2" data-window-id="1" class="contextual-identity-firefox-default tab pinned unread faviconized last-row in-visible-area animation-ready complete not-activated-since-load" data-current-uri="https://discord.com/channels/985244428430110771/985915953944596500" data-current-favicon-uri="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAgAElEQVR4Xu1dB3hUxfY/c3c3CelUQ5OAlIAgoOEPKAIKKDaKiCQEFZQEzMPCszzBFis87ICIgIpICUpRQOUhKmBEmhh6QKRIgFCT7G6STXb33v+ZC0FI23bv3Vtmvm+/XcidmXN+M/O7U86cQ4AlTSMw8sn82LIiS7zbxDchAmkCPNQDwsfRb0IgFgiJ5YGEE0GIJESIFZUVuEgBBEtFxfHv+bxAXATADkAc+G8rCEIBD3AWyyrAfHn4f3k8B3kmN5crED530azos5oG0ODCY1uzpHYERmYIYWXHixIEzt0e3JAgENJeIBDP8UIL/B0VTPkJIGkQ8jcnwEGewBHCwW4Q+N1uU0zOVzMIEglLakaAEYDKWmdouhBpEYq6ud18Ig6uTvgmvh7f7PFVvbFVJvoV4hAgTpT5AHawHAFgk9lEtpVykdsYKair1RgBBLk9hqbb4yxOoTdP+B4gkN44cFprbbB7C+FFUtgLRMjiBC7LaSHrvpoRmedtfvac9A" (truncated))
│  │    │  │  │  ├──170.94 MB (07.64%) ── malloc-heap/two-byte
│  │    │  │  │  └────0.00 MB (00.00%) ── gc-heap/two-byte
│  │    │  │  ├──115.09 MB (05.15%) ++ (328 tiny)
│  │    │  │  └───32.17 MB (01.44%) -- string(length=248989, copies=135, "data:image/x-icon;base64,AAABAAcAUEoAAAEAIAAgYAAAdgAAABAQAAABACAAaAQAAJZgAAAgIAAAAQAgAKgQAAD+ZAAAOTkAAAEAIAC0NAAApnUAAEhIAAABACAAiFQAAFqqAABycgAAAQAgAFjSAADi/gAAgIAAAAEAIAAoCAEAOtEBACgAAABQAAAAlAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN6tAB/YqgAh3qwArN+sAP/frAD/36wA/9+sAP/frAD/36sA0t6tAB/ZqgAbqqoAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/gAAC3rEAF96tAB/frAC236wA/9+sAP/frAD/36wA/9+sAP/fqwCu26gAI96tAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOGtADvfrABf36wA/9+sAP/frAD/36wA/9+sAP/frAD/36wA/9+sAP/frAD/36wA/9+rAOffrABu5K4AEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCsAGvfrADX36wA/9+sAP/frAD/36wA/9+sAP/frA" (truncated))
│  │    │  │      ├──32.17 MB (01.44%) ── malloc-heap/latin1
│  │    │  │      └───0.00 MB (00.00%) ── gc-heap/latin1
│  │    │  └────5.58 MB (00.25%) ++ (16 tiny)

Apparently the number of objects like string(length=89622771, copies=1, "<ul data-window-id="1" id="window-1" class="tabs" increases after tab opening and closing; maybe there is a leak somewhere?

@Hoernchen
Copy link

Hoernchen commented Jan 23, 2024

There is definitely a leak, I ended up here because in my case the strings consume 25gb and ff is incredibly slow...


28,208.01 MB (100.0%) -- explicit
├──27,389.78 MB (97.10%) -- window-objects
│  ├──25,948.77 MB (91.99%) -- top(moz-extension://2a09b8a2-3556-4fb4-9510-1c2fd74c35e6/background/background.html, id=58)
│  │  ├──25,667.08 MB (90.99%) -- js-zone(0x1d5b94e5500)
│  │  │  ├──25,657.95 MB (90.96%) -- strings
│  │  │  │  ├──24,969.46 MB (88.52%) ++ (3761 tiny)
│  │  │  │  └─────688.50 MB (02.44%) -- string(length=7265, copies=87868, "data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn///////////////////////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZp/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD///////9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v///////////////////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6//////////" (truncated))
│  │  │  │        ├──686.48 MB (02.43%) -- malloc-heap
│  │  │  │        │  ├──686.45 MB (02.43%) ── latin1
│  │  │  │        │  └────0.03 MB (00.00%) ── two-byte
│  │  │  │        └────2.01 MB (00.01%) ++ gc-heap
│  │  │  └───────9.13 MB (00.03%) ++ (15 tiny)
│  │  └─────281.69 MB (01.00%) ++ active/window(moz-extension://2a09b8a2-3556-4fb4-9510-1c2fd74c35e6/background/background.html)
│  ├───1,343.21 MB (04.76%) -- top(moz-extension://2a09b8a2-3556-4fb4-9510-1c2fd74c35e6/sidebar/sidebar.html?style=proton&reloadMaskImage=true, id=76)
│  │   ├────951.89 MB (03.37%) -- js-zone(0x1d5bd5a1300)
│  │   │    ├──940.47 MB (03.33%) ++ strings
│  │   │    └───11.42 MB (00.04%) ++ (16 tiny)
│  │   └────391.32 MB (01.39%) -- active
│  │        ├──391.18 MB (01.39%) ++ window(moz-extension://2a09b8a2-3556-4fb4-9510-1c2fd74c35e6/sidebar/sidebar.html?style=proton&reloadMaskImage=true)
│  │        └────0.14 MB (00.00%) ++ window(about:blank)
│  └──────97.79 MB (00.35%) ++ (15 tiny)
├─────441.68 MB (01.57%) ++ (23 tiny)
└─────376.56 MB (01.33%) ── heap-unclassified

@piroor
Copy link
Owner

piroor commented Jan 23, 2024

Hmm... activating "Persist tree cache" is the workaround for now, if it is a problem of browser.storage.session itself.

@Hoernchen
Copy link

Yes, I have checked and unchecked that option a few times and that deflates the ram usage to 1gb, which is fine, and keeping it checked restores the old, fast, firefox, that does not eat my ram.

@ssergiienko
Copy link

Same here.
Both memory and CPU increased drastically (many gigabytes during opening/closing a single new tab) to the unusable level. I have 1086 tabs in tree and it worked fined before upgrade.

Disabling "Optimize tree restoration with cache" seems like solving issue. And what is more interesting - I don't see any visual performance benefit from that option at all (even with my 1086 tabs tree!). Is it still relevant?

p.s. I didn't try to use "Persist tree cache"

@mesvam
Copy link
Author

mesvam commented Jan 23, 2024

Hmm... activating "Persist tree cache" is the workaround for now, if it is a problem of browser.storage.session itself.

From limited testing, disabling "Optimize tree restoration with cache" works equally well as a workaround, if the cache is not needed

@asutherland
Copy link

Here is a profile of opening a new (searchfox) tab. My process was:

  • start profiler
  • middle click on a searchfox link
  • watch the tab appear in the TST sidebar, noticing that there was not a valid icon for the tab initially
  • clicking on the tab in the TST sidebar
  • wait for the tab switch
  • stop the profiler
  • I truncated the first seconds off of the profile which had nothing interesting happening and I think was just me moving my mouse to open the tab.
  • I truncated off a little bit of time at the end of the trace because nothing else interesting happened in the WebExtensions process.

Note that the stack frames mentioning StructuredCloneBlob somewhat misleadingly correspond to the JS StructuredCloneHolder class, rather than the StructuredCloneHolder C++ class which the StructuredCloneBlob does use.

Note that in addition to the time spent in the WebExtensions process deserializing the data, there is significant time spent in the parent process main thread serializing the data which will cause problems as well. It's worth noting that WebExtensions are able to use storage APIs like IndexedDB directly and those don't involve the main thread of the parent process. But any performance issues related to large object (graphs) being serialized/deserialized will potentially remain.

Although using IDB directly does potentially allow for potentially allow for more granular storage since something that might currently be a single map from N tab identifiers to N tab payload could instead be stored as N separate records which could be retrieved at startup using a cursor over the lexicographic range that could contain tabs. (We/the Gecko DOM storage team have put a lot of work into optimizing cursor prefetching, so this can be very performant while also helping avoid blocking the WebExtensions main thread like if getAll() was used, since work can be sliced up. It also avoids concerns about record sizes, somewhat.)

@piroor
Copy link
Owner

piroor commented Jan 23, 2024

I've created three similar branches.

  1. Storing data to the SessionStorage (development build: https://github.com/piroor/treestyletab/actions/runs/7644870528 )
  2. Storing data to a JavaScript object (development build: https://github.com/piroor/treestyletab/actions/runs/7644862985 )
  3. Storing data to an IndexedDB storage (development build: https://github.com/piroor/treestyletab/actions/runs/7644847665 )

I hope they all should work better than TST 3.9.20. 1 should be better than 2, but 1 is possibly slower than 2. 3 should better than 2 also but I have a concern about increased disk I/O. (See also details: #3434 (comment) )

Could you try these versions, with enabled cache and disabled permanent cache? Here is the instruction to try development builds: https://github.com/piroor/treestyletab#development-builds

piroor added a commit that referenced this issue Jan 23, 2024
@piroor
Copy link
Owner

piroor commented Jan 23, 2024

I've made one more branch: storing data to an IndexedDB storage. Development build: https://github.com/piroor/treestyletab/actions/runs/7630580679

@asutherland
Copy link

Thank you so much! (And thank you for creating and maintaining this incredible extension. I love TreeStyleTab so much! It is a must-have, must-use extension, and I am lost whenever I try and use a browser without it!)

Initial testing with all 3 variations without changing any settings (so persistCachedTree remained unchecked) seemed to provide immediate relief from the performance problems. I will try and do some additional testing including toggling that setting.

To anyone else testing the above builds, something that was not immediately obvious to me is that when I clicked on the "treestyletab-we.xpi" artifacts, the download of "treestyletab-we.xpi.zip" that resulted was in fact a zip file containing the XPI file, not just a mis-named XPI file. You do need to unzip that zip file to get to the XPI inside. Otherwise you will get an error about an invalid manifest.

@piroor
Copy link
Owner

piroor commented Jan 24, 2024

The "non-persistent cache" mode was originally introduced as a workaround for the issue #3388 which is about slowing down from bloated session file - increased disk I/O on classical HDD (non-SSD) would triggers slowing down of the Firefox process, when the cache became huge from a tree cache for a large number of tabs.

Firefox's IndexedDB implementation is based on SQLite, and stored caches are finally wrote to an SQLite file like %AppData%\Mozilla\Firefox\Profiles\*****\storage\default\moz-extension+++(local UUID of TST)\idb\*****.sqlite. I still have a concern about re-introduction of the slowing down from disk I/O, on the IndexedDB approach. But this can be an over-concern because tree caches separated from browser session will be smaller than bloated jsonlz4 browser session file.

I have two Windows 11 PCs and both they use SSD as their main storage, so I cannot feel slowing down from increased disk I/O seriously. I need more testing reports of three development versions, from people who use large number of tabs with non-SSD storage.

@asutherland
Copy link

For #3388 I think https://bugzilla.mozilla.org/show_bug.cgi?id=1849393 is important context. It's currently a known problem that the session store (which is distinct from SessionStorage, but which is responsible for persisting SessionStorage data) performs a single JSON serialization of a very large object on the main thread of the parent process which can block the main thread for 0.5 seconds easily for users with a lot of tabs/windows. (And I believe in that bug, non de-duplicated favicons are called out as a problem, although I think that's for the session store's own use, not TST.) To be very clear, the I/O happens on another thread, it's the call that amounts to JSON.stringify() that's the problem. And because the I/O is a sequential write, it actually should be fairly efficient on spinning hard disks.

As I note above, since SessionStorage will also end up stored in the session store file, it's likely that IndexedDB is the best choice, especially since it avoids the main thread of the parent process entirely. As you say, the IDB writes should generally be smaller since our implementation uses SQLite and changes will be limited to modified btree pages and their parental tree structure. The trade-off is that although we will perform sequential writes to the SQLite Write Ahead Log (WAL), when we checkpoint the WAL, we will need to perform random access writes as the pages are transferred into the main database file.

I think the main concern would likely be if values are set in the cache frequently, it could make sense to attempt to accumulate changes in memory and then write them to IDB in a single transaction after a timeout or when you find you need to read one of the values, so that your reads can see the writes as part of the same transaction. This would reduce I/O overall.

It's probably also worth noting that our IndexedDB implementation does a clever thing with Blob/File storage. Any Blob/File that is stored in the database will be file-backed once it is read back out, and if you re-store that Blob, we will simply increment the reference count for the Blob in the database rather than storing the value. We also have an optimization if you store a Blob/File multiple times. But note this is based on object identity, not on content de-duplication. So if you do let a = new Blob(["foo"]); let b = new Blob(["foo"]); and then put a in the DB twice, we only store it once. But if you then put b, that will result in a new file being created on disk. The trade-off with this is of course that if you then use that to load from an image (via URL.createObjectURL), the image load will have to hit disk, although we would expect the image cache to be helpful there. Also, you need to make sure to revoke the object URLs when appropriate or you will create disk storage object leaks.

@asutherland
Copy link

I should probably also mention that in relation to #3387 Firefox does now support storing IndexedDB data in Private Browsing, which it sounds like was impacting people using Firefox in permanent private browsing mode which unfortunately causes loaded WebExtensions to have an mPrivateBrowsingId=1 origin attribute. While the WebExtensions storage APIs go out of their way to not discard the data, QuotaManager's PrivateBrowsing implementation fundamentally must discard the data when the browser restarts. (The data is stored in an encrypted database where the keying material is never saved to disk.)

I think in the medium-to-long term the WebExtensions team would like to try and change the Private Browsing mode behavior of WebExtensions under permanent private browsing mode, as I don't think that was ever an intentional action[1], just a side effect of how private browsing windows work (they are created with mPrivateBrowsingId=1 and that flows to all of the windows they contain) and how permanent private browsing mode meant that all windows were private browsing windows.

1: There are intentional behaviors around not exposing the existence of private browsing windows/tabs to WebExtensions that haven't been granted that ability, but that's separate from putting the WebExtension itself into private browsing mode.

@piroor
Copy link
Owner

piroor commented Jan 24, 2024

@asutherland Thank you for detailed explanation!

Large cache data for sidebar contents (<ul ...) is actually the HTML of the sidebar. I hope to introduce virtual scrolling architecture to TST's sidebar, and after that we won't have to use such a huge cache. I've introduced updated cache mechanism to the main branch as a workaround until such improvements are really introduced:

  • First, TST tries to store cache to the IndexedDB if the tree cache system is configured as persistent.
  • If IndexedDB operation fails or tree cache system is configured as non-persistent, TST just stores cache to a JS object without serialization/deserialization.
  • The cache system will eat the RAM, so the cache system is deactivatable, for people who use PCs with less RAM.

@piroor
Copy link
Owner

piroor commented Jan 24, 2024

Sorry, linked revisions have bugs: caches are not used for sidebar. I've updated links to development versions: #3434 (comment)

@piroor
Copy link
Owner

piroor commented Jan 24, 2024

On my environment <ul...> string uses 55MB on the RAM. I've tried to compress it but I've realized that decompression is painfully slow (about 35 sec) and unusable.

@piroor
Copy link
Owner

piroor commented Jan 24, 2024

I've tried four branches and compare their RAM usage with 525 tabs.

branch (implementation) main process explicit allocations extension process explicit allocations subtotal
no-storage-session (on-memory JS object) 1,274.13 MB 460.05 MB 1,734.18 MB
use-session-storage (window.sessionStorage) 1,282.44 MB 452.05 MB 1,734.49 MB
use-indexed-db (IndexedDB, string) 1,516.47 MB 458.41 MB 1,974.88 MB
use-indexed-db (IndexedDB, blob) 1,397.79 MB 465.95 MB 1,863.74 MB
3.9.20 (browser.storage.session) 2,728.68 MB 813.09 MB 3,541.77 MB

( Reload TST => minimize memory usage => add blank tab => close blank tab => measure )

There are two winners: on-memory JS object implementation and IndexedDB implementation, at the viewpoint about total amount of RAM usage.
  • On-memory JS object implementation is very simple and (maybe) less overhead.
  • IndexedDB implementation is the best to minimize RAM usage of extensions process. In the inspection result, there looks to be no large string (<ul...>) on the RAM - they looks to be exported to the disk and gone away from the RAM.
    • But the RAM usage of the main process is grown. This looks to be caused by the IndexedDB system.

Please note this report ignores the overhead of serializing and deserializing intentionally. There may be different winners about less spiked CPU and RAM usage.

Edit: I've updated the table with IndexedDB + blob.

@asutherland
Copy link

Note that Additional allocations in the IDB case I think would be something like an increase in the number of IPC buffers retained by the IPC subsystem and not something specifically retained by IDB. The SQLite connection itself should target a page cache of only 2 MiB, for example.

This can potentially be reduce by moving to wrap and store your string in a Blob rather than storing the string directly as a value in the object. This would reduce the serialized IPC message size. The data in the blob would instead be sent via a different IPC mechanism to stream its contents to the parent process and to disk (using a more bounded circular buffer mechanism).

The downside to this is that when you get the result values back from the database, you will have a Blob which you will still need to issue an async request to read. Although it does open the possibility of being able to create a blob URL from the blob and to provide that directly to an iframe as its source URL, which could in turn allow for off-the-main-thread HTML parsing (potentially). But that gets potentially quite complicated unless you have cached the entire HTML page. One can of course build composite blobs like new Blob([htmlBeforeUL, CacheULFromDatabase, htmlAfterUL]), or use DOM streams instead, but that seems like potentially quite a lot of work.

@ElhemEnohpi
Copy link

I have unchecked the "Optimize tree restoration with cache" option to avoid the performance degredation I've experienced in the past few days, e.g. very slow and more than 10 GB RAM use by the extension. Is there a disadvantage to disabling the cache, apart from increased startup time? What is the function of the in-memory non-persistent cache, if it's not tree restoration?

piroor added a commit that referenced this issue Jan 25, 2024
piroor added a commit that referenced this issue Jan 25, 2024
@kstepyra
Copy link

Another victim of this patch - i just had to close FF due to slow opening/switching/closing tabs, and about 40GB of ram usage due to TST. It started like 5-15 days ago, probably same thing.

@hikari-no-yume
Copy link

Hi, I am also suffering due to this issue. If I do not disable Tree Style Tabs or enable “Persist tree cache” work, the browser becomes almost unusable for typical browsing due to this: every page load is delayed by several seconds, and while the Firefox tab bar is responsive, the TST tab bar is very, very unresponsive. Performance profiling of a single page load showed over 40,000ms (yes, 40s) spent in JavaScript inside the WebExtension. I also noticed the WebExtensions process taking up something like 4GB when I used macOS's Activity Monitor. Disabling Tree Style Tabs or enabling “Persist tree cache” do seem to work around it at least.

If the experience is as bad for others as it is for me, I wonder if it is an option to temporarily revert the original change.

@SHHSSH
Copy link

SHHSSH commented Feb 4, 2024

I have unchecked the "Optimize tree restoration with cache" option to avoid the performance degredation I've experienced in the past few days, e.g. very slow and more than 10 GB RAM use by the extension. Is there a disadvantage to disabling the cache, apart from increased startup time? What is the function of the in-memory non-persistent cache, if it's not tree restoration?

Unfortunately what I am fearful of is what had happened to me many years ago, whereas, I believe in recent years for whatever setting's reason I have no run into this issue. I.e; very rarely, if FF was to crash or if TST malfunctioned in anyway on its startup, there was the potential for all tabs to be lost. I had found myself using addons like Copy All Tab URLs occasionally just as an insurance method at times.

I've been experiencing these RAM issues as many here have for months maybe even almost all of last year.
Was always a massive tab user, curated into folders in TST, never really felt like an issue until recent times.

It's had me attempting to isolate all kinds of things in about:config too...
I used to think it was NoScript, I used to think it was Windscribe, I stopped using AutoTabDiscard for another "New Tab Suspender" as it claims it's lightweight... I never found a resolution other than to really attempt to keep my amount of tabs relatively low and attempt to bookmark ASAP.

As many have spoken of their resolution by unchecking "Optimize tree restoration with cache", I have done so too and it seems to totally rectify the issue(This was before updating to 3.9.22, from 3.9.20), I have an alternate profile which is on 3.9.20 for which I attempt to fiddle with "Optimize tree restoration with cache" and its sub persist option for additional heuristics to see if the problem persists there. As with -#3415 (comment) I wonder if I can revert back to using the option of "Optimize tree restoration with cache"(of which I was not using the persist sub option before, but now I wonder if I should in 3.9.22), since I worry without these options I risk the potential hazard of total tab loss.

-Running with "Optimize tree restoration with cache" checked in 3.9.22 for a while to see if I run into the RAM issues.
I will not use the sub persist option as it reminds me of the mistake I made years ago of browser.cache.disk.enable being true and writing too much to my SSD, especially when streaming.

I will actually turn on browser.cache.memory.enable for a while now too as I thought that may have been an issue, after I was fiddling with it & its other related about:config memory cache settings :|
See how it goes with usage over the next week or so.

@piroor
Copy link
Owner

piroor commented Feb 4, 2024

Related topic:
The huge RAM usage and low performance issue is basically due to the current architecture of TST: very naive implementation holding DOM elements for all opened tabs.
As a fundamental solution, now I'm trying to update the implementation based on the virtual scrolling architecture. Here is the branch https://github.com/piroor/treestyletab/tree/virtual-scrolling and development builds are available from CI results like https://github.com/piroor/treestyletab/actions/runs/7772749214 https://github.com/piroor/treestyletab/actions/runs/7774466076 .
I think it is almost ready to be released, but I still do dogfooding to brush up it. If you are interested in the experiment, please try the branch and report bugs around the sidebar behavior as new issues.

@SHHSSH
Copy link

SHHSSH commented Feb 4, 2024

Related topic: The huge RAM usage and low performance issue is basically due to the current architecture of TST: very naive implementation holding DOM elements for all opened tabs. As a fundamental solution, now I'm trying to update the implementation based on the virtual scrolling architecture. Here is the branch https://github.com/piroor/treestyletab/tree/virtual-scrolling and development builds are available from CI results like https://github.com/piroor/treestyletab/actions/runs/7772749214 I think it is almost ready to be released, but I still do dogfooding to brush up it. If you are interested in the experiment, please try the branch and report bugs around the sidebar behavior as new issues.

I am sure I speak for most users when I say we look forward to your evolution of TST @piroor, your inspirational seeking of unique technologies is mind-blowing and your endless consideration & open-mindedness regarding your addon is greatly appreciated.

Thanks so much, looking forward to the virtual scrolling architecture in time to come.

@github-actions github-actions bot added the stale no reaction got for a long term label Feb 23, 2024
Copy link

This issue has been labeled as "stale" due to no response by the reporter within 1 month (and 7 days after last commented by someone). And it will be closed automatically 14 days later if not responded.

@piroor piroor removed the stale no reaction got for a long term label Feb 23, 2024
@piroor
Copy link
Owner

piroor commented Feb 23, 2024

The experimental branch virtual-scrolling has been merged to the default branch, and now almost staged to be released. Dogfooding is in progress and I'm waiting for more feedback!
https://github.com/piroor/treestyletab?tab=readme-ov-file#development-builds

@github-actions github-actions bot added the stale no reaction got for a long term label Mar 2, 2024
Copy link

github-actions bot commented Mar 2, 2024

This issue has been labeled as "stale" due to no response by the reporter within 1 month (and 7 days after last commented by someone). And it will be closed automatically 14 days later if not responded.

@stefanct
Copy link

Do the underlying issues also produce a constant CPU load in the plugin (according to about:performance) or is this something else? I tried to checking and unchecking the options mentioned above but don't see enough of a relieve that I would call it a workaround. The time for opening and closing tabs has been reduced by that a bit though. I don't know when this has started exactly but I would guess with a TST update within two months, which would be fitting.

Copy link

This issue has been closed due to no response within 14 days after labeled as "stale", 7 days after last reopened, and 7 days after last commented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug has-workaround help wanted stale no reaction got for a long term
Projects
None yet
Development

No branches or pull requests