mirror of
https://github.com/m-labs/artiq.git
synced 2025-02-12 18:43:19 +08:00
Avoid quadratic behaviour in _unify_attribute
(#2673)
ARTIQ maintains a list of all known instances of a particular class, and after each attribute access, `_unify_attribute` is called to check all these instances have this attribute (and it's the right type). While the attribute type computation is cached, this is still O(nm) (with n being the number of instances, and m the number of attribute lookups). For something like ndscan's `FloatParamHandle.get`, you can have a lot of both. This commit changes `_unify_attribute` to only check the attributes of new instances, effectively lowering the complexity to O(n). This provides a small (5%) boost to compile times. Signed-off-by: Jonathan Coates <jonathan.coates@oxionics.com>
This commit is contained in:
parent
ae12270363
commit
c8d0ab9afe
@ -274,6 +274,27 @@ class EmbeddingMap:
|
||||
))
|
||||
|
||||
|
||||
class _ValueInfo:
|
||||
"""
|
||||
A collection of all values of a particular type.
|
||||
|
||||
Attributes:
|
||||
|
||||
:attr:`objects`: A list of all objects and the location they were added from.
|
||||
:attr:`unchecked_attributes`: The known attributes for this type, and a list of
|
||||
values where we have not yet checked the attribute's presence.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.objects: list[tuple[object, source.Range]] = []
|
||||
self.unchecked_attributes: dict[str, list[tuple[object, source.Range]]] = {}
|
||||
|
||||
def append(self, val_and_loc):
|
||||
self.objects.append(val_and_loc)
|
||||
|
||||
for attr_store in self.unchecked_attributes.values():
|
||||
attr_store.append(val_and_loc)
|
||||
|
||||
|
||||
class ASTSynthesizer:
|
||||
def __init__(self, embedding_map, value_map, quote_function=None, expanded_from=None):
|
||||
self.source = ""
|
||||
@ -807,7 +828,13 @@ class StitchingInferencer(Inferencer):
|
||||
# that we can successfully serialize the value of the attribute we
|
||||
# are now adding at the code generation stage.
|
||||
object_type = value_node.type.find()
|
||||
for object_value, object_loc in self.value_map[object_type]:
|
||||
values: _ValueInfo = self.value_map[object_type]
|
||||
|
||||
# Take all objects whose attribute we haven't checked yet.
|
||||
attribute_objects = values.unchecked_attributes.get(attr_name, values.objects)
|
||||
values.unchecked_attributes[attr_name] = []
|
||||
|
||||
for object_value, object_loc in attribute_objects:
|
||||
attr_type_key = (id(object_value), attr_name)
|
||||
try:
|
||||
attributes, attr_value_type = self.attr_type_cache[attr_type_key]
|
||||
@ -888,7 +915,7 @@ class Stitcher:
|
||||
self.functions = {}
|
||||
|
||||
self.embedding_map = EmbeddingMap(old_embedding_map)
|
||||
self.value_map = defaultdict(lambda: [])
|
||||
self.value_map = defaultdict(_ValueInfo)
|
||||
self.definitely_changed = False
|
||||
|
||||
self.destination = destination
|
||||
@ -946,7 +973,7 @@ class Stitcher:
|
||||
# value is guaranteed to have it too.
|
||||
continue
|
||||
|
||||
for value, loc in self.value_map[instance_type]:
|
||||
for value, loc in self.value_map[instance_type].objects:
|
||||
if hasattr(value, attribute):
|
||||
continue
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user