Merge ~kissiel/plainbox:optimizations into plainbox:master

Proposed by Maciej Kisielewski
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 84e3b2c96566df7322a2dd661c260b60604b55bc
Merged at revision: 9cdf73b29be262eaf57c0a1cad1c76dfef94031e
Proposed branch: ~kissiel/plainbox:optimizations
Merge into: plainbox:master
Diff against target: 177 lines (+36/-14)
7 files modified
plainbox/impl/commands/cmd_session.py (+1/-1)
plainbox/impl/secure/providers/v1.py (+7/-1)
plainbox/impl/secure/qualifiers.py (+8/-1)
plainbox/impl/secure/rfc822.py (+7/-4)
plainbox/impl/session/state.py (+10/-4)
plainbox/impl/session/test_manager.py (+2/-2)
plainbox/impl/unit/unit.py (+1/-1)
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Maciej Kisielewski Needs Resubmitting
Review via email: mp+308936@code.launchpad.net

Description of the change

This branch brings a bunch of optimizations to plainbox/checkbox spoolup time.

When invoking `plainbox -h`, the elapsed time is 5-10x shorter.
As or checkbox-cli invocations it's usually more than 2x faster.

I tested it through and through with launchers and with explicit plainbox invocations.
This branch should not bring any changes in functionality

To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

repushed

review: Needs Resubmitting
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Amazing work, each commit bringing its own performance fix.
Unit tests now run ~ 2X faster.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/plainbox/impl/commands/cmd_session.py b/plainbox/impl/commands/cmd_session.py
2index 066e399..cb41241 100644
3--- a/plainbox/impl/commands/cmd_session.py
4+++ b/plainbox/impl/commands/cmd_session.py
5@@ -112,7 +112,7 @@ class SessionCommand(PlainBoxCommand):
6 group = export_parser.add_argument_group(_("output options"))
7 group.add_argument(
8 '-f', '--output-format', default='text',
9- metavar=_('FORMAT'), choices=[_('?')] + get_all_exporter_names(),
10+ metavar=_('FORMAT'), choices=[_('?')],
11 help=_('save test results in the specified FORMAT'
12 ' (pass ? for a list of choices)'))
13 group.add_argument(
14diff --git a/plainbox/impl/secure/providers/v1.py b/plainbox/impl/secure/providers/v1.py
15index 9f85bcb..fb9a59f 100644
16--- a/plainbox/impl/secure/providers/v1.py
17+++ b/plainbox/impl/secure/providers/v1.py
18@@ -314,8 +314,14 @@ class UnitPlugIn(ProviderContentPlugIn):
19 for unit in (unit for unit in inspect_result
20 if unit.Meta.name == 'test plan'):
21 if unit.include is not None:
22+ patterns = []
23+ for line in unit.include.split('\n'):
24+ if '#' in line:
25+ line = line.split('#')[0]
26+ if line:
27+ patterns.append('${}^'.format(line))
28 yield WhiteList(
29- unit.include, name=unit.partial_id, origin=unit.origin,
30+ patterns, name=unit.partial_id, origin=unit.origin,
31 implicit_namespace=unit.provider.namespace)
32
33 # NOTE: this version of plugin_object() is just for legacy code support
34diff --git a/plainbox/impl/secure/qualifiers.py b/plainbox/impl/secure/qualifiers.py
35index 9f8e4af..773d953 100644
36--- a/plainbox/impl/secure/qualifiers.py
37+++ b/plainbox/impl/secure/qualifiers.py
38@@ -136,13 +136,20 @@ class RegExpJobQualifier(SimpleQualifier):
39 expression
40 """
41
42+ # RegExp qualifier is instantiated many times. To speed up the process,
43+ # let's keep cache of compiled re object. (usually there are only a few of
44+ # those
45+ re_cache = dict()
46+
47 def __init__(self, pattern, origin, inclusive=True):
48 """
49 Initialize a new RegExpJobQualifier with the specified pattern.
50 """
51 super().__init__(origin, inclusive)
52 try:
53- self._pattern = re.compile(pattern)
54+ if pattern not in self.re_cache:
55+ self.re_cache[pattern] = re.compile(pattern)
56+ self._pattern = self.re_cache[pattern]
57 except sre_constants.error as exc:
58 assert len(exc.args) == 1
59 # XXX: This is a bit crazy but this lets us have identical error
60diff --git a/plainbox/impl/secure/rfc822.py b/plainbox/impl/secure/rfc822.py
61index d194fd7..f144a1e 100644
62--- a/plainbox/impl/secure/rfc822.py
63+++ b/plainbox/impl/secure/rfc822.py
64@@ -42,10 +42,13 @@ logger = logging.getLogger("plainbox.secure.rfc822")
65
66
67 def normalize_rfc822_value(value):
68- # Remove the multi-line dot marker
69- value = re.sub('^(\s*)\.$', '\\1', value, flags=re.M)
70- # Remove consistent indentation
71- value = textwrap.dedent(value)
72+ # multi-line markers and consistent indentation happens only on multi-line
73+ # values, so let's run those operations only on multi-line values
74+ if value.count('\n') > 1:
75+ # Remove the multi-line dot marker
76+ value = re.sub('^(\s*)\.$', '\\1', value, flags=re.M)
77+ # Remove consistent indentation
78+ value = textwrap.dedent(value)
79 # Strip the remaining whitespace
80 value = value.strip()
81 return value
82diff --git a/plainbox/impl/session/state.py b/plainbox/impl/session/state.py
83index e92ddfa..d36b4ea 100644
84--- a/plainbox/impl/session/state.py
85+++ b/plainbox/impl/session/state.py
86@@ -245,6 +245,7 @@ class SessionDeviceContext:
87 self._provider_list = []
88 self._state = SessionState(self._unit_list)
89 self._unit_id_map = {}
90+ self._already_added_checksums = set()
91 else:
92 if not isinstance(state, SessionState):
93 raise TypeError
94@@ -257,6 +258,8 @@ class SessionDeviceContext:
95 self._state = state
96 self._unit_id_map = {unit.id: unit for unit in state.unit_list if
97 isinstance(unit, UnitWithId)}
98+ self._already_added_checksums = set(
99+ [unit.checksum for unit in self.unit_list])
100
101 self._test_plan_list = []
102 # Connect SessionState's signals to fire our signals. This
103@@ -397,9 +400,10 @@ class SessionDeviceContext:
104 self.on_provider_added(provider)
105 if add_units:
106 for unit in provider.unit_list:
107- self.add_unit(unit)
108+ self.add_unit(unit, False)
109+ self.state._recompute_job_readiness()
110
111- def add_unit(self, unit):
112+ def add_unit(self, unit, recompute=True):
113 """
114 Add a unit to the context.
115
116@@ -413,10 +417,11 @@ class SessionDeviceContext:
117
118 This method fires the :meth:`on_unit_added()` signal
119 """
120- if unit in frozenset(self._unit_list):
121+ if unit.checksum in self._already_added_checksums:
122 raise ValueError(
123 _("attempting to add the same unit twice: %s" % unit.id))
124- self.state.add_unit(unit)
125+ self._already_added_checksums.add(unit.checksum)
126+ self.state.add_unit(unit, recompute)
127 # NOTE: no need to fire the on_unit_added() signal because the state
128 # object and we've connected it to will fire our version.
129
130@@ -432,6 +437,7 @@ class SessionDeviceContext:
131 if unit not in self._unit_list:
132 raise ValueError(
133 _("attempting to remove unit not in this context"))
134+ self._already_added_checksums.remove(unit.checksum)
135 self.state.remove_unit(unit)
136 # NOTE: no need to fire the on_unit_removed() signal because the state
137 # object and we've connected it to will fire our version.
138diff --git a/plainbox/impl/session/test_manager.py b/plainbox/impl/session/test_manager.py
139index 1c43bfd..975a6a0 100644
140--- a/plainbox/impl/session/test_manager.py
141+++ b/plainbox/impl/session/test_manager.py
142@@ -26,12 +26,12 @@ Test definitions for plainbox.impl.session.manager module
143
144 from unittest import expectedFailure
145
146-from plainbox.abc import IJobDefinition
147 from plainbox.impl.session import SessionManager
148 from plainbox.impl.session import SessionState
149 from plainbox.impl.session import SessionStorage
150 from plainbox.impl.session.state import SessionDeviceContext
151 from plainbox.impl.session.suspend import SessionSuspendHelper
152+from plainbox.impl.unit.job import JobDefinition
153 from plainbox.vendor import mock
154 from plainbox.vendor.morris import SignalTestCase
155
156@@ -105,7 +105,7 @@ class SessionManagerTests(SignalTestCase):
157 verify that SessionManager.load_session() correctly delegates the task
158 to various other objects
159 """
160- job = mock.Mock(name='job', spec_set=IJobDefinition)
161+ job = mock.Mock(name='job', spec_set=JobDefinition)
162 unit_list = [job]
163 flags = None
164 helper_name = "plainbox.impl.session.manager.SessionResumeHelper"
165diff --git a/plainbox/impl/unit/unit.py b/plainbox/impl/unit/unit.py
166index 0b7b4ce..33ba52c 100644
167--- a/plainbox/impl/unit/unit.py
168+++ b/plainbox/impl/unit/unit.py
169@@ -560,7 +560,7 @@ class Unit(metaclass=UnitType):
170 """
171 value = self._data.get('_{}'.format(name))
172 if value is None:
173- value = self._data.get('{}'.format(name), default)
174+ value = self._data.get(name, default)
175 if value is not None and self.is_parametric:
176 value = string.Formatter().vformat(value, (), self.parameters)
177 return value

Subscribers

People subscribed via source and target branches