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 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;
|
||||
|
||||
|
@ -67,6 +67,14 @@ namespace pkpy{
|
||||
if(func->kwargs.size() > 0) func->is_simple = false;
|
||||
if(func->starred_arg >= 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();
|
||||
}
|
||||
|
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)];
|
||||
Type callable_t = _tp(callable);
|
||||
|
||||
bool method_call = p0[1] != PY_NULL;
|
||||
int method_call = p0[1] != PY_NULL;
|
||||
|
||||
// handle boundmethod, do a patch
|
||||
if(callable_t == tp_bound_method){
|
||||
@ -872,11 +872,11 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||
callable_t = _tp(callable);
|
||||
p1[-(ARGC + 2)] = bm.func;
|
||||
p1[-(ARGC + 1)] = bm.self;
|
||||
method_call = true;
|
||||
method_call = 1;
|
||||
// [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);
|
||||
|
||||
PyObject** _base = args.begin();
|
||||
@ -884,11 +884,13 @@ PyObject* VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
||||
|
||||
if(callable_t == tp_function){
|
||||
/*****************_py_call*****************/
|
||||
// check stack overflow
|
||||
if(s_data.is_overflow()) StackOverflowError();
|
||||
|
||||
const Function& fn = PK_OBJ_GET(Function, callable);
|
||||
const CodeObject* co = fn.decl->code.get();
|
||||
int co_nlocals = co->varnames.size();
|
||||
|
||||
if(fn.decl->is_simple){
|
||||
if(args.size() != fn.decl->args.size()){
|
||||
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"));
|
||||
|
||||
// fast path for empty function
|
||||
if(fn.decl->is_empty){
|
||||
s_data.reset(p0);
|
||||
return None;
|
||||
}
|
||||
|
||||
// [callable, <self>, args..., local_vars...]
|
||||
// ^p0 ^p1 ^_sp
|
||||
s_data.reset(_base + co_nlocals);
|
||||
|
@ -140,3 +140,15 @@ try:
|
||||
exit(1)
|
||||
except TypeError:
|
||||
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