Commits

Carl Friedrich Bolz committed 11342bf

improve the JITting of thread-local storage

use a slightly hacky workaround because we can use a loop invariant function:
we only support loop-invariant functions without arguments atm. Instead have
last-used cache on the local object. This helps in a lot of cases.

  • Participants
  • Parent commits 42affcc

Comments (0)

Files changed (3)

File pypy/module/pypyjit/test_pypy_c/test_thread.py

         assert round(log.result, 6) == round(main(500), 6)
         loop, = log.loops_by_filename(self.filepath)
         assert loop.match("""
-            i60 = int_lt(i55, i27)
-            guard_true(i60, descr=...)
-            i61 = call(ConstClass(ll_dict_lookup_trampoline__v1134___simple_call__function_), p33, p32, i35, descr=...)
-            guard_no_exception(descr=...)
-            i62 = int_and(i61, -2147483648)
-            i63 = int_is_true(i62)
-            guard_false(i63, descr=...)
-            p64 = getinteriorfield_gc(p41, i61, descr=...)
-            guard_nonnull_class(p64, ConstClass(W_DictMultiObject), descr=...)
-            p65 = getfield_gc(p64, descr=...)
-            guard_class(p65, 176132160, descr=...)
-            p66 = getfield_gc(p64, descr=...)
-            guard_class(p66, 175975744, descr=...)
-            p67 = getfield_gc(p66, descr=...)
-            guard_value(p67, ConstPtr(ptr49), descr=...)
-            p68 = getfield_gc(p66, descr=...)
-            p69 = getarrayitem_gc(p68, 0, descr=...)
-            guard_nonnull_class(p69, ConstClass(W_IntObject), descr=...)
-            i70 = getfield_gc_pure(p69, descr=...)
-            i71 = int_add_ovf(i55, i70)
+            i53 = int_lt(i48, i27)
+            guard_true(i53, descr=...)
+            i54 = int_add_ovf(i48, i47)
             guard_no_overflow(descr=...)
             --TICK--
-            jump(p0, p1, p3, p5, p10, p12, p14, i71, i27, p33, p32, i35, p41, descr=...)
+            i58 = arraylen_gc(p43, descr=...)
+            jump(p0, p1, p3, p5, p10, p12, p14, i54, i27, i47, p45, p43, descr=...)
         """)

File pypy/module/thread/os_local.py

         w_dict = space.newdict(instance=True)
         self.dicts[ec] = w_dict
         self._register_in_ec(ec)
+        # cache the last seen dict, works because we are protected by the GIL
+        self.last_dict = w_dict
+        self.last_ec = ec
 
     def _register_in_ec(self, ec):
         if not ec.space.config.translation.rweakref:
 
     def getdict(self, space):
         ec = space.getexecutioncontext()
+        if ec is self.last_ec:
+            return self.last_dict
         try:
             w_dict = self.dicts[ec]
         except KeyError:
             w_dict = self.create_new_dict(ec)
+        self.last_ec = ec
+        self.last_dict = w_dict
         return w_dict
 
     def descr_local__new__(space, w_subtype, __args__):
         local = wref()
         if local is not None:
             del local.dicts[ec]
+        local.last_dict = None
+        local.last_ec = None

File pypy/module/thread/test/test_local.py

         gc.collect()
         assert done == ['ok', 'del']
         done.append('shutdown')
+
+def test_local_caching():
+    from pypy.module.thread.os_local import Local
+    class FakeSpace:
+        def getexecutioncontext(self):
+            return self.ec
+
+        def getattr(*args):
+            pass
+        def call_obj_args(*args):
+            pass
+        def newdict(*args, **kwargs):
+            return {}
+        def wrap(self, obj):
+            return obj
+        def type(self, obj):
+            return type(obj)
+        class config:
+            class translation:
+                rweakref = True
+
+    class FakeEC:
+        def __init__(self, space):
+            self.space = space
+            self._thread_local_objs = None
+    space = FakeSpace()
+    ec1 = FakeEC(space)
+    space.ec = ec1
+
+    l = Local(space, None)
+    assert l.last_dict is l.dicts[ec1]
+    assert l.last_ec is ec1
+    d1 = l.getdict(space)
+    assert d1 is l.last_dict
+
+    ec2 = space.ec = FakeEC(space)
+    d2 = l.getdict(space)
+    assert l.last_dict is d2
+    assert d2 is l.dicts[ec2]
+    assert l.last_ec is ec2
+    dicts = l.dicts
+    l.dicts = "nope"
+    assert l.getdict(space) is d2
+    l.dicts = dicts
+
+    space.ec = ec1
+    assert l.getdict(space) is d1
+    l.dicts = "nope"
+    assert l.getdict(space) is d1
+    l.dicts = dicts
+