From 0806b67dbf73188049f61d27ce73638ee907ea5e Mon Sep 17 00:00:00 2001 From: pca006132 Date: Fri, 2 Jul 2021 13:54:54 +0800 Subject: [PATCH] compiler: speedup list processing --- artiq/compiler/embedding.py | 72 ++++++++++-- .../compiler/transforms/llvm_ir_generator.py | 104 +++++++++++------- 2 files changed, 125 insertions(+), 51 deletions(-) diff --git a/artiq/compiler/embedding.py b/artiq/compiler/embedding.py index affb69b9b..4ff303274 100644 --- a/artiq/compiler/embedding.py +++ b/artiq/compiler/embedding.py @@ -156,6 +156,67 @@ class ASTSynthesizer: return source.Range(self.source_buffer, range_from, range_to, expanded_from=self.expanded_from) + def fast_quote_list(self, value): + elts = [None] * len(value) + is_T = False + if len(value) > 0: + v = value[0] + is_T = True + if isinstance(v, int): + T = int + elif isinstance(v, float): + T = float + elif isinstance(v, numpy.int32): + T = numpy.int32 + elif isinstance(v, numpy.int64): + T = numpy.int64 + else: + is_T = False + if is_T: + for v in value: + if not isinstance(v, T): + is_T = False + break + if is_T: + is_int = T != float + if T == int: + typ = builtins.TInt() + elif T == float: + typ = builtins.TFloat() + elif T == numpy.int32: + typ = builtins.TInt32() + elif T == numpy.int64: + typ = builtins.TInt64() + else: + assert False + text = [repr(elt) for elt in value] + start = len(self.source) + self.source += ", ".join(text) + if is_int: + for i, (v, t) in enumerate(zip(value, text)): + l = len(t) + elts[i] = asttyped.NumT( + n=int(v), ctx=None, type=typ, + loc=source.Range( + self.source_buffer, start, start + l, + expanded_from=self.expanded_from)) + start += l + 2 + else: + for i, (v, t) in enumerate(zip(value, text)): + l = len(t) + elts[i] = asttyped.NumT( + n=v, ctx=None, type=typ, + loc=source.Range( + self.source_buffer, start, start + l, + expanded_from=self.expanded_from)) + start += l + 2 + else: + for index, elt in enumerate(value): + elts[index] = self.quote(elt) + if index < len(value) - 1: + self._add(", ") + return elts + def quote(self, value): """Construct an AST fragment equal to `value`.""" if value is None: @@ -217,21 +278,14 @@ class ASTSynthesizer: return asttyped.QuoteT(value=value, type=builtins.TByteArray(), loc=loc) elif isinstance(value, list): begin_loc = self._add("[") - elts = [] - for index, elt in enumerate(value): - elts.append(self.quote(elt)) - if index < len(value) - 1: - self._add(", ") + elts = self.fast_quote_list(value) end_loc = self._add("]") return asttyped.ListT(elts=elts, ctx=None, type=builtins.TList(), begin_loc=begin_loc, end_loc=end_loc, loc=begin_loc.join(end_loc)) elif isinstance(value, tuple): begin_loc = self._add("(") - elts = [] - for index, elt in enumerate(value): - elts.append(self.quote(elt)) - self._add(", ") + elts = self.fast_quote_list(value) end_loc = self._add(")") return asttyped.TupleT(elts=elts, ctx=None, type=types.TTuple([e.type for e in elts]), diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 49bbebc57..b8df85d17 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -180,6 +180,7 @@ class LLVMIRGenerator: self.tbaa_tree, ll.Constant(lli64, 1) ]) + self.quote_fail_msg = None def needs_sret(self, lltyp, may_be_large=True): if isinstance(lltyp, ll.VoidType): @@ -1492,8 +1493,24 @@ class LLVMIRGenerator: return llresult def _quote_listish_to_llglobal(self, value, elt_type, path, kind_name): - llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)]) - for i in range(len(value))] + fail_msg = "at " + ".".join(path()) + if len(value) > 0: + if builtins.is_int(elt_type): + int_typ = (int, numpy.int32, numpy.int64) + for v in value: + assert isinstance(v, int_typ), fail_msg + llty = self.llty_of_type(elt_type) + llelts = [ll.Constant(llty, int(v)) for v in value] + elif builtins.is_float(elt_type): + for v in value: + assert isinstance(v, float), fail_msg + llty = self.llty_of_type(elt_type) + llelts = [ll.Constant(llty, v) for v in value] + else: + llelts = [self._quote(value[i], elt_type, lambda: path() + [str(i)]) + for i in range(len(value))] + else: + assert False, fail_msg lleltsary = ll.Constant(ll.ArrayType(self.llty_of_type(elt_type), len(llelts)), list(llelts)) name = self.llmodule.scope.deduplicate("quoted.{}".format(kind_name)) @@ -1502,60 +1519,63 @@ class LLVMIRGenerator: llglobal.linkage = "private" return llglobal.bitcast(lleltsary.type.element.as_pointer()) + def _quote_attributes(self, value, typ, path, value_id, llty): + llglobal = None + llfields = [] + emit_as_constant = True + for attr in typ.attributes: + if attr == "__objectid__": + objectid = self.embedding_map.store_object(value) + llfields.append(ll.Constant(lli32, objectid)) + + assert llglobal is None + if types.is_constructor(typ): + llglobal = self.get_class(typ) + else: + llglobal = ll.GlobalVariable(self.llmodule, llty.pointee, + name="O.{}".format(objectid)) + + self.llobject_map[value_id] = llglobal + else: + attrvalue = getattr(value, attr) + is_class_function = (types.is_constructor(typ) and + types.is_function(typ.attributes[attr]) and + not types.is_external_function(typ.attributes[attr])) + if is_class_function: + attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue) + if not (types.is_instance(typ) and attr in typ.constant_attributes): + emit_as_constant = False + llattrvalue = self._quote(attrvalue, typ.attributes[attr], + lambda: path() + [attr]) + llfields.append(llattrvalue) + if is_class_function: + llclosureptr = self.get_global_closure_ptr(typ, attr) + llclosureptr.initializer = llattrvalue + + llglobal.global_constant = emit_as_constant + llglobal.initializer = ll.Constant(llty.pointee, llfields) + llglobal.linkage = "private" + return llglobal + def _quote(self, value, typ, path): value_id = id(value) if value_id in self.llobject_map: return self.llobject_map[value_id] llty = self.llty_of_type(typ) - def _quote_attributes(): - llglobal = None - llfields = [] - emit_as_constant = True - for attr in typ.attributes: - if attr == "__objectid__": - objectid = self.embedding_map.store_object(value) - llfields.append(ll.Constant(lli32, objectid)) + fail_msg = self.quote_fail_msg + if fail_msg == None: + self.quote_fail_msg = fail_msg = "at " + ".".join(path()) - assert llglobal is None - if types.is_constructor(typ): - llglobal = self.get_class(typ) - else: - llglobal = ll.GlobalVariable(self.llmodule, llty.pointee, - name="O.{}".format(objectid)) - - self.llobject_map[value_id] = llglobal - else: - attrvalue = getattr(value, attr) - is_class_function = (types.is_constructor(typ) and - types.is_function(typ.attributes[attr]) and - not types.is_external_function(typ.attributes[attr])) - if is_class_function: - attrvalue = self.embedding_map.specialize_function(typ.instance, attrvalue) - if not (types.is_instance(typ) and attr in typ.constant_attributes): - emit_as_constant = False - llattrvalue = self._quote(attrvalue, typ.attributes[attr], - lambda: path() + [attr]) - llfields.append(llattrvalue) - if is_class_function: - llclosureptr = self.get_global_closure_ptr(typ, attr) - llclosureptr.initializer = llattrvalue - - llglobal.global_constant = emit_as_constant - llglobal.initializer = ll.Constant(llty.pointee, llfields) - llglobal.linkage = "private" - return llglobal - - fail_msg = "at " + ".".join(path()) if types.is_constructor(typ) or types.is_instance(typ): if types.is_instance(typ): # Make sure the class functions are quoted, as this has the side effect of # initializing the global closures. self._quote(type(value), typ.constructor, lambda: path() + ['__class__']) - return _quote_attributes() + return self._quote_attributes(value, typ, path, value_id, llty) elif types.is_module(typ): - return _quote_attributes() + return self._quote_attributes(value, typ, path, value_id, llty) elif builtins.is_none(typ): assert value is None, fail_msg return ll.Constant.literal_struct([])