Commit ddb0c9e7 authored by Stefan Behnel's avatar Stefan Behnel

optimise calls on variables that reference builtin methods (e.g. pre-assigned unbound methods)

--HG--
extra : rebase_source : 4aeefab40694c611bfdaa4ecf56a3f4f6cdf3d04
parent e2edd243
This diff is collapsed.
...@@ -37,7 +37,7 @@ class TestMethodDispatcherTransform(TransformTest): ...@@ -37,7 +37,7 @@ class TestMethodDispatcherTransform(TransformTest):
def test_builtin_method(self): def test_builtin_method(self):
calls = [0] calls = [0]
class Test(MethodDispatcherTransform): class Test(MethodDispatcherTransform):
def _handle_simple_method_dict_get(self, node, args, unbound): def _handle_simple_method_dict_get(self, node, func, args, unbound):
calls[0] += 1 calls[0] += 1
return node return node
...@@ -48,10 +48,10 @@ class TestMethodDispatcherTransform(TransformTest): ...@@ -48,10 +48,10 @@ class TestMethodDispatcherTransform(TransformTest):
def test_binop_method(self): def test_binop_method(self):
calls = {'bytes': 0, 'object': 0} calls = {'bytes': 0, 'object': 0}
class Test(MethodDispatcherTransform): class Test(MethodDispatcherTransform):
def _handle_simple_method_bytes___mul__(self, node, args, unbound): def _handle_simple_method_bytes___mul__(self, node, func, args, unbound):
calls['bytes'] += 1 calls['bytes'] += 1
return node return node
def _handle_simple_method_object___mul__(self, node, args, unbound): def _handle_simple_method_object___mul__(self, node, func, args, unbound):
calls['object'] += 1 calls['object'] += 1
return node return node
......
...@@ -517,7 +517,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -517,7 +517,7 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
node = self._dispatch_to_method_handler( node = self._dispatch_to_method_handler(
special_method_name, None, False, type_name, special_method_name, None, False, type_name,
node, [operand1, operand2], None) node, None, [operand1, operand2], None)
return node return node
def visit_UnopNode(self, node): def visit_UnopNode(self, node):
...@@ -532,7 +532,7 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -532,7 +532,7 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
node = self._dispatch_to_method_handler( node = self._dispatch_to_method_handler(
special_method_name, None, False, type_name, special_method_name, None, False, type_name,
node, [operand], None) node, None, [operand], None)
return node return node
### dispatch to specific handlers ### dispatch to specific handlers
...@@ -544,6 +544,22 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -544,6 +544,22 @@ class MethodDispatcherTransform(EnvTransform):
handler = getattr(self, '_handle_any_%s' % match_name, None) handler = getattr(self, '_handle_any_%s' % match_name, None)
return handler return handler
def _delegate_to_assigned_value(self, node, function, arg_list, kwargs):
assignment = function.cf_state[0]
value = assignment.rhs
if value.is_name:
if not value.entry or len(value.entry.cf_assignments) > 1:
# the variable might have been reassigned => play safe
return node
elif value.is_attribute:
if not value.obj.entry or len(value.obj.entry.cf_assignments) > 1:
# the underlying variable might have been reassigned => play safe
return node
else:
return node
return self._dispatch_to_handler(
node, value, arg_list, kwargs)
def _dispatch_to_handler(self, node, function, arg_list, kwargs): def _dispatch_to_handler(self, node, function, arg_list, kwargs):
if function.is_name: if function.is_name:
# we only consider functions that are either builtin # we only consider functions that are either builtin
...@@ -551,18 +567,24 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -551,18 +567,24 @@ class MethodDispatcherTransform(EnvTransform):
# into a C function call (defined in the builtin scope) # into a C function call (defined in the builtin scope)
if not function.entry: if not function.entry:
return node return node
is_builtin = function.entry.is_builtin or\ is_builtin = (
function.entry is self.current_env().builtin_scope().lookup_here(function.name) function.entry.is_builtin or
function.entry is self.current_env().builtin_scope().lookup_here(function.name))
if not is_builtin: if not is_builtin:
if function.cf_state and function.cf_state.is_single:
# we know the value of the variable
# => see if it's usable instead
return self._delegate_to_assigned_value(
node, function, arg_list, kwargs)
return node return node
function_handler = self._find_handler( function_handler = self._find_handler(
"function_%s" % function.name, kwargs) "function_%s" % function.name, kwargs)
if function_handler is None: if function_handler is None:
return node return node
if kwargs: if kwargs:
return function_handler(node, arg_list, kwargs) return function_handler(node, function, arg_list, kwargs)
else: else:
return function_handler(node, arg_list) return function_handler(node, function, arg_list)
elif function.is_attribute and function.type.is_pyobject: elif function.is_attribute and function.type.is_pyobject:
attr_name = function.attribute attr_name = function.attribute
self_arg = function.obj self_arg = function.obj
...@@ -582,13 +604,13 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -582,13 +604,13 @@ class MethodDispatcherTransform(EnvTransform):
type_name = "object" # safety measure type_name = "object" # safety measure
return self._dispatch_to_method_handler( return self._dispatch_to_method_handler(
attr_name, self_arg, is_unbound_method, type_name, attr_name, self_arg, is_unbound_method, type_name,
node, arg_list, kwargs) node, function, arg_list, kwargs)
else: else:
return node return node
def _dispatch_to_method_handler(self, attr_name, self_arg, def _dispatch_to_method_handler(self, attr_name, self_arg,
is_unbound_method, type_name, is_unbound_method, type_name,
node, arg_list, kwargs): node, function, arg_list, kwargs):
method_handler = self._find_handler( method_handler = self._find_handler(
"method_%s_%s" % (type_name, attr_name), kwargs) "method_%s_%s" % (type_name, attr_name), kwargs)
if method_handler is None: if method_handler is None:
...@@ -601,9 +623,11 @@ class MethodDispatcherTransform(EnvTransform): ...@@ -601,9 +623,11 @@ class MethodDispatcherTransform(EnvTransform):
if self_arg is not None: if self_arg is not None:
arg_list = [self_arg] + list(arg_list) arg_list = [self_arg] + list(arg_list)
if kwargs: if kwargs:
return method_handler(node, arg_list, is_unbound_method, kwargs) return method_handler(
node, function, arg_list, is_unbound_method, kwargs)
else: else:
return method_handler(node, arg_list, is_unbound_method) return method_handler(
node, function, arg_list, is_unbound_method)
class RecursiveNodeReplacer(VisitorTransform): class RecursiveNodeReplacer(VisitorTransform):
......
# mode: run
# tag: builtins
cimport cython
@cython.test_assert_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
def unbound_dict_get(d):
"""
>>> unbound_dict_get({})
>>> unbound_dict_get({1:2})
2
"""
get = dict.get
return get(d, 1)
@cython.test_assert_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
def bound_dict_get(dict d):
"""
>>> bound_dict_get({})
>>> bound_dict_get({1:2})
2
"""
get = d.get
return get(1)
@cython.test_fail_if_path_exists(
'//ReturnStatNode//PythonCapiCallNode')
@cython.test_assert_path_exists(
'//ReturnStatNode//SimpleCallNode')
def bound_dict_get_reassign(dict d):
"""
>>> bound_dict_get_reassign({})
>>> bound_dict_get_reassign({1:2})
2
"""
get = d.get
d = {1: 3}
return get(1)
@cython.test_assert_path_exists(
'//PythonCapiCallNode//NameNode[@name="l"]')
def unbound_list_sort(list l):
"""
>>> unbound_list_sort([1, 3, 2])
[1, 2, 3]
>>> unbound_list_sort([1, 3, 2])
[1, 2, 3]
"""
sort = list.sort
sort(l)
return l
@cython.test_assert_path_exists(
'//PythonCapiCallNode//NameNode[@name="l"]')
def bound_list_sort(list l):
"""
>>> bound_list_sort([1, 3, 2])
[1, 2, 3]
>>> bound_list_sort([1, 3, 2])
[1, 2, 3]
"""
sort = l.sort
sort()
return l
@cython.test_fail_if_path_exists(
'//PythonCapiCallNode')
def bound_list_sort_reassign(list l):
"""
>>> bound_list_sort_reassign([1, 3, 2])
[3, 2, 1]
>>> bound_list_sort_reassign([1, 3, 2])
[3, 2, 1]
"""
sort = l.sort
l = [3, 2, 1]
sort()
return l
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment