Jump to content

  • Log In with Google      Sign In   
  • Create Account

- - - - -

AngelScript performance


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
14 replies to this topic

#1 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 02 September 2004 - 08:39 PM

Hi I've tested AngelScript performance against Lua. My synthetic test consists of pretty long loop (to make differences noticeable) with calls to various functions (both script ones and exported from C++), and some (rather simple) math calculations. This is what we need the in-game script for - mostly for calling exported functions, all the time-consuming tasks are done from within C++ code. And sorry to say, AngelScript is nearly 2.35 times slower than Lua. I really like AngelScript (because of its syntax and comfort at exporting functions - I hate Luabind and ToLua :)), so I decided to investigate the problem. The most time-consuming (according to profiler) function is asCContext::ExecuteNext() (no surprise here). Changing 'bcSize[ BC_* ]' to enumerated constants ('Sizeof_BC_*') when incrementing programCounter, and using temporary variables instead of class members helped me to get rid of some dereferencing and made the function more compact in memory. However, the performance gain was insufficient (AngelScript remained 2.10 times slower than Lua). I would say that BC_CALL opcode is not a 'light-weight' one, making code with a lot of function calls slow. Perhaps some further 'low-level' optimization (like making opcodes to be ints, not bytes, because ints ARE faster) could help (a bit), but I don't think that problem can be solved by this. I believe that the main reason why AngelScript is slower is that Lua bytecode consists of less opcodes, which are of higher level than those of AngelScript (for example, Lua has special opcodes for 'FOR' iterations, OP_FORLOOP/OP_TFORLOOP). It is a hard decision to choose between Lua and AngelScript. Performance IS important for games, so it would be really great if you managed to optimize AngelScript to make it at least on par with Lua. P.S. And yes, it would be also great if you supported [at least single :)] inheritance when registering object types, too :-)

Sponsor:

#2 doho   Members   -  Reputation: 378

Like
0Likes
Like

Posted 02 September 2004 - 09:24 PM

Quote:
Original post by RCL
P.S. And yes, it would be also great if you supported [at least single :)] inheritance when registering object types, too :-)
I have a feeling that this is easier said than done ;)

#3 zola   Members   -  Reputation: 122

Like
0Likes
Like

Posted 02 September 2004 - 09:43 PM

Hi RCL

This is very interesting.

What does Your angelscript code look like?
Did you also stresstest object creation and destruction?
Could we download Your test setup somewhere, or could you post here some of Your functions so we have a more accurate picture?

Regards
Tom


#4 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 03 September 2004 - 02:03 AM

Hi Tom,

Quote:
Original post by zola
What does Your angelscript code look like?
Did you also stresstest object creation and destruction?
Could we download Your test setup somewhere, or could you post here some of Your functions so we have a more accurate picture?


No, I did not test object creation and destruction.
Here's my code (yes, I know it is meaningless, but the main point here was to test a) performance of function calls b) performance of math)

Angelscript version
===== 8< =====

// Synthetic test, AngelScript version

float N;

void func5()
{
N += Average( N, N );
}

void func4()
{
N += 2 * Average( N + 1, N + 2 );
}

void func3()
{
N *= 2.1 * N;
}

void func2()
{
N /= 3.5;
}

void Recursion( int nRec )
{
if ( nRec >= 1 )
Recursion( nRec - 1 );

if ( nRec == 5 )
func5();
else if ( nRec == 4 )
func4();
else if ( nRec == 3 )
func3();
else if ( nRec == 2 )
func2();
else
N *= 1.5;
}

int main()
{
N = 0;
float i = 0;

for ( i = 0; i < 1000000; i += 0.25 )
{
Average( i, i );
Recursion( 5 );

if ( N > 100 ) N = 0;
}

Log( "N = "+bstrFormat( N ) );
return 0;
}


===== >8 =====

Lua version
===== 8< =====

-- synthetic test, LUA version

function func5()
n = n + zfx.average( n, n )
end

function func4()
n = n + 2 * zfx.average( n+1, n+2 )
end

function func3()
n = 2.1 * n
end

function func2()
n = n / 3.5
end

function recursion( rec )
if rec >= 1 then
recursion( rec - 1 )
end

if rec == 5 then func5()
else if rec==4 then func4()
else if rec==3 then func3()
else if rec==2 then func2()
else n = n * 1.5
end
end
end
end
end

n = 0
i = 0

for i = 0, 1000000, 0.25 do
zfx.average( i, i + 1 )
recursion( 5 )
if n > 100 then n = 0 end
end

zfx.log( string.format( "N = %f", n ) )

===== >8 =====

both Average() and average() are simple functions returning (arg1 + arg2) / 2.


#5 Andreas Jonsson   Moderators   -  Reputation: 3444

Like
0Likes
Like

Posted 03 September 2004 - 02:14 AM

Very interesting indeed. I didn't make AngelScript to compete in performance with Lua, or any other scripting language. But I am surprised to see that AS is over 2 times slower than Lua. I would have thought the typeless variables of Lua would give it a rather large overhead.

It's also interesting to see that changing bcSize[] to enumerated constants improved the performance so much. What variables are you using as local variables instead of member variables?

Moving the loop into ExecuteNext() ought to raise the performance a little as well, also removing the test (status == tsActive) from every loop iteration could increase the performance sligthly.

Having a specific bytecode for loop iterations might be possible, I'll investigate this. That would save a few bytecodes for each loop.

I think I'll write another test framework for testing the performance of AngelScript so that I can measure the difference with each release.

Inheritance for object types will probably not happen, because I can't guarantee that the method pointers are the same for the base class and the derived one. But I'll investigate this further. Maybe I'll find a solution for a future version.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#6 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 03 September 2004 - 02:18 AM

Hi doho,

Quote:
Original post by doho
Quote:
Original post by RCL
P.S. And yes, it would be also great if you supported [at least single :)] inheritance when registering object types, too :-)
I have a feeling that this is easier said than done ;)


Everything is :)


#7 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 03 September 2004 - 02:45 AM

Hi WitchLord,

Quote:
Original post by WitchLord
It's also interesting to see that changing bcSize[] to enumerated constants improved the performance so much.


Well,
1) we got rid from reading from the memory
2) code got tighter
( move reg1, [mem] \ add reg2, reg1 replaced by just add reg2, const )

Could be that my system is bound by memory speed =)

Quote:
Original post by WitchLord
What variables are you using as local variables instead of member variables?


I reorganized your switch...case in ExecuteNext(), replacing programCounter with local variable. It did improve the performance (a bit :)), however, when I tried to do the same trick with stackPointer, I got performance loss.

Quote:
Original post by WitchLord
Having a specific bytecode for loop iterations might be possible, I'll investigate this. That would save a few bytecodes for each loop.


Maybe you could also store more pre-computed data for each BC_CALL, or make several versions of BC_CALL opcode to avoid redundant 'if's and function calls every time.

Quote:
Original post by WitchLord
Inheritance for object types will probably not happen, because I can't guarantee that the method pointers are the same for the base class and the derived one. But I'll investigate this further. Maybe I'll find a solution for a future version.


It's a pity. Having entity hierarchy represented in the script by not related to each other types sux (problems with type casting etc etc).

Cheers,
RCL

#8 abrken   Members   -  Reputation: 130

Like
0Likes
Like

Posted 03 September 2004 - 03:01 AM

Really, I beleive that object inheritance could be made outside the code of AngelScript.

First you have to create an asIScriptEngine descendant.

Next, each time you register an object class, you can (i'have already made it for other purpose, see my post about AngelScript Editors) register for your convenience (inheritance for you !).

At the end of the process of registering the whole objects, you then have a tree with all objects and their members, and methods (yes, the same that angelscript already has).

Then the beauty comes ... you just have to develop a function to build the object hierarchy :
bool RegisterObjectHierarchy(char *TypeParent, char *TypeChild)
{
for each member of TypeParent
if TypeChild hasn't got the same member
RegisterObjectProperty(TypeChild, definition of TypeParent member, adresse of TypeParent member)

for each method of TypeParent
if TypeChild hasn't got the same member
RegisterObjectMethod(TypeChild, definition of TypeParent method, adress of TypeParent method, call convention of TypeParent method)

return evrything went ok.
}

Once the inheritance is made, free all of your datas tree !

AbrKen.

#9 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 03 September 2004 - 03:18 AM

Quote:
Original post by abrken
Really, I beleive that object inheritance could be made outside the code of AngelScript.


How do I *safely* cast my, say, CActor reference to CEntity (CActor's superclass) reference this way?

Cheers,
RCL

#10 abrken   Members   -  Reputation: 130

Like
0Likes
Like

Posted 03 September 2004 - 03:44 AM

I don't know what you're meaning when your saying *safely*

This work for me :

in_pAsEngine->RegisterObjectType("CXmlValue", sizeof(CXmlValue), asOBJ_CLASS);
in_pAsEngine->RegisterObjectBehaviour("CXmlValue", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CXmlValue_constructor), asCALL_CDECL_OBJLAST);
in_pAsEngine->RegisterObjectBehaviour("CXmlValue", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CXmlValue_destructor), asCALL_CDECL_OBJLAST);
in_pAsEngine->RegisterGlobalFunction("CXmlValue *CXmlValue_new()", asFUNCTION(CXmlValue_new), asCALL_CDECL);
in_pAsEngine->RegisterGlobalFunction("void CXmlValue_delete(CXmlValue *)", asFUNCTION(CXmlValue_delete), asCALL_CDECL);
in_pAsEngine->RegisterObjectProperty("CXmlValue", "string m_strName", offsetof(CXmlValue, m_strName));
in_pAsEngine->RegisterObjectProperty("CXmlValue", "string m_strValue", offsetof(CXmlValue, m_strValue));
in_pAsEngine->RegisterObjectMethod("CXmlValue", "const string &getName()",asMETHOD(CXmlValue,getName), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlValue", "const bool getValueAsBool()",asMETHOD(CXmlValue,getValueAsBool), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlValue", "const bits32 getValueAsDWORD()",asMETHOD(CXmlValue,getValueAsDWORD), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlValue", "const string &getValueAsString()",asMETHOD(CXmlValue,getValueAsString), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlValue", "const CString getValueAsCString()",asMETHOD(CXmlValue,getValueAsCString), asCALL_THISCALL);

in_pAsEngine->RegisterObjectType("CXmlParameter", sizeof(CXmlParameter), asOBJ_CLASS);
in_pAsEngine->RegisterObjectBehaviour("CXmlParameter", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CXmlParameter_constructor), asCALL_CDECL_OBJLAST);
in_pAsEngine->RegisterObjectBehaviour("CXmlParameter", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CXmlParameter_destructor), asCALL_CDECL_OBJLAST);
in_pAsEngine->RegisterGlobalFunction("CXmlParameter *CXmlParameter_new()", asFUNCTION(CXmlParameter_new), asCALL_CDECL);
in_pAsEngine->RegisterGlobalFunction("void CXmlParameter_delete(CXmlParameter *)", asFUNCTION(CXmlParameter_delete), asCALL_CDECL);
in_pAsEngine->RegisterObjectProperty("CXmlParameter", "string m_strName", offsetof(CXmlValue, m_strName));
in_pAsEngine->RegisterObjectProperty("CXmlParameter", "string m_strValue", offsetof(CXmlValue, m_strValue));
in_pAsEngine->RegisterObjectMethod("CXmlParameter", "const string &getName()",asMETHOD(CXmlValue,getName), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlParameter", "const bool getValueAsBool()",asMETHOD(CXmlValue,getValueAsBool), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlParameter", "const bits32 getValueAsDWORD()",asMETHOD(CXmlValue,getValueAsDWORD), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlParameter", "const string &getValueAsString()",asMETHOD(CXmlValue,getValueAsString), asCALL_THISCALL);
in_pAsEngine->RegisterObjectMethod("CXmlParameter", "const CString getValueAsCString()",asMETHOD(CXmlValue,getValueAsCString), asCALL_THISCALL);


and this is the class definition :


/*!
*****************************************************************************************
* \class CXmlValue : public CObject XmlMemoryTree.h
* \brief Description
*
* detaille
*****************************************************************************************
* \exception Aucune
*
*****************************************************************************************
* \bug Aucun de connu au 01/10/01
*****************************************************************************************/
class CXmlValue : public CObject {
public:
//! Nom de la Valeur
string m_strName,
//! Contenu de la valeur
m_strValue;

CXmlValue();
CXmlValue(const CXmlValue &);
virtual ~CXmlValue();

const string &getName()const{ return m_strName ; };

const bool getValueAsBool()const;
const DWORD getValueAsDWORD()const;
const string &getValueAsString()const;
const CString getValueAsCString()const;
const CTime getValueAsDate(LPCTSTR in_szDateFormat, bool &out_bGoodFmt) const;
const CTime getValueAsTime(LPCTSTR in_szTimeFormat, bool &out_bGoodFmt) const;

void Serialize(CArchive &ar);

CXmlValue &operator =(const CXmlValue&);
bool operator ==(const CXmlValue&in_xmlValue) const {return (m_strName == in_xmlValue.m_strName && m_strValue == in_xmlValue.m_strValue); };
bool operator !=(const CXmlValue&in_xmlValue) const {return (m_strName != in_xmlValue.m_strName || m_strValue != in_xmlValue.m_strValue); };

DECLARE_SERIAL(CXmlValue);
private:
string Uppercase(const string & in_str)const;
};



/*!
*****************************************************************************************
* \class CXmlParameter : public CXmlValue XmlMemoryTree.h
* \brief Description
*
* detaille
*****************************************************************************************
* \exception Aucune
*
*****************************************************************************************
* \bug Aucun de connu au 01/10/01
*****************************************************************************************/
class CXmlParameter : public CXmlValue
{
public:

CXmlParameter();
//CXmlParameter(const CXmlValue &);
//CXmlParameter(const CXmlParameter &);
virtual ~CXmlParameter();

void Serialize(CArchive &ar);

//CXmlParameter &operator =(const CXmlValue&);
//CXmlParameter &operator =(const CXmlParameter&);

DECLARE_SERIAL(CXmlParameter);
};



CXmlParameter is a CXmlValue descandant and it works fine.
I'm working with MSVC6 so peharps it's because of that ?


#11 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 03 September 2004 - 09:34 PM

Quote:
Original post by abrken
I don't know what you're meaning when your saying *safely*


I meant situation like this:

Say, I have two (C++) classes, CChild and CParent, CChild : public CParent, which are exported to Angel Script (as unrelated types, alas).

Suppose I have an instance of CChild (in AngelScript) and I want to call a function which takes CParent &. How do I cast CChild to CParent?

To illustrate this:


void SomeFunc( CParent & p );

...

CChild c;
SomeFunc( c ); // fails to compile - unrelated types
SomeFunc( (CParent &) c ); // fails to compile - expected expression value


Right now it seems like no such typecast is possible in AngelScript.


#12 abrken   Members   -  Reputation: 130

Like
0Likes
Like

Posted 04 September 2004 - 02:18 AM

Ok, I see now what your meaning by *safely* cast.

In such situation you've got two choices :
1/ Wait for AngelScript to have inheritance.
2/ Provide some extra code with the algo I have explained and change a little your script function.

bool RegisterObjectHierarchy(char *TypeParent, char *TypeChild)

should be changed to

bool RegisterObjectHierarchy(char *TypeParent, char *TypeChild, asUPtr funcPointerToCast)


funcPointerToCast is a C++ function that could be like this

CParent *castCChildToCCparent(CChild *p)
{
if p is kind of CParent
return (CParent*)p;
else
return NULL;
}



in your RegisterObjectHierarchy you can then RegisterGlobalFunction with a preformated name ("%s_cast_%s", TypeChild, TypeParent) that point to the C++ cast function.

Next in your're script :
void SomeFunc( CParent *p );

CChild *c;

SomeFunc(CChild_casst_CParent©);

This should work !

Of course, your're speaking about non pointer vars, so you can insert function dedicated to give a pointer in RegisterObjectHierarchy.

Does it sound good for you ?

#13 Andreas Jonsson   Moderators   -  Reputation: 3444

Like
0Likes
Like

Posted 04 September 2004 - 07:22 AM

To add on what abrken said.

AngelScript doesn't have inheritance yet, which of course you already know. It also doesn't support any kind of typecasting for registered objects or pointers.

In your situation I would register a function that takes a pointer to CChild, and returns a pointer to CParent. Note that if the CChild has multiple inheritance then the actual value of the pointers are not guaranteed to be the same.

Object inheritance, and classes declared in the script is something that I'm delaying for a future version. Perhaps 2.x. There are many other things that I want to implement first, like arrays, exception handling, data structures, etc.

To go back to the subject of performance in AngelScript.

I've done some experimenting and believe I have figured out a way to improve performance with at least 30%. Still doesn't make it as fast as Lua, but at least it will be closer. I'll put some effort in making these performance improvements already for version 1.9.1.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

#14 RCL   Members   -  Reputation: 122

Like
0Likes
Like

Posted 05 September 2004 - 02:58 AM

Quote:
Original post by WitchLord
To add on what abrken said.

AngelScript doesn't have inheritance yet, which of course you already know. It also doesn't support any kind of typecasting for registered objects or pointers.

In your situation I would register a function that takes a pointer to CChild, and returns a pointer to CParent. Note that if the CChild has multiple inheritance then the actual value of the pointers are not guaranteed to be the same.

Object inheritance, and classes declared in the script is something that I'm delaying for a future version. Perhaps 2.x. There are many other things that I want to implement first, like arrays, exception handling, data structures, etc.


Well, I already considered using such functions, but it seemed like inconvenient and somewhat slow (additional calls) solution to me.

However, bigger issue here is inconvenience. I want to automate binding to AS by wrapping Register*() calls with macros (I know about AngelScriptBind, but I dislike the idea of maintaining separate *.pkg files), and things get more complicated with 'casting' functions. Nevertheless, I admit those can solve the problem, although in somewhat clumsy way.

By the way, in my humble opinion, arrays won't be as useful as class hierarchy would (especially considering the fact that arrays can already be simulated by operator[], which is sufficient in most cases) :)

Quote:
Original post by WitchLord
To go back to the subject of performance in AngelScript.

I've done some experimenting and believe I have figured out a way to improve performance with at least 30%. Still doesn't make it as fast as Lua, but at least it will be closer. I'll put some effort in making these performance improvements already for version 1.9.1.


That's a good news. I believe that AngelScript should actually work *faster* than Lua, for it is not dynamically-typed, has no garbage collection, does not need wrappers for calling C++ funcs etc etc :-)

Cheers,
RCL

#15 Andreas Jonsson   Moderators   -  Reputation: 3444

Like
0Likes
Like

Posted 05 September 2004 - 03:13 AM

All I can say is that I will investigate inheritance for a future version, but I can't guarantee anything. I recognize the need for automatic binding and I can assure you that I will do what's possible to support it.

I agree with you that AngelScript should theoretically be faster than Lua, but it seems that I'm far from there. I'll have to take a look at the source for Lua to see what they do to make it so fast. Maybe then I can get some further ideas for optimizations. But most likely it is that they are using specialized bytecodes to join many instructions in one.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS