[pypy-svn] r23010 - in pypy/dist/pypy: interpreter objspace

pedronis at codespeak.net pedronis at codespeak.net
Fri Feb 3 21:58:20 CET 2006


Author: pedronis
Date: Fri Feb  3 21:58:17 2006
New Revision: 23010

Modified:
   pypy/dist/pypy/interpreter/baseobjspace.py
   pypy/dist/pypy/interpreter/function.py
   pypy/dist/pypy/interpreter/pyopcode.py
   pypy/dist/pypy/objspace/descroperation.py
Log:
refactor: attach the fast call logic to functions themself. This introduce some indirection OTOH there no 
significant slowdown in the benchmark/microbenchmark. 

special fast call logic taking directly the valuestack, it allows to use ArgumentsFromValuestack more consistently.

The microbenchmark slightly improve. Calling sin shows a 10% improvement, at the moment this is the Arguments
vs ArgumentsFromValuestack difference. Not much given the complexity, OTOH for more perfomance we would need
even more agressive approaches for the "valuestack" case.

The funccall* interfaces should allow to play with fast path at the code objects/function level, leaving the
rest alone.

There's 3 times slower vs. 6-7 slower difference between calling sin and calling a nested function. There
are obviosuly costs beyond argument parsing itself. Also our floats are likely comparitevely less slower
than our ints.




Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py	(original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py	Fri Feb  3 21:58:17 2006
@@ -1,6 +1,6 @@
 from pypy.interpreter.executioncontext import ExecutionContext
 from pypy.interpreter.error import OperationError
-from pypy.interpreter.argument import Arguments
+from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
 from pypy.interpreter.pycompiler import CPythonCompiler, PythonAstCompiler
 from pypy.interpreter.miscutils import ThreadLocals
 from pypy.tool.cache import Cache 
@@ -480,43 +480,42 @@
     def call_function(self, w_func, *args_w):
         # XXX start of hack for performance
         from pypy.interpreter.function import Function, Method
-        if (isinstance(w_func, Method) and
-            w_func.w_instance is not None and
-            len(args_w) <= 3):
-            return self.call_function(w_func.w_function, w_func.w_instance, *args_w)
-            
-        from pypy.interpreter.function import Function
+        if isinstance(w_func, Method):
+            w_inst = w_func.w_instance
+            if w_inst is not None:
+                func = w_func.w_function
+                return func.funccall(w_inst, *args_w)
+            else:
+                w_func = w_func.w_function
+
         if isinstance(w_func, Function):
-            if len(args_w) == 0:
-                w_res = w_func.code.fastcall_0(self, w_func)
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 1:
-                w_res = w_func.code.fastcall_1(self, w_func, args_w[0])
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 2:
-                w_res = w_func.code.fastcall_2(self, w_func, args_w[0],
-                                               args_w[1])
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 3:
-                w_res = w_func.code.fastcall_3(self, w_func, args_w[0],
-                                               args_w[1], args_w[2])
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 4:
-                w_res = w_func.code.fastcall_4(self, w_func, args_w[0],
-                                               args_w[1], args_w[2], args_w[3])
-                if w_res is not None:
-                    return w_res
-            args = Arguments(self, list(args_w))
-            return w_func.call_args(args)
+            return w_func.funccall(*args_w)
         # XXX end of hack for performance
 
         args = Arguments(self, list(args_w))
         return self.call_args(w_func, args)
 
+    def call_valuestack(self, w_func, nargs, valuestack):
+        # XXX start of hack for performance
+        from pypy.interpreter.function import Function, Method
+        if isinstance(w_func, Method):
+            w_inst = w_func.w_instance
+            if w_inst is not None:
+                func = w_func.w_function
+                return func.funccall_obj_valuestack(w_inst, nargs, valuestack)
+            else:
+                w_func = w_func.w_function
+
+        if isinstance(w_func, Function):
+            return w_func.funccall_valuestack(nargs, valuestack)
+        # XXX end of hack for performance
+
+        args = ArgumentsFromValuestack(self, valuestack, nargs)
+        try:
+            return self.call_args(w_func, args)
+        finally:
+            args.valuestack = None
+
     def call_method(self, w_obj, methname, *arg_w):
         w_meth = self.getattr(w_obj, self.wrap(methname))
         return self.call_function(w_meth, *arg_w)

Modified: pypy/dist/pypy/interpreter/function.py
==============================================================================
--- pypy/dist/pypy/interpreter/function.py	(original)
+++ pypy/dist/pypy/interpreter/function.py	Fri Feb  3 21:58:17 2006
@@ -10,6 +10,7 @@
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.eval import Code
 from pypy.interpreter.pyframe import PyFrame
+from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
 
 class Function(Wrappable):
     """A function is a code object captured with some environment:
@@ -47,6 +48,89 @@
             frame.setfastscope(scope_w)
         return frame.run()
 
+    def funccall(self, *args_w): # speed hack
+        if len(args_w) == 0:
+            w_res = self.code.fastcall_0(self.space, self)
+            if w_res is not None:
+                return w_res
+        elif len(args_w) == 1:
+            w_res = self.code.fastcall_1(self.space, self, args_w[0])
+            if w_res is not None:
+                return w_res
+        elif len(args_w) == 2:
+            w_res = self.code.fastcall_2(self.space, self, args_w[0],
+                                           args_w[1])
+            if w_res is not None:
+                return w_res
+        elif len(args_w) == 3:
+            w_res = self.code.fastcall_3(self.space, self, args_w[0],
+                                           args_w[1], args_w[2])
+            if w_res is not None:
+                return w_res
+        elif len(args_w) == 4:
+            w_res = self.code.fastcall_4(self.space, self, args_w[0],
+                                           args_w[1], args_w[2], args_w[3])
+            if w_res is not None:
+                return w_res
+        return self.call_args(Arguments(self.space, list(args_w)))
+
+    def funccall_valuestack(self, nargs, valuestack): # speed hack
+        if nargs == 0:
+            w_res = self.code.fastcall_0(self.space, self)
+            if w_res is not None:
+                return w_res
+        elif nargs == 1:
+            w_res = self.code.fastcall_1(self.space, self, valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        elif nargs == 2:
+            w_res = self.code.fastcall_2(self.space, self, valuestack.top(1),
+                                         valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        elif nargs == 3:
+            w_res = self.code.fastcall_3(self.space, self, valuestack.top(2),
+                                         valuestack.top(1), valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        elif nargs == 4:
+            w_res = self.code.fastcall_4(self.space, self, valuestack.top(3),
+                                         valuestack.top(2), valuestack.top(1),
+                                         valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        args = ArgumentsFromValuestack(self.space, valuestack, nargs)
+        try:
+            return self.call_args(args)
+        finally:
+            args.valuestack = None
+
+    def funccall_obj_valuestack(self, w_obj, nargs, valuestack): # speed hack
+        if nargs == 0:
+            w_res = self.code.fastcall_1(self.space, self, w_obj)
+            if w_res is not None:
+                return w_res
+        elif nargs == 1:
+            w_res = self.code.fastcall_2(self.space, self, w_obj, valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        elif nargs == 2:
+            w_res = self.code.fastcall_3(self.space, self, w_obj, valuestack.top(1),
+                                         valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        elif nargs == 3:
+            w_res = self.code.fastcall_4(self.space, self, w_obj, valuestack.top(2),
+                                         valuestack.top(1), valuestack.top(0))
+            if w_res is not None:
+                return w_res
+        stkargs = ArgumentsFromValuestack(self.space, valuestack, nargs)
+        args = stkargs.prepend(w_obj)
+        try:
+            return self.call_args(args)
+        finally:
+            stkargs.valuestack = None
+
     def getdict(self):
         return self.w_func_dict
 

Modified: pypy/dist/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyopcode.py	(original)
+++ pypy/dist/pypy/interpreter/pyopcode.py	Fri Feb  3 21:58:17 2006
@@ -657,37 +657,13 @@
         
     def CALL_FUNCTION(f, oparg):
         # XXX start of hack for performance
-        if oparg == 0:      # 0 arg, 0 keyword arg
-            w_function = f.valuestack.pop()
-            w_result = f.space.call_function(w_function)
-            f.valuestack.push(w_result)
-        elif oparg == 1:    # 1 arg, 0 keyword arg
-            w_arg = f.valuestack.pop()
-            w_function = f.valuestack.pop()
-            w_result = f.space.call_function(w_function, w_arg)
-            f.valuestack.push(w_result)
-        elif oparg == 2:    # 2 args, 0 keyword arg
-            w_arg2 = f.valuestack.pop()
-            w_arg1 = f.valuestack.pop()
-            w_function = f.valuestack.pop()
-            w_result = f.space.call_function(w_function, w_arg1, w_arg2)
-            f.valuestack.push(w_result)
-        elif oparg == 3:    # 3 args, 0 keyword arg
-            w_arg3 = f.valuestack.pop()
-            w_arg2 = f.valuestack.pop()
-            w_arg1 = f.valuestack.pop()
-            w_function = f.valuestack.pop()
-            w_result = f.space.call_function(w_function, w_arg1, w_arg2, w_arg3)
-            f.valuestack.push(w_result)
-        elif (oparg >> 8) & 0xff == 0:
+        if (oparg >> 8) & 0xff == 0:
             # Only positional arguments
             nargs = oparg & 0xff
-            args = ArgumentsFromValuestack(f.space, f.valuestack, nargs)
             w_function = f.valuestack.top(nargs)
             try:
-                w_result = f.space.call_args(w_function, args)
+                w_result = f.space.call_valuestack(w_function, nargs, f.valuestack)
             finally:
-                args.valuestack = None
                 f.valuestack.drop(nargs + 1)
             f.valuestack.push(w_result)
         # XXX end of hack for performance

Modified: pypy/dist/pypy/objspace/descroperation.py
==============================================================================
--- pypy/dist/pypy/objspace/descroperation.py	(original)
+++ pypy/dist/pypy/objspace/descroperation.py	Fri Feb  3 21:58:17 2006
@@ -84,26 +84,7 @@
         if type(descr) is Function:
             # the fastcall paths are purely for performance, but the resulting
             # increase of speed is huge
-            if len(args_w) == 0:
-                w_res = descr.code.fastcall_1(space, descr, w_obj)
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 1:
-                w_res = descr.code.fastcall_2(space, descr, w_obj, args_w[0])
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 2:
-                w_res = descr.code.fastcall_3(space, descr, w_obj, args_w[0],
-                                                            args_w[1])
-                if w_res is not None:
-                    return w_res
-            elif len(args_w) == 3:
-                w_res = descr.code.fastcall_4(space, descr, w_obj, args_w[0],
-                                              args_w[1], args_w[2])
-                if w_res is not None:
-                    return w_res
-            args = Arguments(space, list(args_w))
-            return descr.call_args(args.prepend(w_obj))
+            return descr.funccall(w_obj, *args_w)
         else:
             args = Arguments(space, list(args_w))
             w_impl = space.get(w_descr, w_obj)



More information about the Pypy-commit mailing list