brave / brave-browser

Brave browser for Android, iOS, Linux, macOS, Windows.
https://brave.com
Mozilla Public License 2.0
17.02k stars 2.22k forks source link

Defer Tab Loading #23252

Open Renara opened 2 years ago

Renara commented 2 years ago

Description

Sometimes when I'm viewing a site with a load of articles, gallery of images/movies/whatever I like to skim through opening everything I'm interested in as a new tab, so I can then work my way through them later.

However, opening a lot of tabs in Brave Browser means a heavy impact on RAM; for example, right now at time of writing I have maybe 50 background tabs, and Brave is consuming nearly 7gb of RAM.

This is because Brave is loading every single tab and trying to keep it in memory, but I don't actually need this; I'd much rather have Brave load say 5-6 tabs that I'm likely to switch to (i.e- have switched to recently, or are next in line once the current tab is closed), the rest can simply take a note of the URL and remain unloaded until I actually switch to them (or I close one that was already loaded to free up room).

I'd like to see some kind of control added that lets me limit how many tabs can be loaded at once; Brave might even set this to some sensible default. If more tabs are opened, they will not load until the user switches to them, or until the user closes enough tabs that go below the limit once more (so the next tab in line can load up).

bridiver commented 2 years ago

This is a general problem with chromium and some sites tend to use more and more ram over time. It's an interesting idea, but I think not loading a newly opened tab would be very strange and annoying behavior. Brave will unload tabs if available memory drops too low and we could potentially unload tabs based on how recently they have been used so opening a new tab would unload the oldest loaded tab once you hit the limit.

bridiver commented 2 years ago

btw - an unloaded tab still appears in the tab strip, but it no longer has content in it and automatically reloads when you select it again

lazymonkey2 commented 2 years ago

deferred tab loading is a good idea, and I hope it will be implemented. this option should give the user the ability to specify how many tabs will be loaded in the background, including the "zero" option: a tab openend in background is loaded only when the user click on it, and brings it to the foreground. this should be hugely beneficial to memory usage, and network usage too: on metered connections could help reducing wasted data.

bridiver commented 2 years ago

I don't think deferred tab loading is a feature we would ever implement. Limiting the number of active tabs by most recently used is much more user friendly and is a possibility. This would mean unloading old tabs instead of blocking the load of newly opened tabs

Renara commented 2 years ago

It's an interesting idea, but I think not loading a newly opened tab would be very strange and annoying behavior.

It depends upon how it's implemented. To be clear, what I'm thinking is that limit will only limit the number of tabs that are loaded automatically in the background; if a user switches to a tab for any reason then it will be loaded as normal, and remain loaded until closed. For example, if the limit were set to 10, but the user manually switches between 20 tabs, then there will be 20 loaded tabs.

Put another way, the setting would only apply to tabs opened in the background, and only to prevent them from loading automatically if there are currently more loaded tabs than the limit is set to. If the number of loaded tabs drops below the limit, then the "next" background tab will load automatically.

The tricky part here is how to define the "next" tab; I'm thinking the best way to do it is to assume that tabs next to the current one are the most likely to be switched to. So for example, let's say I have 50 tabs open, and I'm currently focused on the 10th, my limit is set to six, so 3 background tabs to the left, and 3 to the right will be loaded automatically (the 7th, 8th, 9th, 11th, 12th, and 13th). If instead I'm focused on the 2nd tab then there won't be enough on the left to do half, so the excess will go to the right (the 1st, 3rd, 4th, 5th, 6th, 7th).

Bit fiddly to describe, but I think this makes the most sense, as it should mean that closing the current tab will usually mean switching to a tab that has already been loaded, unless you've reached the limit and jumped around the tabs in a weird way. In the worst case this just means a tab is loaded fresh (a bit slower than normal), however I've noticed that sometimes when I switch to tabs they seem to reload anyway, so I don't think this would affect user experience much.

Brave will unload tabs if available memory drops too low and we could potentially unload tabs based on how recently they have been used so opening a new tab would unload the oldest loaded tab once you hit the limit.

The problem with this is that unloading when memory is low means Brave is acting too late; by the time this kicks in a load of cached files will have already been dumped out of memory, paging of memory to disk may have already begun and so-on. I'd much rather limit Brave's memory usage in advance so this doesn't happen, as I just don't need all of my tabs to respond instantly. I suppose if we were given some kind of control over how soon this kicks in it could help, but it would still mean wasted loading (tabs loaded only be unloaded to save memory) and actually the least recently used behaviour would make this worse for user experience (as in my usual use case the least recently opened tabs are the ones I'm most likely to switch to next).

In terms of presenting deferred tab loading to users, it could be given as a simple option to prioritise Speed (unlimited, or very high limit) or Memory (low limit, e.g- 6) with a default "balanced" option that sets the limit based on the available RAM (e.g- if we assume 100 mb per tab, and the system has 16gb of RAM then a sensible default might be: 16gb / 100mb / 8 = ~20, with an advanced option to set the limit to a specific value?

While some tweaking may be required, I definitely think this feature can be implemented with negligible impact on user experience, and in most cases none whatsoever, all while saving RAM which may result in better overall performance.

bridiver commented 2 years ago

@Renara I think you're making this more complicated than it needs to be. Unloading tabs in an LRU list is much simpler and more intuitive and useful imo. Just pick a number of tabs and anything older than that will be unloaded when new tabs are opened (excluding tabs with media playing). The memory based tab unloading will always be in place and independent of the tab limit

Renara commented 2 years ago

Unloading tabs in an LRU list is much simpler and more intuitive and useful imo.

Under a least recently used technique, the tabs that I am most likely to switch to next (the oldest) would be unloaded first, this would actually be the worst possible scenario as it would mean that nearly every tab I switch to would end up being reloaded (having already loaded earlier then unloaded as "least recently used").

The purpose of deferred tab loading as I'm presenting it is to avoid unloading anything in the first place, meaning no wasted page loads, and to tie the automatic background tab loading to where the user currently is within their row of tabs, which should make it as intuitive as possible for an end user (the tabs near to where they are are the ones that should already be loaded).

It might sound complicated being fully described in detail but it's actually dead simple; it's just a limit on the number of background tabs that are automatically loaded.

bridiver commented 2 years ago

Under a least recently used technique, the tabs that I am most likely to switch to next (the oldest) would be unloaded first

It seems unlikely to me that this would be typical. If the purpose is to reduce memory usage it doesn't matter anyway because regardless of the order you access them in, you'll always only have N tabs loaded at once. See below for more on optimizing wasted page loads.

The purpose of deferred tab loading as I'm presenting it is to avoid unloading anything in the first place, meaning no wasted page loads, and to tie the automatic background tab loading to where the user currently is within their row of tabs, which should make it as intuitive as possible for an end user (the tabs near to where they are are the ones that should already be loaded).

I don't think this is a case we would optimize for and in order to do this effectively we'd need actual data to determine which tabs are likely to be used next, not just a guess or one person's typical behavior.

bridiver commented 2 years ago

So I think actually the best thing to do here would be to use the engagement score to decide which tabs to unload. The engagement score is what is used now to decide which tabs to unload under memory pressure and looks at various metrics to decide which tabs are the "least important"

Renara commented 2 years ago

So I think actually the best thing to do here would be to use the engagement score to decide which tabs to unload. The engagement score is what is used now to decide which tabs to unload under memory pressure and looks at various metrics to decide which tabs are the "least important"

What criteria is that score calculated from?

I still think that proximity to the active tab should be taken into account; at the very least it should be calculated into the engagement score if that's what you're determined to use, because there still needs to be some kind of default criteria for when the tabs are all equally valid (as they will be when you open a large number of never viewed before pages from the same site, as you've no other way to prioritise them).

My concern is that you're actually the one overcomplicating things now, as you're trying to repurpose tab unloading as a means of solving what is really a different problem.

Deferring tab loads is about not wasting resources, whereas unloading tabs is about reclaiming resources, so while they're definitely related features they're not strictly the same.

I still think that in terms of which tabs to preload, proximity to the active tab is the best criteria, because where the user is in the stack determines which tab will open when the user closes the current one.

However, there's a valid argument to be made for unloading preloaded tabs if the user changes the active tab, i.e- if Brave initially preloaded the ten leftmost tabs, but the user jumped to the rightmost tab, then it should start trying to preload some tabs on the right so they're quick to access if the user continues closing from the right. But if Brave is already at the limit of 10 preloaded tabs, it will need to free up room to preload more, so there's absolutely an argument for unloading preloaded tabs that haven't been ever been in focus, as it makes no difference to the user if they're unloaded if they've never seen them.

The goal should still be to minimise unnecessary loads first though, unloading should be the option of last resort when you're either very low on RAM, or have reached the preloading limit and the user has switched to a different tab (currently preloaded tabs are no longer the ones closest to where they are in the stack). It should probably be done tentatively though, i.e- preload the nearest tabs only, don't just unload all the preloaded ones and preloaded a whole new set each time, as the user may have only switch tabs temporarily before switching back.

bridiver commented 2 years ago

I'd like to see some kind of control added that lets me limit how many tabs can be loaded at once

I'm describing a way to do that which would be easy to implement using existing mechanisms, but the goal is to limit memory usage and prevent it from ever becoming a problem. Preventing new tabs from loading seems like an edge case very few people will care about. Most people probably don't even know you can open links in a new background tab.

We will be enabling reading list soon and that seems like a better fit for this with a right click option to add a link to reading list without opening it

niutech commented 2 years ago

There is a similar issue in Brave Community - please defer loading background tabs on startup until a user clicks on them.

lazymonkey2 commented 2 years ago

yes, I want lazy tab loading, to save ram but mainly to save network quota when using metered connection. it should work when I open a tab in background, but also when I save the session, and later reopen the browser. in this case only the foreground tab should be loaded, and all remaining tabs will not be loaded until I click on them. this behaviour should be optional, and disabled by default.

If I'm not mistaken this lazy loading behavious was implemented in firefox years ago, and I loved it. I don't know if it is still enabled in the current firefox thought.

I'd also like the idea of loading not more than 5-6 tabs at the same time, but lazy loading is what I really need. I have filed a different issue for this, but I feel it is related to this issue.

https://github.com/brave/brave-browser/issues/23819

Renara commented 2 years ago

I'm describing a way to do that which would be easy to implement using existing mechanisms, but the goal is to limit memory usage and prevent it from ever becoming a problem. Preventing new tabs from loading seems like an edge case very few people will care about. Most people probably don't even know you can open links in a new background tab.

Does Brave track any kind of stats to show many users regularly use background tabs? I use them all the time, which is why this is an issue for me, because there's no way for me to easily do what I do using only a handful of tabs without slowing me down or risking losing my place. And because they use a lot less RAM with more tabs open, this is an area in which Firefox and Safari are currently superior to Brave.

The problem with simply unloading tabs more aggressively is that you're still loading them in the first place, which risks double dipping on bandwidth usage (by loading the page, unloading it, then having to load it again); since one of the emphasised features of Brave is cutting down on bandwidth, loading background tabs twice seems counterintuitive?

Another way of thinking about it is with an example; say I go to news.bbc.co.uk and I open thirty articles in background tabs, with a limit of five preloading and consuming the tabs, for each solution the key questions are:

  1. Which background tabs are preloaded?
  2. How many page loads will there be?
  3. What is the best case number of "fresh" page loads (tab was not preloaded before viewing)?
  4. What is the worst case number of fresh page loads?

For what I'm proposing the answer should be 1) "the nearest five", 2) 30 (unless tabs are switched), 3) none (all tabs should be preloaded), 4) <5 (user jumps to the last tab and consumes in reverse order, forcing preloading to catch up).

From what I understand of your alternative, the engagement score would still be unloading least recently used first as it has no other criteria to go by, so the answers would be 1) "the last five", 2) 55 (each tab once, then 25 unloaded needing to be reloaded on-demand), 3) none (but only if user jumps to the last tab and consumes in reverse order), 4) <25 (user consuming tabs in left to right order).

Please correct me if I'm wrong, but this doesn't seem like an ideal alternative, as it requires the user to consume the tabs in a more unusual order in order to achieve its best case performance, with no way to short circuit it. At least with my proposal (prioritising nearest tabs) the preloading should still catch up if the user switches, so the number of "fresh" page loads should be minimised no matter where the user switches to, whereas LRU unloading only works if you consume right to left (newest tab first).

We will be enabling reading list soon and that seems like a better fit for this with a right click option to add a link to reading list without opening it

How does the reading list compare to Safari's implementation for example? Safari has a nice sidebar where you can easily add (and then remove) items. It's still not necessarily as convenient as opening a tab and closing it when you're done though, unless there's going to be some way to automatically remove items from the list as you open them from it?

Even with a really good reading list though, unless Brave preloads some of these pages (meaning more RAM used) it still means loading each page fresh every time, which will make it still inferior to using tabs. The aim of preloading some (but not all) tabs is to create a balance between responsiveness (page is already loaded) and memory consumption, by trying to load the tabs that will be accessed next so for the user it's functionally equivalent to preloading them all, just without the bloat.

As 424344 says there's also still the case of Brave reopening with a bunch of tabs already open. Plus just because there is a "better fit" doesn't guarantee people will use it, or won't prefer to use tabs anyway. And there's really just no reason for Brave (or any browser) to consume so much RAM for pages that don't need to be loaded yet.

When I use Safari for the same workflow, even though it has a good reading list implementation, I still heavily use tabs for for things I will be using "next". For me the reading list is more for "short term" bookmarks such as resources I need temporarily, or for videos on YouTube I've only partially watched (pop the URL with timestamp into the reading list). When I'm just reading a bunch of news articles I still prefer to open them all as tabs, then consume them by closing them in order, as I never have to manage any list or keep any of them longer than I need to.

bridiver commented 2 years ago

Does Brave track any kind of stats to show many users regularly use background tabs? I use them all the time, which is why this is an issue for me, because there's no way for me to easily do what I do using only a handful of tabs without slowing me down or risking losing my place. And because they use a lot less RAM with more tabs open, this is an area in which Firefox and Safari are currently superior to Brave.

No, Brave does not track that kind of information. btw - this is some info on the site engagement service https://www.chromium.org/developers/design-documents/site-engagement/

The problem with simply unloading tabs more aggressively is that you're still loading them in the first place, which risks double dipping on bandwidth usage (by loading the page, unloading it, then having to load it again); since one of the emphasised features of Brave is cutting down on bandwidth, loading background tabs twice seems counterintuitive?

We do emphasize reduced bandwidth as one of the benefits of ad blocking, but it's not necessarily a direct focus in general. Reducing memory usage, on the other hand, is always something we are looking at. If you were looking purely to reduce bandwidth then you would probably not enable the feature that I am describing.

Another way of thinking about it is with an example; say I go to news.bbc.co.uk and I open thirty articles in background tabs, with a limit of five preloading and consuming the tabs, for each solution the key questions are:

So again here I think reading list is going to be a good solution for you. I am a bit of a tab hoarder myself and I'm looking forward to using reading list to decrease the number of open tabs I have in the first place.

For what I'm proposing the answer should be 1) "the nearest five", 2) 30 (unless tabs are switched), 3) none (all tabs should be preloaded), 4) <5 (user jumps to the last tab and consumes in reverse order, forcing preloading to catch up).

This is part of the problem: we need to figure out and implement some algorithm to determine which tabs to load and our dataset for people who do this current has a size of one :)

From what I understand of your alternative, the engagement score would still be unloading least recently used first as it has no other criteria to go by,

It does in fact have other criteria to go on, some of which is explained in the link above, but it also considers things like whether media is playing or not when deciding whether to unload tabs.

How does the reading list compare to Safari's implementation for example? Safari has a nice sidebar where you can easily add (and then remove) items. It's still not necessarily as convenient as opening a tab and closing it when you're done though, unless there's going to be some way to automatically remove items from the list as you open them from it?

You can see reading list in Chrome (it was temporarily disabled in Brave because of some conflicts with sidebar), but I have used Safari's reading list in the past and it is very similar.

Even with a really good reading list though, unless Brave preloads some of these pages (meaning more RAM used) it still means loading each page fresh every time, which will make it still inferior to using tabs. The aim of preloading some (but not all) tabs is to create a balance between responsiveness (page is already loaded) and memory consumption, by trying to load the tabs that will be accessed next so for the user it's functionally equivalent to preloading them all, just without the bloat.

We don't have the resources of Google, Apple or Mozilla (yet) so we really need to keep our focus on features that are important to a large number of our users and afaik this is the only request for something like this. Memory usage is a much bigger issue affecting more people (whether they know it or not).

Renara commented 2 years ago

We don't have the resources of Google, Apple or Mozilla (yet) so we really need to keep our focus on features that are important to a large number of our users and afaik this is the only request for something like this. Memory usage is a much bigger issue affecting more people (whether they know it or not).

Sure, a lot of users who use lots of tabs won't know that they need a feature like this, but that doesn't mean it wouldn't benefit them. What I'm trying to propose is a way to solve the memory usage issue without impacting the user experience; my concern is that trying to simplify the feature could mean a "reduce memory usage at any cost" alternative in which a user ends up with slower overall performance (preloading is essentially pointless, because the wrong tabs are preloaded most of the time).

The way things currently work I can have a heap of tabs open in the background, and as long as Brave hasn't started forcing them to unload, switching to them is usually near instantaneous; the problem is that past a certain point the memory usage will start to slow down either Brave, or other programs that are competing for memory.

This is why I don't think the reading list is an obvious alternative, because if it isn't preloading, and with the need to add/remove items from the list it's going to be slower overall, so it doesn't solve the problem (reduce memory footprint without impacting user experience). Again, when using Safari's reading list I still open a tonne of tabs, because it is more convenient than using the reading list for items I want to consume immediately; I use the reading list for things I don't want to look at yet, e.g- I'll get to them later in the day, or week or whatever, but don't intend to keep them.

Meanwhile the engagement score just doesn't solve the problem either; it might be fine for improving the unloading tabs behaviour when the system is low on memory, but for deciding which tabs to preload (or keep preloaded) it's less useful because if we're talking about a lot of tabs that the user hasn't switched to yet (i.e- never engaged with) then it provides no more useful information. If the fallback is still ultimately least recently used is unloaded first then it will result in the worst possible behaviour (many upcoming tabs are not preloaded because they are the least recently used). The goal of this feature request is to minimise memory usage preemptively, not claw it back once it's too late; they're two different problems, even though they seem similar.

And I just don't see the issue with limiting the number of preloaded tabs? If the goal is to minimise memory usage then it's the most logical thing to do, and focusing preloading of tabs near to the active tab is the most logical way to do it, because it should mean the same behaviour (fast tab switching) for the vast majority of cases, because to defeat it you'd have to be jumping randomly around your tab stack, in which case you can always just increase the preload limit (or set it to "performance" rather than "memory" or whatever) as engagement score would be no better at predicting this, you'd need a machine learning system to even stand a chance, and that'd probably still be wrong most of the time.

I appreciate that you're trying to find a simpler way to solve the same problem, but the problem isn't simply memory usage, it's how to cut down memory usage without the user noticing. Retroactively unloading isn't a better way to do it as it risks making the user experience worse (unnecessary background loading and slower tab switching).

Renara commented 1 year ago

So I see Brave Browser now has a Memory Saver feature but sadly it has the very problem I was hoping to avoid; it saves memory but makes Brave significantly slower as every single tab is double loaded (once when opened, then again when I switch to it after it was unloaded).

As far as I can tell none of my tabs are being preloaded or kept in memory at all, so it's saving memory at a cost to both data (bandwidth) and speed.

The idea being keeping several tabs preloaded, and deferring the rest, is that should save memory while also avoiding both of these problems, because you only ever need to load a tab once, and as long as it preloads the right tabs the browsing performance should be indistinguishable from keeping all tabs in memory (as the tabs should still be loaded in advance).

The only case where speed should be affected is if Brave guesses wrong, but as far as I can tell it's not keeping any tabs in memory at all, or if it is, it's always the very last one (not the nearest N)?

And the Reading List doesn't solve my use case either, as that doesn't seem to preload anything either, so flicking through everything added is again slow.