• 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.

14 replies to this topic

### #1RCL  Members

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 :-)

### #2doho  Members

Posted 02 September 2004 - 09:24 PM

Quote:
 Original post by RCLP.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 ;)

### #3zola  Members

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

### #4RCL  Members

Posted 03 September 2004 - 02:03 AM

Hi Tom,

Quote:
 Original post by zolaWhat 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 versionfloat 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 versionfunction func5()    n = n + zfx.average( n, n )endfunction func4()    n = n + 2 * zfx.average( n+1, n+2 )endfunction func3()    n = 2.1 * nendfunction func2()    n = n / 3.5endfunction 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        endendn = 0i = 0for i = 0, 1000000, 0.25 do    zfx.average( i, i + 1 )     recursion( 5 )    if n > 100 then n = 0 endendzfx.log( string.format( "N = %f", n ) )

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

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

### #5Andreas Jonsson  Moderators

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

### #6RCL  Members

Posted 03 September 2004 - 02:18 AM

Hi doho,

Quote:
Original post by doho
Quote:
 Original post by RCLP.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 :)

### #7RCL  Members

Posted 03 September 2004 - 02:45 AM

Hi WitchLord,

Quote:
 Original post by WitchLordIt'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 WitchLordWhat 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 WitchLordHaving 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 WitchLordInheritance 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

### #8abrken  Members

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.

### #9RCL  Members

Posted 03 September 2004 - 03:18 AM

Quote:
 Original post by abrkenReally, 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

### #10abrken  Members

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 ?

### #11RCL  Members

Posted 03 September 2004 - 09:34 PM

Quote:
 Original post by abrkenI 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 typesSomeFunc( (CParent &) c ); // fails to compile - expected expression value

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

### #12abrken  Members

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;

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 ?

### #13Andreas Jonsson  Moderators

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

### #14RCL  Members

Posted 05 September 2004 - 02:58 AM

Quote:
 Original post by WitchLordTo 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 WitchLordTo 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

### #15Andreas Jonsson  Moderators

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.