]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- reduce some callcounts related to the lightweight tuple,
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Mar 2015 19:24:01 +0000 (15:24 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Mar 2015 19:24:01 +0000 (15:24 -0400)
basically by caching them.
- improve LRUCache so that size management only proceeds
in one thread, add a real get() method

lib/sqlalchemy/util/_collections.py

index 84895182a20e2d561a9a824b77f4ddd65b2f598b..2d9665e32f2e1cc7303cc1a106151b1cbf207515 100644 (file)
@@ -127,22 +127,6 @@ class _LW(AbstractKeyedTuple):
         return d
 
 
-def lightweight_named_tuple(name, fields):
-
-    tp_cls = type(
-        name, (_LW,),
-        dict([
-            (field, _property_getters[idx])
-            for idx, field in enumerate(fields) if field is not None
-        ])
-    )
-
-    tp_cls._real_fields = fields
-    tp_cls._fields = tuple([f for f in fields if f is not None])
-
-    return tp_cls
-
-
 class ImmutableContainer(object):
     def _immutable(self, *arg, **kw):
         raise TypeError("%s object is immutable" % self.__class__.__name__)
@@ -374,7 +358,10 @@ class OrderedSet(set):
         set.__init__(self)
         self._list = []
         if d is not None:
-            self.update(d)
+            self._list = unique_list(d)
+            set.update(self, self._list)
+        else:
+            self._list = []
 
     def add(self, element):
         if element not in self:
@@ -857,17 +844,30 @@ class LRUCache(dict):
     """Dictionary with 'squishy' removal of least
     recently used items.
 
+    Note that either get() or [] should be used here, but
+    generally its not safe to do an "in" check first as the dictionary
+    can change subsequent to that call.
+
     """
 
     def __init__(self, capacity=100, threshold=.5):
         self.capacity = capacity
         self.threshold = threshold
         self._counter = 0
+        self._mutex = threading.Lock()
 
     def _inc_counter(self):
         self._counter += 1
         return self._counter
 
+    def get(self, key, default=None):
+        item = dict.get(self, key, default)
+        if item is not default:
+            item[2] = self._inc_counter()
+            return item[1]
+        else:
+            return default
+
     def __getitem__(self, key):
         item = dict.__getitem__(self, key)
         item[2] = self._inc_counter()
@@ -893,18 +893,45 @@ class LRUCache(dict):
         self._manage_size()
 
     def _manage_size(self):
-        while len(self) > self.capacity + self.capacity * self.threshold:
-            by_counter = sorted(dict.values(self),
-                                key=operator.itemgetter(2),
-                                reverse=True)
-            for item in by_counter[self.capacity:]:
-                try:
-                    del self[item[0]]
-                except KeyError:
-                    # if we couldn't find a key, most
-                    # likely some other thread broke in
-                    # on us. loop around and try again
-                    break
+        if not self._mutex.acquire(False):
+            return
+        try:
+            while len(self) > self.capacity + self.capacity * self.threshold:
+                by_counter = sorted(dict.values(self),
+                                    key=operator.itemgetter(2),
+                                    reverse=True)
+                for item in by_counter[self.capacity:]:
+                    try:
+                        del self[item[0]]
+                    except KeyError:
+                        # deleted elsewhere; skip
+                        continue
+        finally:
+            self._mutex.release()
+
+
+_lw_tuples = LRUCache(1000)
+
+
+def lightweight_named_tuple(name, fields):
+    hash_ = (name, ) + tuple(fields)
+    tp_cls = _lw_tuples.get(hash_)
+    if tp_cls:
+        return tp_cls
+
+    tp_cls = type(
+        name, (_LW,),
+        dict([
+            (field, _property_getters[idx])
+            for idx, field in enumerate(fields) if field is not None
+        ] + [('__slots__', ())])
+    )
+
+    tp_cls._real_fields = fields
+    tp_cls._fields = tuple([f for f in fields if f is not None])
+
+    _lw_tuples[hash_] = tp_cls
+    return tp_cls
 
 
 class ScopedRegistry(object):