Script: Split SPos out of the bytecode into an extra vector for speed

SPos is only needed to display debug messages. Not having that data in the
cache for normal execution speeds up script execution by ten percent on at
least one artificial benchmark.
Günther Brammer 2010-10-25 23:48:08 +02:00
parent 7f899cc1b0
commit d02c424dab
5 changed files with 108 additions and 112 deletions

View File

@ -169,8 +169,8 @@ void C4AulScript::Default()
// not compiled
State = ASS_NONE;
Script = NULL;
Code = CPos = NULL;
CodeSize = CodeBufSize = 0;
Code.clear();
CPos = NULL;
IncludesResolved = false;
// defaults

View File

@ -235,8 +235,7 @@ struct C4AulBCC
C4ValueArray * a;
C4AulFunc * f;
intptr_t X;
} Par; // extra info (long for use with amd64)
const char *SPos;
} Par; // extra info
};
// call context
@ -445,16 +444,54 @@ public:
void Unreg(); // remove from list
virtual bool Delete() { return true; } // allow deletion on pure class
StdCopyStrBuf ScriptName; // script name
C4Def *Def; // owning def file
C4ValueMapNames LocalNamed;
enum Strict { NONSTRICT = 0, STRICT1 = 1, STRICT2 = 2, MAXSTRICT=STRICT2 };
enum Strict Strict; // new or even newer syntax?
bool Temporary; // set for DirectExec-scripts; do not parse those
const char *GetScript() const { return Script.getData(); }
C4AulFunc *GetFuncRecursive(const char *pIdtf); // search function by identifier, including global funcs
C4AulScriptFunc *GetSFunc(const char *pIdtf, C4AulAccess AccNeeded, bool fFailSafe = false); // get local sfunc, check access, check '~'-safety
C4AulScriptFunc *GetSFunc(const char *pIdtf); // get local script function by name
C4AulScriptFunc *GetSFunc(int iIndex, const char *szPattern = NULL); // get local script function by index
C4AulScriptFunc *GetSFuncWarn(const char *pIdtf, C4AulAccess AccNeeded, const char *WarnStr); // get function; return NULL and warn if not existant
void AddFunc(const char *pIdtf, C4ScriptFnDef* Def); // add def def func to table
C4Value DirectExec(C4Object *pObj, const char *szScript, const char *szContext, bool fPassErrors = false, enum Strict Strict = MAXSTRICT, C4AulScriptContext* context = NULL); // directly parse uncompiled script (WARG! CYCLES!)
void ResetProfilerTimes(); // zero all profiler times of owned functions
void CollectProfilerTimes(class C4AulProfiler &rProfiler);
bool IsReady() { return State == ASS_PARSED; } // whether script calls may be done
// helper functions
void Warn(const char *pMsg, const char *pIdtf);
int GetLineOfCode(C4AulBCC * CPos);
friend class C4AulParseError;
friend class C4AulFunc;
friend class C4AulScriptFunc;
friend class C4AulScriptEngine;
friend class C4AulParseState;
// Translate a string using the script's lang table
std::string Translate(const std::string &text) const;
protected:
C4LangStringTable *stringTable;
C4AulFunc *Func0, *FuncL; // owned functions
C4AulScriptEngine *Engine; //owning engine
C4AulScript *Owner, *Prev, *Next, *Child0, *ChildL; // tree structure
StdStrBuf Script; // script
C4AulBCC *Code, *CPos; // compiled script (/pos)
std::vector<C4AulBCC> Code;
std::vector<const char *> PosForCode;
C4AulBCC *CPos; // compiled script (/pos)
C4AulScriptState State; // script state
int CodeSize; // current number of byte code chunks in Code
int CodeBufSize; // size of Code buffer
bool Preparsing; // set while preparse
bool Resolving; // set while include-resolving, to catch circular includes
@ -484,48 +521,8 @@ protected:
C4AulScript *FindFirstNonStrictScript(); // find first script that is not #strict
int GetCodePos() const { return CPos - Code; }
C4AulBCC *GetCodeByPos(int iPos) { return Code + iPos; }
public:
StdCopyStrBuf ScriptName; // script name
C4Def *Def; // owning def file
C4ValueMapNames LocalNamed;
enum Strict { NONSTRICT = 0, STRICT1 = 1, STRICT2 = 2, MAXSTRICT=STRICT2 };
enum Strict Strict; // new or even newer syntax?
bool Temporary; // set for DirectExec-scripts; do not parse those
const char *GetScript() const { return Script.getData(); }
C4AulFunc *GetFuncRecursive(const char *pIdtf); // search function by identifier, including global funcs
C4AulScriptFunc *GetSFunc(const char *pIdtf, C4AulAccess AccNeeded, bool fFailSafe = false); // get local sfunc, check access, check '~'-safety
C4AulScriptFunc *GetSFunc(const char *pIdtf); // get local script function by name
C4AulScriptFunc *GetSFunc(int iIndex, const char *szPattern = NULL); // get local script function by index
C4AulScriptFunc *GetSFuncWarn(const char *pIdtf, C4AulAccess AccNeeded, const char *WarnStr); // get function; return NULL and warn if not existant
void AddFunc(const char *pIdtf, C4ScriptFnDef* Def); // add def def func to table
public:
C4Value DirectExec(C4Object *pObj, const char *szScript, const char *szContext, bool fPassErrors = false, enum Strict Strict = MAXSTRICT, C4AulScriptContext* context = NULL); // directly parse uncompiled script (WARG! CYCLES!)
void ResetProfilerTimes(); // zero all profiler times of owned functions
void CollectProfilerTimes(class C4AulProfiler &rProfiler);
bool IsReady() { return State == ASS_PARSED; } // whether script calls may be done
// helper functions
void Warn(const char *pMsg, const char *pIdtf);
friend class C4AulParseError;
friend class C4AulFunc;
friend class C4AulScriptFunc;
friend class C4AulScriptEngine;
friend class C4AulParseState;
// Translate a string using the script's lang table
std::string Translate(const std::string &text) const;
protected:
C4LangStringTable *stringTable;
int GetCodePos() const { return CPos - &Code[0]; }
C4AulBCC *GetCodeByPos(int iPos) { return &Code[iPos]; }
};
// holds all C4AulScripts

View File

@ -223,13 +223,14 @@ void C4AulDebug::ProcessLine(const StdStrBuf &Line)
{
C4AulBCC* foundDebugChunk = NULL;
const char* scriptText = script->GetScript();
for (C4AulBCC* chunk = script->Code; chunk; chunk++)
for (C4AulBCC* chunk = &script->Code[0]; chunk; chunk++)
{
switch (chunk->bccType)
{
case AB_DEBUG:
int lineOfThisOne;
if ((lineOfThisOne = SGetLine(scriptText, chunk->SPos)) == line)
{
int lineOfThisOne = script->GetLineOfCode(chunk);
if (lineOfThisOne == line)
{
foundDebugChunk = chunk;
goto Done;
@ -237,7 +238,7 @@ void C4AulDebug::ProcessLine(const StdStrBuf &Line)
/*else {
DebugLogF("Debug chunk at %d", lineOfThisOne);
}*/
}
break;
case AB_EOF:
goto Done;
@ -472,8 +473,7 @@ void C4AulDebug::ObtainStackTrace(C4AulScriptContext* pCtx, C4AulBCC* pCPos)
StdStrBuf C4AulDebug::FormatCodePos(C4AulScriptContext *pCtx, C4AulBCC *pCPos)
{
// Get position in script
const char *szScript = pCtx->Func->pOrgScript->GetScript();
int iLine = SGetLine(szScript, pCPos->SPos);
int iLine = pCtx->Func->pOrgScript->GetLineOfCode(pCPos);
// Format
return FormatString("%s:%d", RelativePath(pCtx->Func->pOrgScript->ScriptName), iLine);
}

View File

@ -95,10 +95,10 @@ StdStrBuf C4AulScriptContext::ReturnDump(StdStrBuf Dump)
else if (Func->Owner->Def != NULL)
Dump.AppendFormat(" (def %s)", Func->Owner->Def->GetName());
// Script
if (!fDirectExec && Func->Owner)
if (!fDirectExec && Func->pOrgScript)
Dump.AppendFormat(" (%s:%d)",
Func->pOrgScript->ScriptName.getData(),
SGetLine(Func->pOrgScript->GetScript(), CPos ? CPos->SPos : Func->Script));
CPos ? Func->pOrgScript->GetLineOfCode(CPos) : SGetLine(Func->pOrgScript->GetScript(), Func->Script));
// Return it
return Dump;
}
@ -1178,7 +1178,7 @@ C4Value C4AulScript::DirectExec(C4Object *pObj, const char *szScript, const char
delete pScript;
return C4VNull;
}
pFunc->Code = pScript->Code;
pFunc->Code = &pScript->Code[0];
pScript->State = ASS_PARSED;
// Execute. The TemporaryScript-parameter makes sure the script will be deleted later on.
C4Value vRetVal(AulExec.Exec(pFunc, pObj, NULL, fPassErrors, true));

View File

@ -888,13 +888,13 @@ static const char * GetTTName(C4AulBCCType e)
case AB_CALLFS: return "CALLFS"; // failsafe direct call
case AB_STACK: return "STACK"; // push nulls / pop
case AB_INT: return "INT"; // constant: int
case AB_BOOL: return "bool"; // constant: bool
case AB_BOOL: return "BOOL"; // constant: bool
case AB_STRING: return "STRING"; // constant: string
case AB_CPROPLIST: return "CPROPLIST"; // constant: proplist
case AB_CARRAY: return "CARRAY"; // constant: array
case AB_NIL: return "NIL"; // constant: nil
case AB_DUP: return "DUP"; // constant: nil
case AB_ARRAY: return "ARRAY"; // semi-constant: array
case AB_DUP: return "DUP"; // constant: nil
case AB_PROPLIST: return "PROPLIST"; // semi-constant: array
case AB_IPROPLIST: return "IPROPLIST";
case AB_IVARN: return "IVARN"; // initialization of named var
@ -906,6 +906,7 @@ static const char * GetTTName(C4AulBCCType e)
case AB_FOREACH_NEXT: return "FOREACH_NEXT"; // foreach: next element
case AB_RETURN: return "RETURN"; // return statement
case AB_ERR: return "ERR"; // parse error at this position
case AB_DEBUG: return "DEBUG"; // debug break
case AB_EOFN: return "EOFN"; // end of function
case AB_EOF: return "EOF";
@ -915,59 +916,50 @@ static const char * GetTTName(C4AulBCCType e)
void C4AulScript::AddBCC(C4AulBCCType eType, intptr_t X, const char * SPos)
{
// range check
if (CodeSize >= CodeBufSize)
{
// create new buffer
CodeBufSize = CodeBufSize ? 2 * CodeBufSize : C4AUL_CodeBufSize;
C4AulBCC *nCode = new C4AulBCC [CodeBufSize];
// copy data
memcpy(nCode, Code, sizeof(*Code) * CodeSize );
// replace buffer
delete[] Code;
Code = nCode;
// adjust pointer
CPos = Code + CodeSize;
}
// store chunk
CPos->bccType = eType;
CPos->Par.X = X;
CPos->SPos = SPos;
C4AulBCC bcc;
bcc.bccType = eType;
bcc.Par.X = X;
Code.push_back(bcc);
PosForCode.push_back(SPos);
CPos = &Code.back();
switch (eType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_PROP:
/* case AB_LOCALN_SET/AB_PROP_SET: -- expected to already have a reference upon creation, see MakeSetter */
CPos->Par.s->IncRef();
break;
default:
// TODO
break;
default: break;
}
CPos++; CodeSize++;
CPos++;
}
void C4AulScript::RemoveLastBCC()
{
C4AulBCC *pBCC = Code + CodeSize - 1;
C4AulBCC *pBCC = &Code.back();
switch (pBCC->bccType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET:
pBCC->Par.s->DecRef();
break;
default: break; // I don't want to do anything, thank you very much. Stupid warnings.
default: break;
}
CodeSize--;
Code.pop_back();
PosForCode.pop_back();
CPos--;
}
void C4AulScript::ClearCode()
{
while(CodeSize > 0)
while(Code.size() > 0)
RemoveLastBCC();
delete[] Code;
Code = 0;
CodeSize = CodeBufSize = 0;
CPos = Code;
CPos = 0;
}
int C4AulScript::GetLineOfCode(C4AulBCC * bcc)
{
return SGetLine(GetScript(), PosForCode[bcc - &Code[0]]);
}
bool C4AulScript::Preparse()
@ -981,7 +973,7 @@ bool C4AulScript::Preparse()
however, this is just a few bytes per updated definition in developer mode, which
seems acceptable for me. The mem will be released when destroying the list */
Includes = NULL; Appends=NULL;
CPos = Code;
CPos = &Code[0];
while (Func0)
{
// belongs to this script?
@ -1125,7 +1117,7 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
}
// Join checks only if it's not a jump target
if (!fJump && a->CPos > a->Code)
if (!fJump && a->CPos > &a->Code[0])
{
// Join together stack operations
C4AulBCC *pCPos1 = a->CPos - 1;
@ -1381,7 +1373,7 @@ void C4AulScript::ParseFn(C4AulScriptFunc *Fn, bool fExprOnly, C4AulScriptContex
// store byte code pos
// (relative position to code start; code pointer may change while
// parsing)
Fn->Code = (C4AulBCC *) (CPos - Code);
Fn->Code = (C4AulBCC *) (CPos - &Code[0]);
// parse
C4AulParseState state(Fn, this, C4AulParseState::PARSER);
state.ContextToExecIn = context;
@ -3103,8 +3095,8 @@ bool C4AulScript::Parse()
{
C4ScriptHost * scripthost = 0;
if (Def) scripthost = &Def->Script;
if (scripthost) LogSilentF("parsing %s...\n", scripthost->GetFilePath());
else LogSilentF("parsing unknown...\n");
if (scripthost) fprintf(stderr, "parsing %s...\n", scripthost->GetFilePath());
else fprintf(stderr, "parsing unknown...\n");
}
// parse children
C4AulScript *s = Child0;
@ -3151,12 +3143,12 @@ bool C4AulScript::Parse()
delete err;
// make all jumps that don't have their destination yet jump here
// intptr_t to make it work on 64bit
for (intptr_t i = reinterpret_cast<intptr_t>(Fn->Code); i < CPos - Code; i++)
for (intptr_t i = reinterpret_cast<intptr_t>(Fn->Code); i < CPos - &Code[0]; i++)
{
C4AulBCC *pBCC = Code + i;
C4AulBCC *pBCC = &Code[i];
if (IsJump(pBCC->bccType))
if (!pBCC->Par.i)
pBCC->Par.i = CPos - Code - i;
pBCC->Par.i = CPos - &Code[0] - i;
}
// add an error chunk
AddBCC(AB_ERR);
@ -3181,7 +3173,7 @@ bool C4AulScript::Parse()
if (Fn) if (Fn->Owner != Engine) Fn=NULL;
}
if (Fn)
Fn->Code = Code + (intptr_t) Fn->Code;
Fn->Code = &Code[(intptr_t) Fn->Code];
}
// save line count
@ -3197,23 +3189,30 @@ bool C4AulScript::Parse()
if (f->LinkedTo) Fn = f->LinkedTo->SFunc();
if (Fn) if (Fn->Owner != Engine) Fn=NULL;
}
if (Fn)
if (!Fn)
continue;
fprintf(stderr, "%s:\n", Fn->Name);
for (C4AulBCC *pBCC = Fn->Code;; pBCC++)
{
LogSilentF("%s:", Fn->Name);
for (C4AulBCC *pBCC = Fn->Code;; pBCC++)
C4AulBCCType eType = pBCC->bccType;
fprintf(stderr, "\t%d\t%s", GetLineOfCode(pBCC), GetTTName(eType));
switch (eType)
{
C4AulBCCType eType = pBCC->bccType;
switch (eType)
{
case AB_FUNC:
LogSilentF("%s\t'%s'\n", GetTTName(eType), pBCC->Par.f->Name); break;
case AB_STRING: case AB_CALL: case AB_CALLFS:
LogSilentF("%s\t'%s'\n", GetTTName(eType), pBCC->Par.s->GetCStr()); break;
default:
LogSilentF("%s\t%ld\n", GetTTName(eType), static_cast<long>(pBCC->Par.X)); break;
}
if (eType == AB_EOFN) break;
case AB_FUNC:
fprintf(stderr, "\t%s\n", pBCC->Par.f->Name); break;
case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_PROP:
fprintf(stderr, "\t%s\n", pBCC->Par.s->GetCStr()); break;
case AB_STRING:
fprintf(stderr, "\t\"%s\"\n", pBCC->Par.s->GetCStr()); break;
case AB_DEBUG: case AB_NIL: case AB_RETURN:
case AB_PROPLIST: case AB_IPROPLIST: case AB_PAR:
case AB_ARRAYA: case AB_ARRAY_SLICE: case AB_ERR:
case AB_EOFN: case AB_EOF:
assert(!pBCC->Par.X); fprintf(stderr, "\n"); break;
default:
fprintf(stderr, "\t%ld\n", static_cast<long>(pBCC->Par.X)); break;
}
if (eType == AB_EOFN) break;
}
}