optimize for empty function

This commit is contained in:
blueloveTH 2024-04-11 01:31:45 +08:00
parent 335d35cbc9
commit 49ee693d40
6 changed files with 54 additions and 4 deletions

8
benchmarks/function_0.py Normal file
View File

@ -0,0 +1,8 @@
def f(a, b, c):
pass
for i in range(10000000):
f(1, 2, 3)
f(1, 2, 3)
f(1, 2, 3)
f(1, 2, 3)

11
benchmarks/function_1.py Normal file
View File

@ -0,0 +1,11 @@
class A:
def f(self, a, b, c):
pass
a = A()
for i in range(10000000):
a.f(1, 2, 3)
a.f(1, 2, 3)
a.f(1, 2, 3)
a.f(1, 2, 3)

View File

@ -107,7 +107,9 @@ struct FuncDecl {
Str signature; // signature of this function Str signature; // signature of this function
Str docstring; // docstring of this function Str docstring; // docstring of this function
bool is_simple;
bool is_simple; // whether this function is simple (no *arg, **kwarg, nested)
bool is_empty; // whether this function is empty (no code)
NameDictInt kw_to_index; NameDictInt kw_to_index;

View File

@ -67,6 +67,14 @@ namespace pkpy{
if(func->kwargs.size() > 0) func->is_simple = false; if(func->kwargs.size() > 0) func->is_simple = false;
if(func->starred_arg >= 0) func->is_simple = false; if(func->starred_arg >= 0) func->is_simple = false;
if(func->starred_kwarg >= 0) func->is_simple = false; if(func->starred_kwarg >= 0) func->is_simple = false;
func->is_empty = false;
if(func->code->codes.size() == 1){
Bytecode bc = func->code->codes[0];
if(bc.op == OP_RETURN_VALUE && bc.arg == 1){
func->is_empty = true;
}
}
} }
contexts.pop(); contexts.pop();
} }

View File

@ -862,7 +862,7 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
PyObject* callable = p1[-(ARGC + 2)]; PyObject* callable = p1[-(ARGC + 2)];
Type callable_t = _tp(callable); Type callable_t = _tp(callable);
bool method_call = p0[1] != PY_NULL; int method_call = p0[1] != PY_NULL;
// handle boundmethod, do a patch // handle boundmethod, do a patch
if(callable_t == tp_bound_method){ if(callable_t == tp_bound_method){
@ -872,11 +872,11 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
callable_t = _tp(callable); callable_t = _tp(callable);
p1[-(ARGC + 2)] = bm.func; p1[-(ARGC + 2)] = bm.func;
p1[-(ARGC + 1)] = bm.self; p1[-(ARGC + 1)] = bm.self;
method_call = true; method_call = 1;
// [unbound, self, args..., kwargs...] // [unbound, self, args..., kwargs...]
} }
ArgsView args(p1 - ARGC - int(method_call), p1); ArgsView args(p1 - ARGC - method_call, p1);
ArgsView kwargs(p1, s_data._sp); ArgsView kwargs(p1, s_data._sp);
PyObject** _base = args.begin(); PyObject** _base = args.begin();
@ -884,11 +884,13 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
if(callable_t == tp_function){ if(callable_t == tp_function){
/*****************_py_call*****************/ /*****************_py_call*****************/
// check stack overflow
if(s_data.is_overflow()) StackOverflowError(); if(s_data.is_overflow()) StackOverflowError();
const Function& fn = PK_OBJ_GET(Function, callable); const Function& fn = PK_OBJ_GET(Function, callable);
const CodeObject* co = fn.decl->code.get(); const CodeObject* co = fn.decl->code.get();
int co_nlocals = co->varnames.size(); int co_nlocals = co->varnames.size();
if(fn.decl->is_simple){ if(fn.decl->is_simple){
if(args.size() != fn.decl->args.size()){ if(args.size() != fn.decl->args.size()){
TypeError(_S( TypeError(_S(
@ -896,6 +898,13 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
)); ));
} }
if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments")); if(!kwargs.empty()) TypeError(_S(co->name, "() takes no keyword arguments"));
// fast path for empty function
if(fn.decl->is_empty){
s_data.reset(p0);
return None;
}
// [callable, <self>, args..., local_vars...] // [callable, <self>, args..., local_vars...]
// ^p0 ^p1 ^_sp // ^p0 ^p1 ^_sp
s_data.reset(_base + co_nlocals); s_data.reset(_base + co_nlocals);

View File

@ -140,3 +140,15 @@ try:
exit(1) exit(1)
except TypeError: except TypeError:
pass pass
# empty function
def f(a, b, c):
pass
assert f(1, 2, 3) == None
class A:
def f(self, a, b, c):
pass
assert A().f(1, 2, 3) == None