summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <daniel.silverstone@codethink.co.uk>2018-11-09 11:05:50 +0000
committerDaniel Silverstone <daniel.silverstone@codethink.co.uk>2019-02-12 15:12:30 +0000
commitb6f08e1b8aa9e7ea8f97a2828f16aa002bd9c904 (patch)
tree7543ad38e5e9422693d3733dea293184a0c17027
parentcda03313c85592dd30a57cf76c82e963208eba44 (diff)
downloadbuildstream-b6f08e1b8aa9e7ea8f97a2828f16aa002bd9c904.tar.gz
_yaml.py: Remove use of isinstance() in `{node,list}_{chain_,}copy`
A non-trivial proportion of time pre-scheduler in `bst build` is spent copying (or chain-copying) nodes. Approximately a quarter of the time spent in that effort is in `isinstance()`. This removes that CPU load. Signed-off-by: Daniel Silverstone <daniel.silverstone@codethink.co.uk>
-rw-r--r--buildstream/_yaml.py72
1 files changed, 57 insertions, 15 deletions
diff --git a/buildstream/_yaml.py b/buildstream/_yaml.py
index 9918e4241..0c2fcfabb 100644
--- a/buildstream/_yaml.py
+++ b/buildstream/_yaml.py
@@ -1082,15 +1082,48 @@ class ChainMap(collections.ChainMap):
return default
+# Node copying
+#
+# Unfortunately we copy nodes a *lot* and `isinstance()` is super-slow when
+# things from collections.abc get involved. The result is the following
+# intricate but substantially faster group of tuples and the use of `in`.
+#
+# If any of the {node,list}_{chain_,}_copy routines raise a ValueError
+# then it's likely additional types need adding to these tuples.
+
+# When chaining a copy, these types are skipped since the ChainMap will
+# retrieve them from the source node when needed. Other copiers might copy
+# them, so we call them __QUICK_TYPES.
+__QUICK_TYPES = (str, bool,
+ yaml.scalarstring.PreservedScalarString,
+ yaml.scalarstring.SingleQuotedScalarString,
+ yaml.scalarstring.DoubleQuotedScalarString)
+
+# These types have to be iterated like a dictionary
+__DICT_TYPES = (dict, ChainMap, yaml.comments.CommentedMap)
+
+# These types have to be iterated like a list
+__LIST_TYPES = (list, yaml.comments.CommentedSeq)
+
+# These are the provenance types, which have to be cloned rather than any other
+# copying tactic.
+__PROVENANCE_TYPES = (Provenance, DictProvenance, MemberProvenance, ElementProvenance)
+
+
def node_chain_copy(source):
copy = ChainMap({}, source)
for key, value in source.items():
- if isinstance(value, collections.abc.Mapping):
+ value_type = type(value)
+ if value_type in __DICT_TYPES:
copy[key] = node_chain_copy(value)
- elif isinstance(value, list):
+ elif value_type in __LIST_TYPES:
copy[key] = list_chain_copy(value)
- elif isinstance(value, Provenance):
+ elif value_type in __PROVENANCE_TYPES:
copy[key] = value.clone()
+ elif value_type in __QUICK_TYPES:
+ pass # No need to copy these, the chainmap deals with it
+ else:
+ raise ValueError("Unable to be quick about node_chain_copy of {}".format(value_type))
return copy
@@ -1098,14 +1131,17 @@ def node_chain_copy(source):
def list_chain_copy(source):
copy = []
for item in source:
- if isinstance(item, collections.abc.Mapping):
+ item_type = type(item)
+ if item_type in __DICT_TYPES:
copy.append(node_chain_copy(item))
- elif isinstance(item, list):
+ elif item_type in __LIST_TYPES:
copy.append(list_chain_copy(item))
- elif isinstance(item, Provenance):
+ elif item_type in __PROVENANCE_TYPES:
copy.append(item.clone())
- else:
+ elif item_type in __QUICK_TYPES:
copy.append(item)
+ else: # Fallback
+ raise ValueError("Unable to be quick about list_chain_copy of {}".format(item_type))
return copy
@@ -1113,14 +1149,17 @@ def list_chain_copy(source):
def node_copy(source):
copy = {}
for key, value in source.items():
- if isinstance(value, collections.abc.Mapping):
+ value_type = type(value)
+ if value_type in __DICT_TYPES:
copy[key] = node_copy(value)
- elif isinstance(value, list):
+ elif value_type in __LIST_TYPES:
copy[key] = list_copy(value)
- elif isinstance(value, Provenance):
+ elif value_type in __PROVENANCE_TYPES:
copy[key] = value.clone()
- else:
+ elif value_type in __QUICK_TYPES:
copy[key] = value
+ else:
+ raise ValueError("Unable to be quick about node_copy of {}".format(value_type))
ensure_provenance(copy)
@@ -1130,14 +1169,17 @@ def node_copy(source):
def list_copy(source):
copy = []
for item in source:
- if isinstance(item, collections.abc.Mapping):
+ item_type = type(item)
+ if item_type in __DICT_TYPES:
copy.append(node_copy(item))
- elif isinstance(item, list):
+ elif item_type in __LIST_TYPES:
copy.append(list_copy(item))
- elif isinstance(item, Provenance):
+ elif item_type in __PROVENANCE_TYPES:
copy.append(item.clone())
- else:
+ elif item_type in __QUICK_TYPES:
copy.append(item)
+ else:
+ raise ValueError("Unable to be quick about list_copy of {}".format(item_type))
return copy