• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
RCL

AngelScript performance

14 posts in this topic

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

Share this post


Link to post
Share on other sites
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 ;)
0

Share this post


Link to post
Share on other sites
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
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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 :)
0

Share this post


Link to post
Share on other sites
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
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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
0

Share this post


Link to post
Share on other sites
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 ?
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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(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 ?
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0