[Tutorial] New Code Optimizations
#1

New Code Optimizations
Total Optimization Tricks:13 | Update:21st June 2016

I will be sharing few ideas using which you can optimize your code. There have been many such topics in the past but this topic has some new ideas.

Related Threads: Currently this topic has the following tricks:
  • Arrays are slower than normal variables
  • Do not use CallLocalFunction & funcidx when you know the function name in advance
  • Natives are lot faster than PAWN Code
  • Conditions in loops
  • Assigning multiple variables to the same value
  • Delay declaring local variables
  • Simplifying & Rephrasing Math to avoid expensive operations
  • memcpy,strfind,etc work on arrays too
  • Is using CallRemoteFunction really worth?
  • Accessing array elements multiple times
  • Do not mix floats and integers in an expression by Mauzen
  • Using Streamer unnecessarily
  • Good & Bad Use of functions (Optimizing 2D Array manipulation code)
Some of these make a significant improvement whereas some do not. You can ignore some minor optimizations and give priority to writing readable code.

1. Arrays are slower than normal variables

The following code is inefficient:
Код:
new Float:pos[3];
GetPlayerPos(playerid, pos[0], pos[1], pos[2]);
And here is the assembly version of that above code:
Код:
zero.pri
addr.alt fffffff4
fill c ;These 3 instructions are responsible for zeroing all the array elements
break	; 38

addr.pri fffffff4 ;Get the address of the array
add.c 8 ;Add the index (index 2 means 2*4 bytes ahead)
load.i ;This will get the value stored at that address
push.pri ;Now push the argument

addr.pri fffffff4 ;Same as above
add.c 4
load.i
push.pri
addr.pri fffffff4 ;Same as above
load.i
push.pri
Now here is an equivalent code written more efficiently:
Код:
new Float:x, Float:y, Float:z;
GetPlayerPos(playerid, x, y , z);
And here is the assembly version:
Код:
push.c 0 //Making room for the variables on the stack
push.c 0
push.c 0
push.adr fffffff4 //Pushing the arguments
push.adr fffffff8
push.adr fffffffc
When you want to access an array element, the compiler uses the following algorithm:

Address of first element + 4*Index = the location where Array[Index] is stored (This formula is true only for 1-dimensional arrays)

After computing the address of the array element, the data stored in the element can be retrieved.

This doesn't mean that you must not use arrays. You must instead use arrays wisely. Do not make arrays for no reason when the same thing can be simply done using normal variables.

In my opinion using x, y, z is actually more readable than using an array pos[3].

Speed Tests:
Array (10 Assignments):2444,2448,2473
Non-Array (10 Assignments):972,975,963

Speed Test Code:http://pastebin.com/aMkNtaC2

Non-Array version is 2.5 times faster than the array version.

2. Do not use CallLocalFunction & funcidx when you know the function name in advance

Do you know that CallLocalFunction and funcidx are slow functions? They are very slow because they need to check the function name which you pass as an argument in a list of all publics. That means lot of internal strcmps.

Код:
if(funcidx("OnPlayerEatBanana") == -1)
You actually don't need that line.You can do the same with "0" instructions. If you know the function name already, then simply use the pre-processor directives to find out if the function exists.

Код:
#if defined OnPlayerEatBanana
//OnPlayerEatBanana has been declared
#endif
Код:
if(CallLocalFunction("OnPlayerEatBanana","ii",playerid,bananaid"))
you can
Код:
#if defined OnPlayerEatBanana
if(OnPlayerEatBanana(playerid,bananaid))
#else
//That function hasn't been declared
#endif
Speed Tests: (1 public taking zero arguments)
Calling directly:204,226,218
CallLocalFunction:1112,1097,1001


Please note that this is the best case for CallLocalFunction. In reality CallLocalFunction will be much slower since there will be many public functions.

3. Natives are lot faster than PAWN Code
Avoid creating your functions when there is a native which can do it (or maybe using a combination of natives).

The reason why natives are way lot faster is because the native functions are directly executed by your computer whereas all your PAWN Code is executed in a virtual computer. For every PAWN instruction the AMX Machine (the virtual computer) has to decode the instruction then get the operands and then execute the instruction. Decoding and fetching the operands takes some CPU.

Код:
stock strcpy(dest[], src[], sz=sizeof(dest))
{
  dest[0] = 0;
  return strcat(dest,src,sz); //Notice that I have used strcat instead of writing my own loops
}
Speed Tests:
Loop based strcpy vs native strcat

Here are two equivalent functions which function as strcpy.
http://pastebin.com/Y7RJ21tw

Native:697,700,718,705
Non-Native:5484,5422,5507,5562

4. Conditions in loops

I don't know how many times I have told people about this but still there are few who still don't do this simple optimization.

Code 1:
Код:
for(new i = 0;i <= GetPlayerPoolSize();i++) {}
Code 2:
Код:
for(new i = 0,j = GetPlayerPoolSize();i <= j;i++) {}
In the first code, GetPlayerPoolSize is called for every iteration. GetPlayerPoolSize in our timeframe, returns a constant value every time it is called. So why simply call GetPlayerPoolSize on every iteration?

That's what the second code avoids. It makes a local variable which stores the value returned by GetPlayerPoolSize and uses it in the condition. Therefore calling the function just once and avoiding the function overhead.

Speed Tests:
With Optimization:1102,1080,1069,1091
Without Optimization:2374,2359,2429,2364

Test Code:http://pastebin.com/SLZDGRG4

Though the improvement in the above case may be negligible compared to the code which you put inside the loop, there will be times when you will be using a more slower functions.

Код:
for(new i = 0; i < CallRemoteFunction("GetPlayersInTeam", "i", TEAM_ID); i++) 
{
  
}
5. Assigning multiple variables to the same value & using memset

Code 1:
Код:
x = abc;
y = abc;
z = abc;
Code 2:
Код:
x = 
y = 
z = abc;
Which code do you think is faster?

Code 1:
Код:
load.pri c ;Get abc
stor.pri 8 ;Store it in X
break	; 20
load.pri c ;Get abc
stor.pri 4 ;Store it in Y
break	; 34
load.pri c ;Get abc
stor.pri 0 ;Store it in Z
Code 2:
Код:
load.pri c ;Get abc
stor.pri 0 ;Store in X
stor.pri 4 ;Store in Y
stor.pri 8 ;Store in Z
See the difference? The first code has extra useless instructions which fetches abc again and again when its already there whereas the second version gets abc only once and sets x,y,z.

The obvious conclusion is that Code 2 is faster but this is probably insignificant.

When you have large arrays that have to be set to zeros or ones or any other value use memset.

Speed Tests:
Using memset to set all elements of an array (3D) of 100 elements to zero:363,367,372
Setting elements of an array (3D) of 100 elements to zero using for loop:6662,6642,6687

6. Delay declaring local variables
I have seen scripts where all local variables are put at the top of the function though there are some which are needed sometimes.The examples should make things clear.

Bad Code:
Код:
public OnPlayerDoSomething(playerid)
{
  new actionid = GetPlayerAction(playerid), pee_id, peed_on_whome, amount_of_pee;
  if(actionid == PLAYER_PEE)
  {

  }
}
Good Code:
Код:
public OnPlayerDoSomething(playerid)
{
  new actionid = GetPlayerAction(playerid);
  if(actionid == PLAYER_PEE)
  {
  new pee_id,peed_on_whome,amount_of_pee;
  }
}
If you had gone through my previous tips, you must be knowing by now that when you create a local variable, the compiler first creates some room for it in the stack and then initializes it with zero.

So you must not simply make local variables if you are not sure if you are going to use them.The second code creates locals if and only if it needs it whereas in the first code the locals are created even though you may not use them.

This does not make any significant effect on the performance for few variables however it improves the readability of the code.

7.Simplifying & Rephrasing Math to avoid expensive operations

I always keep a pen and paper on the desk while I write programs. I write on paper the equations and do some shifting and changes and get a simpler equation.

Here is a classic example which will boost the performance of this snippet:
Код:
new Float:x,Float:y,Float:z;
GetPlayerVelocity(playerid,x,y,z);
if(floatsqrt( (x*x) + (y*y) + (z*z)) > 5.0)
Код:
new Float:x,Float:y,Float:z;
GetPlayerVelocity(playerid,x,y,z);
if( ((x*x) + (y*y) + (z*z)) > 25.0)
Do you notice the change?
I squared both sides on the condition in the if statement and eliminated the slow function 'floatsqrt'.

Here is another one:
Код:
for(new i = 0, j = GetTickCount(); i < 10; i++)
{
  if( j - LastTick[i] > MAX_TIME_ALLOWED)
  {

  }
}
Код:
for(new i = 0, j = GetTickCount() - MAX_TIME_ALLOWED; i < 10; i++)
{
  if(j > LastTick[i])
  {

  }
}
Whoa, I removed MAX_TIME_ALLOWED for the condition. Now the subtraction is done only once whereas it is done every time in the first code. Even this improvement is insignificant unless you have the operation which consume lot of CPU.

8. memcpy, strfind, etc work on arrays too

After all strings and arrays are one and the same. The only difference is that a string is terminated by a null character whereas a normal array doesn't.

Код:
new DefaultPlayerArray[100] = {1,2,3,4,5,6,7,8,9,10};
new PlayerArray[MAX_PLAYERS][100];

for(new i = sizeof(DefaultPlayerArray); i != -1; i--)
{
  PlayerArray[playerid][i] = DefaultPlayerArray[i];
}
Here is another equivalent code
Код:
memcpy(PlayerArray[playerid], DefaultPlayerArray, 0, sizeof(DefaultPlayerArray)*4, sizeof(PlayerArray[]));
I did some benchmarks for the two codes and here are the results:
Loop Version:
4286ms
4309ms
4410ms

memcpy version:
60ms
62ms
60ms

Similarly you can use strfind,strmid and many other string functions on arrays.The only problem is that when str functions find a element '0' in your array, the function terminates because the value 0 means '\0', i.e:null character.

9. Is using CallRemoteFunction really worth?
First of all, I would like to say that CallRemoteFunction is horribly slow and must be avoided whenever possible. CallRemoteFunction is usually used to update player variables when you have an anti-cheat in some other script.

Did you ever thought of having an anti-cheat in every script? I actually have one anti-hack in my gamemode which ensures that modified data is not updated in database and another anti-hack in administration filterscript which deals with taking action on the cheat (works independently).

Why two anti-cheats? We have two choices, either make two anti-cheats or use CallRemoteFunction to update the player variable.

Sometimes having two separate anti cheats is faster, in fact the some of the anti-cheat checks take a quarter of the time CallRemoteFunction takes to call the update function.

It doesn't matter if you are calculating some player variables in each and every script. Its way better than updating in one script and using CallRemoteFunction to access it.

10. Accessing array elements multiple times

Lets take an example to understand what we are talking about here
Код:
new val = value[x][y][z];
for(new i = 50; i != -1; --i) Arr[i] = val;
Код:
for(new i = 50; i != -1; --i) Arr[i] = value[x][y][z];
Which one in your opinion is faster?

The first one is faster if you had read the Tip #2 carefully. You know that calculating the correct address from the array index takes some time. In the second code, the address calculation is done every time the value is copied to Arr whereas in the first case we calculate the address only once.

Here is the take away message, if you are going to access an array element multiple times then create a temporary copy of the array element in a local variable and use the local variable.

Speed Tests:
Code 1:2280,2330,2350
Code 2:8008,8183,8147

11. Do not mix floats and integers in an expression
by Mauzen

Maybe this one is too simple, but I at least wanted to add it, as I see people doing that "mistake" very often.

Never mix up floats and integers (even if it does not give a tag mismatch warning). Always use the same datatypes in a single statement.

e.g.
pawn Код:
new Float:result = 2.0 + 1;

// Is compiled as
new Float:result = 2.0 + float(1);

// Which is significantly slower than
new Float:result = 2.0 + 1.0;
pawn Код:
new mindist = 10;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < mindist)

// Is compiled as
new mindist = 10;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < float(mindist))

// Which is significantly slower than
new Float:mindist = 10.0;
if (GetPlayerDistanceFromPoint(playerid, 237.9, 115.6, 1010.2) < mindist)
For complex mathematical tasks a simple .0 can make a speed difference of some percent.

12. Using streamer unnecessarily
It has become a habit for everyone to use streamer even when you need just 10 or 20 map icons, 50 objects, etc.

Do you even know what is a streamer? Streamer is a plugin/include which allows you to bypass the SAMP limits. SAMP allows a maximum of 1000 objects and you cannot have more objects than that.

Streamer allows you to bypass that limit by creating objects when a player is within the draw distance to the object and destroying it when there is no player in vicinity of the object. So basically streamer creates objects when required and destroys them when not required. In this way it allows you to cross the SAMP Limits.

When you use Streamer functions, say CreateDynamicObject, streamer doesn't really create an object. It adds the object information (X,Y,Z,RotX,RotY,RotZ....) to a database of objects. After a definite number of server tick/cycle have passed, it goes through all the objects in the database and checks if there is a player close to an object and creates it if required.

You can see streamer adding information about the object to a database here.

Player update starts here
Here is the function responsible for updating objects.

Does it makes sense to use streamer when you have less than 1000 objects?
Do you really need a streamer?

NO!

If you are sure that you are not going to cross the SAMP limit, then you needn't use a streamer.

This brings us a new problem, suppose in your existing version, you have 500 objects but you are going to update your script which needs 1500 objects. So now do you need to convert all the SAMP Object natives to streamer natives?

No if you wrote your initial version smartly.

Here is what I do:
Код:
#define CreateDynamicObject CreateObject
You can now use CreateDynamicObject in your code even though you do not have streamer.

When you know that you are going to need streamer, just remove the defines and include streamer.

A even clever way is to use CreateObject for the objects which are present at popular zones such as a spawn point where you can assume that a player will almost always be present at that location. For objects which are at remote locations and players hardly visit them, you should definitely use CreateDynamicObject because these are the ones which needn't be created all the time whereas the popular ones are bound to exist all the time (even if used with streamer so using streamer for such objects is not worth the cost).

You will have to do it for many objects to see a reasonable improvment since streamer is a plugin and hence it is pretty fast compared to PAWN code.

Similarly, you can do it for other natives.

13. Good & Bad Use of functions (Optimizing 2D Array manipulation code)
A common myth which many believe in is that function calls are very expensive which isn't true. In fact, raw function calls (empty) are many times faster than dereferencing a 2D array.

Код:
native SLE_algo_foreach_list_init(list:listid, &val);
native SLE_algo_foreach_list_get(feid);

#define foreach::list(%0(%1)) for(new %1, fel_%0@%1_id = SLE_algo_foreach_list_init(%0, %1); SLE_algo_foreach_list_get(fel_%0@%1_id);)
If you carefully it just makes a function call to obtain the value which is faster than dereferencing the array. To clear the mist completely, the function is defined in a plugin otherwise for obvious reasons it wouldn't be faster because there is indeed an array being used but that is inside a plugin.

That example was just to show that a function call isn't that costly compared to the other code that you write. This means that you should create functions for large chunks of code which does a specific task when necessary especially if it improves the readablity of the code.

However, misuse of functions could prove costly especially in loops which has been discussed earlier.

Here is one situation where using a 1D/2D array would fare better.

Quote:
Originally Posted by Vince
Посмотреть сообщение
One thing I see constantly, and which is not mentioned in the first post, is the excessive usage of GetPlayerName. A player can't change his name while connected (the exception, of course, being SetPlayerName) so it seems redundant to call the function over and over again. I would say using a wrapper is even worse. Simply store it in a variable when the player connects and then use that variable everywhere. The same goes for GetPlayerIP.
There is another myth which says "making functions is always slower" which isn't true at all when it comes to dealing with multi-dimensional arrays. If done properly, making functions can actually improve performance significantly.

Код:
for(new y = 0; y < 100; y++)
{
  Array[playerid][y] = y;
}
is considerably slower than

Код:
stock DoSomething(arr[])
{
  for(new y = 0; y < 100; y++)
  {
  arr[y] = y;
  }
}
The reason lies within the roots of the assembly code. A quick look at how arrays are dereferenced in PAWN explains it.

This is the amount of code that is involved in dereferencing a 2D Array
Код:
#emit CONST.alt arr //Load the address of the array
#emit CONST.pri 2 //We want to access the 2nd sub-array
#emit IDXADDR //Address of the 2nd element of the major array

#emit MOVE.alt //Keep a copy of that address since we need to add it to the offset to get the address of the sub-array

//ALT = PRI = Address of the 2nd element of the major array
#emit LOAD.I

//ALT = Address of the 2nd element of the major array
//PRI = offset relative to the address stored in the ALT to the 2nd sub-array
#emit ADD

//PRI now has the address of the sub-array
#emit MOVE.alt //Move the address of the first element of the sub-array from PRI to ALT

#emit CONST.pri 4 //We want the 4th element of the sub-array
#emit LIDX//Load the value stored at arr[2][4]
Compare it with what goes to dereference a 1D array
Код:
#emit CONST.alt array_address
#emit CONST.pri n
#emit IDXADDR //PRI now has the address of the (n + 1)th element

#emit CONST.alt array_address
#emit CONST.pri n
#emit LIDX //PRI now has the value stored in the (n + 1)th element
There is

This is how arrays are passed
Код:
 //Pushing the address of the global string
  #emit PUSH.C global_str 

  //Pushing a local string
  #emit PUSH.S cmdtext
Which clearly explains why that works. When you push arrays, you push the address of the array so in your function call you recieve a 1D array. The first part of the 2D array dereference code is essentially skipped in every iteration in this case which makes it a whole lot faster.

Its sad that PAWN doesn't provide pointers.
Reply
#2

pawn Код:
for(new i = 0,j = GetPlayerPoolSize();i < j;i++)
{

}
Must be:
Код:
for(new i = 0,j = GetPlayerPoolSize();i <= j;i++)
{

}
I think using arrays hardly matters, according to me its much readable than individual variables for each assignment (like your position example).

One thing i would really suggest people to limit defines (constants) to their exact usage limit. That would really save the memory and the output size.
For example: You can save alot of memory by just redefining MAX_PLAYERS.
pawn Код:
#undef MAX_PLAYERS
#define MAX_PLAYERS 100
Widely used in making player arrays; same for other stuff as well.
Reply
#3

Can you explain about when we should use stock or public and whats more optimized to which function?
ex:
Код:
public SavePlayerData(playerid)
or
Код:
stock SavePlayerData(playerid)
Or it doesn't matter in speed matters?
Also, you didn't speak about timers and OnPlayerUpdate alot of players use OnPlayerUpdate as a timer...
Also, does it matters if i'm using foreach function or for with MAX_PLAYERS in speed matters?

Overall i have learned alot more from this tutorial then ****** tutorial, I dont know why.
Reply
#4

neat

Quote:
Originally Posted by maximthepain
Посмотреть сообщение
Can you explain about when we should use stock or public and whats more optimized to which function?
ex:
Код:
public SavePlayerData(playerid)
or
Код:
stock SavePlayerData(playerid)
Or it doesn't matter in speed matters?
public functions headers are saved in memory, so you can call them using CallLocal/RemoteFunction or SetTimer(Ex). If you aren't using any of the mentioned functions to call yours, there is no point to forward a public function. stock is not a function declaration keyword. It's used to tell compiler that if you aren't using this function anywhere in the code, he should ignore it.
Reply
#5

Quote:
Originally Posted by Gammix
Посмотреть сообщение
pawn Код:
for(new i = 0,j = GetPlayerPoolSize();i < j;i++)
{

}
Must be:
Код:
for(new i = 0,j = GetPlayerPoolSize();i <= j;i++)
{

}
I think using arrays hardly matters, according to me its much readable than individual variables for each assignment (like your position example).

One thing i would really suggest people to limit defines (constants) to their exact usage limit. That would really save the memory and the output size.
For example: You can save alot of memory by just redefining MAX_PLAYERS.
pawn Код:
#undef MAX_PLAYERS
#define MAX_PLAYERS 100
Widely used in making player arrays; same for other stuff as well.
Is that equation <= only for player loops?

Also about this loop

Код:
The same code can be optimized further
Code:

for(new i = GetPlayerPoolSize();i != -1;i--)
{

}
Can I use i++ according to which direction I want to loop, right?
Reply
#6

stock doesn't do anything except ignore the unused function exception.

if you're making functions, just write

Код:
myFunction(){};
Unless you're making an include. Then use stock myFunction(){};


OT: Most of these optimizations only improve the execution time by milliseconds I'd bet, half of them aren't even worth the time to sit through and optimize. Put it this way: you won't need to do this in your scripts.
Reply
#7

Code:
new Float:pos[3];
GetPlayerPos(playerid,pos[0],pos[1],pos[2]);
SetPlayerPos(playerid,pos[0] + 5,pos[1] + 5,pos[2] + 5);
deleted

BTW I'm not having a go at you I'm just challenging your code


Actually, you're right. The variables are quicker than the array, sorry. But when using arrays it's much easier to contain and to sort. In scripting it's pretty much all about what gets the job done in the most readable manner. The difference in memory isnt even kbytes.
Reply
#8

Quote:
Originally Posted by sammp
Посмотреть сообщение
OT: Most of these optimizations only improve the execution time by milliseconds I'd bet, half of them aren't even worth the time to sit through and optimize. Put it this way: you won't need to do this in your scripts.
Yes in most of the case, why care about negligible speed!

Quote:
Originally Posted by Cypress
Посмотреть сообщение
Is that equation <= only for player loops?
No, for all Pool sizes. Because the pool size have all the valid cells and if you don't use =, you'll miss the last cell.

Quote:
Originally Posted by Cypress
Посмотреть сообщение
Also about this loop

Код:
The same code can be optimized further
Code:

for(new i = GetPlayerPoolSize();i != -1;i--)
{

}
Can I use i++ according to which direction I want to loop, right?
Doesn't make sense if you use i++. It will turn out to be an infinite loop which will always exceed a value greater than Pool size every time (+1).
Reply
#9

Quote:
Originally Posted by Gammix
Посмотреть сообщение
I think using arrays hardly matters, according to me its much readable than individual variables for each assignment (like your position example).

One thing i would really suggest people to limit defines (constants) to their exact usage limit. That would really save the memory and the output size.
For example: You can save alot of memory by just redefining MAX_PLAYERS.
pawn Код:
#undef MAX_PLAYERS
#define MAX_PLAYERS 100
Widely used in making player arrays; same for other stuff as well.
Thanks for reporting! Fixed it!

Using arrays matter a lot!!
If ****** were to be here he would have screwed you for that statement lol (because I remember him telling more organized doesn't mean more efficient)

At the MAX_PLAYERS statement, the memory you save depends more on your code. By the way no one cares about memory in 2015! But anyway it is good to keep memory usage minimum.

Quote:
Originally Posted by maximthepain
Посмотреть сообщение
Can you explain about when we should use stock or public and whats more optimized to which function?
ex:
Код:
public SavePlayerData(playerid)
or
Код:
stock SavePlayerData(playerid)
Or it doesn't matter in speed matters?
Also, you didn't speak about timers and OnPlayerUpdate alot of players use OnPlayerUpdate as a timer...
Also, does it matters if i'm using foreach function or for with MAX_PLAYERS in speed matters?

Overall i have learned alot more from this tutorial then ****** tutorial, I dont know why.
Calling a public or a stock produce identical assembly (AMX Code). Therefore the speeds will remain the same. But wait, public functions make the function accessible outside the script but that comes with a cost. More public functions you add to your script, the more slower CallLocalFunction & CallRemoteFunction & funcidx gets.

Hence, you must make functions public if and only if you are going to use them in CallLocalFunction/CallRemoteFunction/funcidx.

The stock keyword is optional, by prefixing a function identifier with stock only tells the compiler to not compile that function if it doesn't get called in the script.

About the OnPlayerUpdate, using OnPlayerUpdate is for timers with small interval is good instead of having to create shitlod of timers for each player. Each SetTimer/SetTimerEx you use , you make the server's overall performance less efficient and it will also take some memory. It is usually good to have one single timer (SetTimer/Ex) which checks all the players rather than having a timer for each individual player.

First of all if you are using MAX_PLAYERS, then stop using it and switch to GetPlayerPoolSize().When you have scattered playerids then foreach will be faster than using GetPlayerPoolSize Loop. But this never happens (extremely rare since SAMP gives the lowest available ID to the player who joins). GetPlayerPoolSize loop will be much faster than foreach when the player count is high, especially when the player count is nearly the max capacity.

@Cypress Gammix has answered but I would like to add some more to it. Use the subtraction loop if the order of iterations do not matter. I have never come across a situation where I had to check every player in order.

Quote:
Originally Posted by sammp
Посмотреть сообщение
stock
OT: Most of these optimizations only improve the execution time by milliseconds I'd bet, half of them aren't even worth the time to sit through and optimize. Put it this way: you won't need to do this in your scripts.
None of them improve execution time by milliseconds, in fact they are less than 0.1 milliseconds.Computers are very fast that you cannot observe the effect. You need to iterate a million times to see milliseconds. Please check my reply to Gammix below and let me know if you'd change your mind (half? or just 1 or 2 optimizations that are not worth optimizing).

Quote:
Originally Posted by sammp;
Actually, you're right. The variables are quicker than the array, sorry. But when using arrays it's much easier to contain and to sort. In scripting it's pretty much all about what gets the job done in the most readable manner. The difference in memory isnt even kbytes.
There is no question of memory here since local variables are stored on the stack. Moreover, both the array version and the individual variable version occupy the same amount of space on the stack. I think it pretty much clear that arrays are a way lot slower than individual variables after looking at the assembly output that I showed in the thread.If that does not convenience you, then here are few benchmark result:
Array:921,921,917
Non-Array:505,532,510

By the way I never told "not to use arrays". I just said avoid them when you can.

Examples:
Код:
new params[5];
new pos[3];
new color[2];
Quote:
Originally Posted by Gammix
Посмотреть сообщение
Yes in most of the case, why care about negligible speed!
I have no idea how many times I have told you that everything optimization makes negligible difference whereas collection of optimizations make a huge improvement.

Moreover, please justify "most of the case". I would prefer to use "few of them" make negligible improvement.

#1 I agree on this
#2 You just have no idea, it depends how many times you access the array.
Array (10 Assignments):2444,2448,2473
Non-Array (10 Assignments):972,975,963

Speed Test Code:http://pastebin.com/aMkNtaC2

#3 7x faster than using CallLocalFunction in my gamemode - hmm, how is this negligible?
#4 Anyone goes against this would have got badly screwed by ******
#5 Makes a difference when you hook SetPlayerHealth,SetPlayerPos,etc for Anti-Cheat or for some other purpose

#6 Check this
With Optimization:1102,1080,1069,1091
Without Optimization:2374,2359,2429,2364

Test Code:http://pastebin.com/SLZDGRG4

#7 I would agree on this but does make a small difference
With Optimization:134,128,129
Without Optimization:172,168,169

#8 This will improve performance to a very small extent but makes the code much more readable!

#9 You have no idea how slow floatsqrt is

#10 73.5 times faster than original code, negligible :O really?

#11 Sometimes twice as fast, sometimes thrice as fast! Negligible ye?

I have tested every optimization I have posted here whereas you made claims without checking.

If you are so least bothered about performance, then why not use dini,dcmds instead of y_ini/ZCMD?
Reply
#10

I really like what you did, have alot interesting stuff here.

Nice job.
Reply
#11

Most of these optimizations are too minute to make a big a noticeable impact. Removing 2 machine code instructions just isnt worth it. If one cares about performance - pawn isnt the language of choice.

Quote:
Originally Posted by Yashas
View Post
Avoid creating your functions when there is a native which can do it (or maybe using a combination of natives).
It entirely depends on the natives implementation and your use case. Say you wanted to find a substring, you would probably use strfind. The native uses a linear algorithm, but it can be done much faster with, say, kmp with the cost of a little more pre-calculation.
Reply
#12

Quote:
Originally Posted by Macluawn
View Post
Most of these optimizations are too minute to make a big a noticeable impact. Removing 2 machine code instructions just isnt worth it. If one cares about performance - pawn isnt the language of choice.


It entirely depends on the natives implementation and your use case. Say you wanted to find a substring, you would probably use strfind. The native uses a linear algorithm, but it can be done much faster with, say, kmp with the cost of a little more pre-calculation.
Whatever algorithm you use, natives will always be much faster. The reason is "AMX" is an abstract machine. Some instructions are executed for decoding the P-Code and identifying the arguments,.... Check the source!

Just ****** Abstract Machine, read about it and you'll realize why abstract machines are very slow. The virtual machine has lot of work to do for PAWN Code. PAWN Code is executed by the virtual machine whereas native functions directly run on your real computer.

Just to give you an idea how fast natives can be, here is a custom strcpy functions vs strcat.

http://pastebin.com/Y7RJ21tw

Results:
native: 697,700,718,705
non-native:5484,5422,5507,5562

Mind blown?

Quote:
Originally Posted by Macluawn
View Post
Most of these optimizations are too minute to make a big a noticeable impact. Removing 2 machine code instructions just isnt worth it. If one cares about performance - pawn isnt the language of choice.
Hmm, there are only two examples which do that.And at the beginning of the thread I had mentioned that ignore minor optimizations if you want to and keep the code more readable.

I think "Most of these optimizations are too minute" needs to be changed to "Few of these optimizations are too minute".
Reply
#13

Quote:

pawn isnt the language of choice.

That's the reason why I don't care about speed, I rather is more practical and concerned about the efficiency of gameplay and try reducing lag.

Optimizing such like using individual vars from arrays doesn't matter.

And I would have answered the same to Y_Less as well.
Reply
#14

I'm not sure if that is the best possible implementation, but I suppose you're right, I forgot about the virtual machine.
The example I gave was from a test in c++ when checking the performance of a native function vs a more advanced implementation.
Reply
#15

Quote:
Originally Posted by Gammix
View Post
That's the reason why I don't care about speed, I rather is more practical and concerned about the efficiency of gameplay and try reducing lag.

Optimizing such like using individual vars from arrays doesn't matter.

And I would have answered the same to Y_Less as well.
Hmm, can I know how you manage to improve the efficiency and reduce lag without optimizing your code. Do you know who Y_Less is?

He was the best programmer here at the forums unfortunately left us.

There was a thread written by Y_Less titled "Code Optimizations" which has been deleted since he left the community. No one hasn't re-posted it till now.

But just to let you know there were at least 25 optimization tricks in that thread. Even the ones like

Code:
if( (variable = func()) == 1) 

VS

variable = func();
if(variable == 1)
and many more!

Most of them who claimed that they make negligible improvements have apologized for the false claims. I have provided you the speed tests, what more do you want? It is obvious from the tests that most of the tricks improve the performance of the code in multiples like 3x,4x,5x,6x,7x.

Quote:
Originally Posted by Macluawn
View Post
I'm not sure if that is the best possible implementation, but I suppose you're right, I forgot about the virtual machine.
The example I gave was from a test in c++ when checking the performance of a native function vs a more advanced implementation.
lol hmm I see
The efficiency of libraries and C++ code will be almost the same provided the programmer knows how to write efficient code.

When I try to re-invent the wheel in C++ just to optimize, it never works because the C++ libraries are written by professionals and are very efficient whereas I am not that professional in C++.
Reply
#16

Quote:
Originally Posted by Yashas
View Post
Hmm, can I know how you manage to reduce lag without optimizing your code.
Throwing a more powerful server at the problem is always an option.
Reply
#17

Quote:
Originally Posted by Macluawn
View Post
Throwing a more powerful server at the problem is always an option.
For that you need to be powerful too. ( $$ ).

By the way, bad code can still cause problems in the server.
Reply
#18

Pretty good information. Thanks for the post.
Reply
#19

Quote:
Originally Posted by Macluawn
View Post
Throwing a more powerful server at the problem is always an option.
No, it's not. The SA-MP server application is single threaded. 1 processing thread is only so powerful. Sure, you can write shitty code that works, and throw more power at it, but at some very reachable point, you're going to feel the impacts, especially if you ever plan to have a beefy playerbase.

So, you can either write bad code and end up needing to fix it later, or you can write code that is good to start out. It doesn't take much longer to do a bit of thinking an research.
Reply
#20

@Yashas: I didn't covered optimization but only speed, the one i have been saying "negligible speed". And if you are going to say it's harmful in long term or in cluster, i don't have any use of those things in a cluster. And if i ever get to, i'll use your method. And i reduce the lag by improving the structure, use static and constant things where mostly required; natives as you said; more usage of stocks/functions to prevent repetition of code.

Its a good practice to use optimizations but not until if it's just a pin drop improvement and when i find the other code more fascinated; at least for me.
I won't reply any further because people may take it as an argument, you may PM if you want to continue.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)