From 33d9d4fe90597d6c383b132ddb5e1bfaeaeebabe Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sat, 10 Feb 2018 13:21:04 -0500 Subject: [PATCH 1/3] Improve performance of startup and shutdown We call 'update_tab_titles' a lot of times which calls 'setTabText' on every tab. 'setTabText' calls tabSizeHint and minTabSizeHint on every tab as well, meaning this is an n^2 operation repeated many times. First, this prevents setTabText from being called unless it's needed, removing most of the work done. Second, I remove tabs in reverse, to avoid recomputing the above for every tab on shutdown (which is at least n^3) --- qutebrowser/mainwindow/tabbedbrowser.py | 5 ++++- qutebrowser/mainwindow/tabwidget.py | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index cd9f52b37..8cee35524 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -253,7 +253,10 @@ class TabbedBrowser(tabwidget.TabWidget): def shutdown(self): """Try to shut down all tabs cleanly.""" self.shutting_down = True - for tab in self.widgets(): + # Reverse tabs so we don't have to recacluate tab titles over and over + # Removing first causes [2..-1] to be recomputed + # Removing the last causes nothing to be recomputed + for tab in reversed(self.widgets()): self._remove_tab(tab) def tab_close_prompt_if_pinned( diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index da1f2624b..6cded2a71 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -150,8 +150,9 @@ class TabWidget(QTabWidget): title = '' if fmt is None else fmt.format(**fields) tabbar = self.tabBar() - tabbar.setTabText(idx, title) - tabbar.setTabToolTip(idx, title) + if tabbar.tabText(idx) != title: + tabbar.setTabText(idx, title) + tabbar.setTabToolTip(idx, title) def get_tab_fields(self, idx): """Get the tab field data.""" From 11e04c79f4aca245ed140146f69f6229034a600e Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sat, 10 Feb 2018 15:10:33 -0500 Subject: [PATCH 2/3] Add add/remove tests to benchmarks --- tests/helpers/stubs.py | 3 +++ tests/unit/mainwindow/test_tabwidget.py | 34 ++++++++++++++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index 711b1105f..64bc793cb 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -261,6 +261,9 @@ class FakeWebTab(browsertab.AbstractTab): def load_status(self): return self._load_status + def shutdown(self): + pass + class FakeSignal: diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index 1f0ff55d5..7ad22fcc3 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -23,7 +23,7 @@ import pytest from PyQt5.QtGui import QIcon, QPixmap -from qutebrowser.mainwindow import tabwidget +from qutebrowser.mainwindow import tabwidget, tabbedbrowser from qutebrowser.utils import usertypes @@ -39,6 +39,14 @@ class TestTabWidget: usertypes.Backend.QtWebKit) return w + @pytest.fixture + def browser(self, qtbot, monkeypatch, config_stub): + w = tabbedbrowser.TabbedBrowser(win_id=0, private=False) + qtbot.addWidget(w) + monkeypatch.setattr(tabwidget.objects, 'backend', + usertypes.Backend.QtWebKit) + return w + def test_small_icon_doesnt_crash(self, widget, qtbot, fake_web_tab): """Test that setting a small icon doesn't produce a crash. @@ -53,15 +61,29 @@ class TestTabWidget: with qtbot.waitExposed(widget): widget.show() + @pytest.mark.parametrize("num_tabs", [4, 10]) def test_update_tab_titles_benchmark(self, benchmark, widget, - qtbot, fake_web_tab): + qtbot, fake_web_tab, num_tabs): """Benchmark for update_tab_titles.""" - widget.addTab(fake_web_tab(), 'foobar') - widget.addTab(fake_web_tab(), 'foobar2') - widget.addTab(fake_web_tab(), 'foobar3') - widget.addTab(fake_web_tab(), 'foobar4') + for i in range(num_tabs): + widget.addTab(fake_web_tab(), 'foobar' + str(i)) with qtbot.waitExposed(widget): widget.show() benchmark(widget._update_tab_titles) + + @pytest.mark.parametrize("num_tabs", [4, 10]) + def test_add_remove_tab_benchmark(self, benchmark, browser, + qtbot, fake_web_tab, num_tabs): + """Benchmark for addTab and removeTab.""" + def _run_bench(): + for i in range(num_tabs): + browser.addTab(fake_web_tab(), 'foobar' + str(i)) + + with qtbot.waitExposed(browser): + browser.show() + + browser.shutdown() + + benchmark(_run_bench) From f6eb8929c35c03f0db2217f8381fb25bb31cd6a4 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sun, 11 Feb 2018 00:19:08 -0500 Subject: [PATCH 3/3] Fix incorrect scroll offset after tab pin --- qutebrowser/mainwindow/tabwidget.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 6cded2a71..965e5b219 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -110,8 +110,6 @@ class TabWidget(QTabWidget): tab.data.pinned = pinned self._update_tab_title(idx) - bar.refresh() - def tab_indicator_color(self, idx): """Get the tab indicator color for the given index.""" return self.tabBar().tab_indicator_color(idx)