mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-21 12:00:18 +00:00
optimize for empty function
This commit is contained in:
parent
335d35cbc9
commit
49ee693d40
8
benchmarks/function_0.py
Normal file
8
benchmarks/function_0.py
Normal 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
11
benchmarks/function_1.py
Normal 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)
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
15
src/vm.cpp
15
src/vm.cpp
@ -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);
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user