speed up ==, hash, <, >, <=, and >= for plain_data master
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 16 Nov 2022 06:02:12 +0000 (22:02 -0800)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 16 Nov 2022 06:02:12 +0000 (22:02 -0800)
this makes test_register_allocate_spread finish in under half as much time for:
https://git.libre-soc.org/?p=bigint-presentation-code.git;a=commit;h=a369418056bf51137af0fc6bdcfc0799697df583

src/nmutil/plain_data.py

index 0ebcb5d2b04500de537ccb1b36cb107bd188fd98..7bde6ba2f27ff3786beab9c036019337e36bbfe7 100644 (file)
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: LGPL-3-or-later
 # Copyright 2022 Jacob Lifshay programmerjake@gmail.com
+import keyword
+
 
 class FrozenPlainDataError(AttributeError):
     pass
@@ -96,6 +98,10 @@ def _decorator(cls, *, eq, unsafe_hash, order, repr_, frozen):
             if not isinstance(field, str):
                 raise TypeError("plain_data() requires __slots__ to be a "
                                 "tuple of str")
+            if not field.isidentifier() or keyword.iskeyword(field):
+                raise TypeError(
+                    "plain_data() requires __slots__ entries to be valid "
+                    "Python identifiers and not keywords")
             if field not in slots:
                 fields.append(field)
             slots[field] = None
@@ -181,52 +187,62 @@ def _decorator(cls, *, eq, unsafe_hash, order, repr_, frozen):
 
     add_method_or_error(__setstate__)
 
-    # get a tuple of all fields
-    def fields_tuple(self):
-        return tuple(getattr(self, name) for name in fields)
+    # get source code that gets a tuple of all fields
+    def fields_tuple(var):
+        # type: (str) -> str
+        l = []
+        for name in fields:
+            l.append(f"{var}.{name}, ")
+        return "(" + "".join(l) + ")"
 
     if eq:
-        def __eq__(self, other):
-            if other.__class__ is not self.__class__:
-                return NotImplemented
-            return fields_tuple(self) == fields_tuple(other)
+        exec(f"""
+def __eq__(self, other):
+    if other.__class__ is not self.__class__:
+        return NotImplemented
+    return {fields_tuple('self')} == {fields_tuple('other')}
 
-        add_method_or_error(__eq__)
+add_method_or_error(__eq__)
+""")
 
     if unsafe_hash:
-        def __hash__(self):
-            return hash(fields_tuple(self))
+        exec(f"""
+def __hash__(self):
+    return hash({fields_tuple('self')})
 
-        add_method_or_error(__hash__)
+add_method_or_error(__hash__)
+""")
 
     if order:
-        def __lt__(self, other):
-            if other.__class__ is not self.__class__:
-                return NotImplemented
-            return fields_tuple(self) < fields_tuple(other)
+        exec(f"""
+def __lt__(self, other):
+    if other.__class__ is not self.__class__:
+        return NotImplemented
+    return {fields_tuple('self')} < {fields_tuple('other')}
 
-        add_method_or_error(__lt__)
+add_method_or_error(__lt__)
 
-        def __le__(self, other):
-            if other.__class__ is not self.__class__:
-                return NotImplemented
-            return fields_tuple(self) <= fields_tuple(other)
+def __le__(self, other):
+    if other.__class__ is not self.__class__:
+        return NotImplemented
+    return {fields_tuple('self')} <= {fields_tuple('other')}
 
-        add_method_or_error(__le__)
+add_method_or_error(__le__)
 
-        def __gt__(self, other):
-            if other.__class__ is not self.__class__:
-                return NotImplemented
-            return fields_tuple(self) > fields_tuple(other)
+def __gt__(self, other):
+    if other.__class__ is not self.__class__:
+        return NotImplemented
+    return {fields_tuple('self')} > {fields_tuple('other')}
 
-        add_method_or_error(__gt__)
+add_method_or_error(__gt__)
 
-        def __ge__(self, other):
-            if other.__class__ is not self.__class__:
-                return NotImplemented
-            return fields_tuple(self) >= fields_tuple(other)
+def __ge__(self, other):
+    if other.__class__ is not self.__class__:
+        return NotImplemented
+    return {fields_tuple('self')} >= {fields_tuple('other')}
 
-        add_method_or_error(__ge__)
+add_method_or_error(__ge__)
+""")
 
     if repr_:
         def __repr__(self):