1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-01-31 18:07:14 +01:00

Improve performance in overloaded methods calls by cheating a little and calling the overload directly instead of pushing the parameters back on the stack and performing a regular function call.

This commit is contained in:
Sandu Liviu Catalin 2018-10-26 21:56:06 +03:00
parent 5c859fb5aa
commit 086eeae7b4
2 changed files with 83 additions and 74 deletions

View File

@ -556,24 +556,35 @@ protected:
// Bind a function and it's associated Squirrel closure to the object // Bind a function and it's associated Squirrel closure to the object
inline void BindOverload(const SQChar* name, void* method, size_t methodSize, SQFUNCTION func, SQFUNCTION overload, int argCount, bool staticVar = false) { inline void BindOverload(const SQChar* name, void* method, size_t methodSize, SQFUNCTION func, SQFUNCTION overload, int argCount, bool staticVar = false) {
string overloadName = SqOverloadName::Get(name, argCount); string overloadName;
overloadName.reserve(15);
SqOverloadName::Get(name, argCount, overloadName);
sq_pushobject(vm, GetObject()); sq_pushobject(vm, GetObject());
// Bind overload handler // Bind overload handler
sq_pushstring(vm, name, -1); sq_pushstring(vm, name, -1);
sq_pushstring(vm, name, -1); // function name is passed as a free variable // function name is passed as a free variable
//sq_pushstring(vm, name, -1);
sq_push(vm, -1); // <- Let's cheat(?) by pushing the same object
sq_newclosure(vm, overload, 1); sq_newclosure(vm, overload, 1);
// Set the closure name (for debug purposes)
sq_setnativeclosurename(vm, -1, name);
// Include it into the object
sq_newslot(vm, -3, staticVar); sq_newslot(vm, -3, staticVar);
// Bind overloaded function // Bind overloaded function
sq_pushstring(vm, overloadName.c_str(), -1); sq_pushstring(vm, overloadName.c_str(), static_cast<SQInteger>(overloadName.size()));
// Push the native closure pointer as a free variable
SQUserPointer methodPtr = sq_newuserdata(vm, static_cast<SQUnsignedInteger>(methodSize)); SQUserPointer methodPtr = sq_newuserdata(vm, static_cast<SQUnsignedInteger>(methodSize));
memcpy(methodPtr, method, methodSize); memcpy(methodPtr, method, methodSize);
sq_newclosure(vm, func, 1); sq_newclosure(vm, func, 1);
// Set the closure name (for debug purposes)
sq_setnativeclosurename(vm, -1, overloadName.c_str());
// Include it into the object
sq_newslot(vm, -3, staticVar); sq_newslot(vm, -3, staticVar);
sq_pop(vm,1); // pop table sq_pop(vm,1); // pop object
} }
// Set the value of a variable on the object. Changes to values set this way are not reciprocated // Set the value of a variable on the object. Changes to values set this way are not reciprocated

View File

@ -51,15 +51,77 @@ namespace Sqrat {
class SqOverloadName { class SqOverloadName {
public: public:
static string Get(const SQChar* name, int args) { static void Get(const SQChar* name, int args, string & out) {
std::basic_stringstream<SQChar> overloadName; SQChar buf[16] = {'_', 'o'};
overloadName << _SC("__overload_") << name << args; itoa(args, &buf[2], 10);
out.append(buf);
out.push_back('_');
out.append(name);
}
return overloadName.str(); static string Get(const SQChar* name, int args) {
string out;
out.reserve(15);
Get(name, args, out);
return out;
} }
}; };
//
// Don't include the overloaded call forwarder into the templated class
// to avoid duplicating code that doesn't need to be specialized.
//
inline SQInteger OverloadExecutionForwarder(HSQUIRRELVM vm) {
// Get the argument count
const int argCount = static_cast<int>(sq_gettop(vm) - 2);
// Subtract environment and base name in free variable^
const SQChar* funcName;
SQInteger funcNameSize;
// Get the un-mangled function name (free variable)
sq_getstringandsize(vm, -1, &funcName, &funcNameSize);
// Generate the overload mangled name
string overloadName;
overloadName.reserve(funcNameSize+5);
SqOverloadName::Get(funcName, argCount, overloadName);
// Pop the un-mangled closure name from the stack so we can replace it later
sq_poptop(vm); // `funcName` becomes invalid after this
// Push the overload mangled name on the stack
sq_pushstring(vm, overloadName.c_str(), static_cast<SQInteger>(overloadName.size()));
// Lookup the proper overload and get it on the stack
#if !defined (SCRAT_NO_ERROR_CHECKING)
if (SQ_FAILED(sq_get(vm, 1))) {
sq_pushnull(vm); // Push something to take the place of the free variable
return sq_throwerror(vm, _SC("wrong number of parameters"));
}
#else
sq_get(vm, 1);
#endif
SQFUNCTION f = nullptr;
// Get the native closure pointer that we must invoke
SQRESULT res = sq_getnativeclosurepointer(vm, -1, &f);
if (SQ_FAILED(res)) return res;
// Make sure a native closure pointer is available
if (!f) {
return sq_throwerror(vm, _SC("unable to acquire the proper overload closure"));
}
// Attempt to get the free variable containing the native closure pointer on the stack
const SQChar *name = sq_getonefreevariable(vm, 0);
// This is simply a hack to implement a direct call and gain some performance
// Since both closures expect a free variable we simply replace the free variable
// containing closure name with the free variable containing the closure pointer
// Perform a direct call and store the result
res = f(vm);
// If there was a free variable and the closure on the stack was native
// Then we must push something to take the place of the free variable
if (name && name[0] == '@') {
sq_pushnull(vm);
}
// Return the result back to the caller
return res;
}
// //
// Squirrel Overload Functions // Squirrel Overload Functions
// //
@ -69,39 +131,7 @@ class SqOverload {
public: public:
static SQInteger Func(HSQUIRRELVM vm) { static SQInteger Func(HSQUIRRELVM vm) {
// Get the arg count return OverloadExecutionForwarder(vm);
int argCount = sq_gettop(vm) - 2;
const SQChar* funcName;
sq_getstring(vm, -1, &funcName); // get the function name (free variable)
string overloadName = SqOverloadName::Get(funcName, argCount);
sq_pushstring(vm, overloadName.c_str(), static_cast<SQInteger>(overloadName.size()));
#if !defined (SCRAT_NO_ERROR_CHECKING)
if (SQ_FAILED(sq_get(vm, 1))) { // Lookup the proper overload
return sq_throwerror(vm, _SC("wrong number of parameters"));
}
#else
sq_get(vm, 1);
#endif
// Push the args again
for (int i = 1; i <= argCount + 1; ++i) {
sq_push(vm, i);
}
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQRESULT result = sq_call(vm, argCount + 1, true, ErrorHandling::IsEnabled());
if (SQ_FAILED(result)) {
return sq_throwerror(vm, LastErrorString(vm).c_str());
}
#else
sq_call(vm, argCount + 1, true, ErrorHandling::IsEnabled());
#endif
return 1;
} }
}; };
@ -115,39 +145,7 @@ class SqOverload<void> {
public: public:
static SQInteger Func(HSQUIRRELVM vm) { static SQInteger Func(HSQUIRRELVM vm) {
// Get the arg count return OverloadExecutionForwarder(vm);
int argCount = sq_gettop(vm) - 2;
const SQChar* funcName;
sq_getstring(vm, -1, &funcName); // get the function name (free variable)
string overloadName = SqOverloadName::Get(funcName, argCount);
sq_pushstring(vm, overloadName.c_str(), -1);
#if !defined (SCRAT_NO_ERROR_CHECKING)
if (SQ_FAILED(sq_get(vm, 1))) { // Lookup the proper overload
return sq_throwerror(vm, _SC("wrong number of parameters"));
}
#else
sq_get(vm, 1);
#endif
// Push the args again
for (int i = 1; i <= argCount + 1; ++i) {
sq_push(vm, i);
}
#if !defined (SCRAT_NO_ERROR_CHECKING)
SQRESULT result = sq_call(vm, argCount + 1, false, ErrorHandling::IsEnabled());
if (SQ_FAILED(result)) {
return sq_throwerror(vm, LastErrorString(vm).c_str());
}
#else
sq_call(vm, argCount + 1, false, ErrorHandling::IsEnabled());
#endif
return 0;
} }
}; };