OXP Performance tips
Moderators: winston, another_commander
Re: OXP Performance tips
ok it's fixed in 1.85 so ignore my coments
Re: OXP Performance tips
@Astrobe
Thanks for the links, I'll check them out.
@another_commander
Ok, I did a search and found:
I take that back: neither first case or second. Some can go individually, while others in small batches:
I'm fairly certain the rest can go in one at a time. Changes in oolite-contracts-helpers are function limited and the 3 contract scripts are nearly identical.
Unfortunately, it's the large & huge ones that are most problematic, not for their size but because 'random()' is by far the most common function call in populator and priorityai is, well, behaviour (how can we quantify that?). These two will definitely need testing in isolation.
Remember, most of the changes are in syntax, not logic and they all got past JSLint. I believe (hope!) that any mistakes will either be obvious (ie. D'oh! moments) or missing that a third function also relys on an array/object that I've re-used too early (most likely in priorityai)
https://www.dropbox.com/s/cudwuq8d56epc ... s.zip?dl=0
Thanks for the links, I'll check them out.
@another_commander
The second case. Most of what I did wasanother_commander wrote: ↑Thu Oct 05, 2017 5:45 pmWould these files need to replace the original ones as a batch or can we substitute the core files one by one? In the second case, maybe a good way to test would be to simply take the first file, substitute the original one and test for a while, looking for different behaviours and/or bugs etc. If we hit bugs, we know immediately which file to look at for fixing them. When happy with that one, put back the original file, then replace the second in the list with yours and repeat. This way, we test small changes each time and in case of problems we know that the new file we introduced will probably need further analysis. It still takes a heck of a time to test, but at least we test in smaller chunks.
- function reference caching (eg. random = Math.random;) ->[speed]
- property caching (eg. x = this.ship.group; if( x.leader )... ->[speed]
- for loop boundary tests (move .length test out of for stmt) ->[speed]
- re-use arrays & objects ->[GC]
oolite-priorityai
(a special case) and in the 3 oolite-contracts-*.js
files & oolite-primable-equipment-manager.js
, where I implemented pools for recycling objects (post is in the works). Even there, it is limited to the file. Mind you, I didn't track the cross-talk....Ok, I did a search and found:
Code: Select all
oolite-constrictor-hunt-mission.js calls worldScripts["oolite-populator"].systemWillPopulate();
oolite-thargoid-plans-mission.js calls worldScripts["oolite-populator"].systemWillPopulate();
oolite-cloaking-device-mission.js calls worldScripts["oolite-populator"].systemWillPopulate();
oolite-cloaking-device-pod.js calls worldScripts["oolite-primable-equipment-register"]._updatePrimableEquipmentSettings("EQ_CLOAKING_DEVICE",true);
oolite-contracts-cargo.js calls worldScripts["oolite-contracts-helpers"];
oolite-contracts-parcels.js calls worldScripts["oolite-contracts-helpers"];
oolite-contracts-passengers.js calls worldScripts["oolite-contracts-helpers"];
oolite-priorityai.js calls worldScripts["oolite-contracts-helpers"]);
oolite-tutorial-fighter.js calls worldScripts["oolite-tutorial"]._nextItem();
- oolite-cloaking-device-*.js 4 small files (Norby is currently working on one: oolite-cloaking-device-equipment.js)
- oolite-constrictor*.js 4 small files
- oolite-thargoid-*.js 1 small, 1 tiny (single fn call)
- oolite-tutorial*.js 1 small, 1 medium
I'm fairly certain the rest can go in one at a time. Changes in oolite-contracts-helpers are function limited and the 3 contract scripts are nearly identical.
Unfortunately, it's the large & huge ones that are most problematic, not for their size but because 'random()' is by far the most common function call in populator and priorityai is, well, behaviour (how can we quantify that?). These two will definitely need testing in isolation.
Remember, most of the changes are in syntax, not logic and they all got past JSLint. I believe (hope!) that any mistakes will either be obvious (ie. D'oh! moments) or missing that a third function also relys on an array/object that I've re-used too early (most likely in priorityai)
I agree completely, release what is known to be stable. There is no great urgency here, as 4 MB over 5 minutes is pretty tame compared to some oxp's. But it's a good exercise for those involved and once published, authors will have some examples to draw from.another_commander wrote: ↑Thu Oct 05, 2017 5:45 pmBecause of the time that it will require for a full test snd confirmation of good working order, I think that this should probably be the first thing to look at after we release 1.86. Testing can of course begin before that, but given that it has been more than one year since the last stable release and that applying these changes now may affect stability in various and original ways, maybe it would be best to get a new stable out first.
OK, I've restored those changes and left mine commentted out (search for 'cag:')another_commander wrote: ↑Thu Oct 05, 2017 5:45 pmRegarding the deliberate changes you have made, I think it's best to wait for cim to give some input, as he was the author of all those scripts.
https://www.dropbox.com/s/cudwuq8d56epc ... s.zip?dl=0
Last edited by cag on Sat Oct 07, 2017 1:58 am, edited 1 time in total.
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
As promised, part 2, object pools:
When trying to re-use arrays/object, you'll encounter a situation where they are shared among functions. If it's just a one-off instance, placing it in a
Rather than creating a thing directly, we have an allocating function that first checks if there are any used ones available for re-use before creating a new one.
When we're finished with a thing, we recycle it by putting it back in the list of used ones.
Care must be taken so that no information is carried over from a thing's previous incarnation. You can either clear all the keys when freeing or over-write them all when you allocate (I've included both just to demonstrate). This is quite easy for arrays, just set .length = 0. If you don't, you may develope some particularly nasty bugs! It has been suggested that for objects, using the
I'm a firm believer in the K.I.S.S. methodology (Keep It Simple, Stupid), so if you have more than one type of thing, just make a pool for each. A single pair of functions to handle a complex mix of things is just asking for trouble. The same argument if your things have things; a pool for each type and be done with it.
Between these 2 posts, I believe that's all we need to cut significantly the amount of garbage we generate (though I'd be happy to be proved wrong). We cannot eliminate it completely, even with some changes to the core I have in mind. But it would be nice if we could measure the interval in minutes, not seconds!
When trying to re-use arrays/object, you'll encounter a situation where they are shared among functions. If it's just a one-off instance, placing it in a
this.$variable
works fine. But when there are multiple instances and/or they are dynamically created, we'll need a different solution. (NB: the following applies equally to arrays and objects, so I'll use 'things' for brevity)Rather than creating a thing directly, we have an allocating function that first checks if there are any used ones available for re-use before creating a new one.
Code: Select all
function Pending( fn, parm ) { // constructor
this.fn = fn;
this.parm = parm;
}
var used_pending = [];
function alloc_pending( fn, parm ) {
var event;
if( used_pending.length > 0 ) {
event = used_pending.pop();
event.fn = fn; // over-write previous info
event.parm = parm; // "
} else {
event = new Pending( fn, parm );
}
return event;
}
var events = [];
...
//events.push( {fn: some_fn, parm: true} ); // instead of creating new ones
events.push( alloc_pending( some_fn, true ) ); // we recycle
Code: Select all
function free_pending( event ) {
if( !event ) return;
event.fn = null; // clear existing info
event.parm = null; // "
used_pending.push( event );
}
//event = null; // instead of discarding when finished
free_pending( event ); // we return it to the used list
delete
statement to clear keys is preferable but I found no sigficant difference when I profiled both methods.I'm a firm believer in the K.I.S.S. methodology (Keep It Simple, Stupid), so if you have more than one type of thing, just make a pool for each. A single pair of functions to handle a complex mix of things is just asking for trouble. The same argument if your things have things; a pool for each type and be done with it.
Between these 2 posts, I believe that's all we need to cut significantly the amount of garbage we generate (though I'd be happy to be proved wrong). We cannot eliminate it completely, even with some changes to the core I have in mind. But it would be nice if we could measure the interval in minutes, not seconds!
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
- Norby
- ---- E L I T E ----
- Posts: 2577
- Joined: Mon May 20, 2013 9:53 pm
- Location: Budapest, Hungary (Mainly Agricultural Democracy, TL10)
- Contact:
Re: OXP Performance tips
I asked cag to optimize a modified oolite-cloaking-device-equipment.js what I find once in the forum This solve a problem that missiles should lost lock when the player cloaks. Here is the original:
The optimized variant with cag's comments:
I tested it using the next commands in debug console:
Before these I put my target lock to a ship which has missiles and paused the game. After these I unpaused and cloaked, then the missile exploded immediately so the code is working well. I think this should be in the core.
Code: Select all
this.activated = function()
{
player.ship.isCloaked = !player.ship.isCloaked;
if (player.ship.isCloaked)
{
// find missiles targeting player
var missilesTargetingPlayer = system.filteredEntities(this, function(ent)
{
return (ent.isMissile && ent.target == player.ship);
}, player.ship, 25600);
for (var i=0;i<missilesTargetingPlayer.length;i++)
{
missilesTargetingPlayer[i].target = null; // target lost -> missile detonates
}
}
}
cag wrote:I replaced filteredEntities with entitiesWithScanClass as it runs 8x's faster. It's too bad it doesn't take a function to filter entities, but whenever the situation can be defined by class, it's great! There's also a countEntitiesWithScanClass if you only need to know how many (really fast and no array to worry about). And matching pairs for role & primary role.
With no missiles, this has an overhead cost of 0.093 ms (vs. 0.138 ms for your version); on a population of 54, it profiled at 0.168 (vs. 0.847 ms for your version). Code:
Code: Select all
this.activated = function activated()
{
"use strict";
var that = activated;
var entitiesWithScanClass = (that.entitiesWithScanClass = that.entitiesWithScanClass
|| system.entitiesWithScanClass);
var isCloaked, ps = player && player.ship;
isCloaked = ps.isCloaked = !ps.isCloaked;
if (isCloaked)
{
// find missiles targeting player
var ent, scannerRange = ps.scannerRange;
var missilesTargetingPlayer = entitiesWithScanClass( 'CLASS_MISSILE', ps, scannerRange );
for( var i = 0, len = missilesTargetingPlayer.length; i < len; i++ )
{
ent = missilesTargetingPlayer[i];
if( !ent.isValid ) continue;
if( ent.target !== ps ) continue;
ent.target = null; // target lost -> missile detonates
}
}
}
Code: Select all
PS.awardEquipment("EQ_CLOAKING_DEVICE");
PS.target.target=PS; PS.target.fireMissile();
Last edited by Norby on Mon Oct 16, 2017 7:14 am, edited 2 times in total.
- Norby
- ---- E L I T E ----
- Posts: 2577
- Joined: Mon May 20, 2013 9:53 pm
- Location: Budapest, Hungary (Mainly Agricultural Democracy, TL10)
- Contact:
Re: OXP Performance tips
@cag: I installed the full pack of your optimized oolite-*.js files and I got some errors. First occur with cim's Comms Pack:
Next is with Snoopers:
Finally when I selected the F4/Primable Equipment the game crashed. The end of the log show the next lines about 20 thousand times within a second:
Your v2 pack give the same results.
Code: Select all
11:42:02.507 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript exception (Comms Pack A 0.5): TypeError: obj[role][personality] is undefined
11:42:02.507 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/.Oolite/AddOns/Test.oxp/Scripts/oolite-priorityai.js, line 7197.
Code: Select all
11:42:11.037 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript exception (snoopers 2.5): TypeError: strA is undefined
11:42:11.037 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/GNUstep/Library/ApplicationSupport/Oolite/ManagedAddOns/oolite.oxp.Svengali.CCL.oxz/Scripts/Cabal_Common_Functions.js, line 256.
11:42:11.040 [LogEvents] GlobalLog (OOJSGlobal.m:256): Player gui screen changed from GUI_SCREEN_LOAD to GUI_SCREEN_STATUS
11:42:11.097 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript exception (snoopers 2.5): TypeError: strA is undefined
11:42:11.097 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/GNUstep/Library/ApplicationSupport/Oolite/ManagedAddOns/oolite.oxp.Svengali.CCL.oxz/Scripts/Cabal_Common_Functions.js, line 256.
Code: Select all
11:49:54.956 [script.javaScript.stackTrace] OOJSDumpStack (OOJavaScriptEngine.m:811): 0 (oolite-primable-equipment-manager.js:228) _configurePrimableEquipment()
11:49:54.956 [script.javaScript.stackTrace] DumpVariable (OOJavaScriptEngine.m:731): this: [Script "oolite-primable-equipment-register" version (nil)]
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript error (snoopers 2.5): can't convert stage1 to string
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/.Oolite/AddOns/Test.oxp/Scripts/oolite-primable-equipment-manager.js, line 228.
11:49:54.956 [script.javaScript.stackTrace] OOJSDumpStack (OOJavaScriptEngine.m:811): 0 (oolite-primable-equipment-manager.js:228) _configurePrimableEquipment()
11:49:54.956 [script.javaScript.stackTrace] DumpVariable (OOJavaScriptEngine.m:731): this: [Script "oolite-primable-equipment-register" version (nil)]
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript error (snoopers 2.5): can't convert stage1 to string
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/.Oolite/AddOns/Test.oxp/Scripts/oolite-primable-equipment-manager.js, line 228.
11:49:54.956 [script.javaScript.stackTrace] OOJSDumpStack (OOJavaScriptEngine.m:811): 0 (oolite-primable-equipment-manager.js:228) _configurePrimableEquipment()
11:49:54.956 [script.javaScript.stackTrace] DumpVariable (OOJavaScriptEngine.m:731): this: [Script "oolite-primable-equipment-register" version (nil)]
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript error (snoopers 2.5): can't convert stage1 to string
11:49:54.956 [script.javaScript.error.cantConvertTo] ReportJSError (OOJavaScriptEngine.m:214): /home/norbi/.Oolite/AddOns/Test.oxp/Scripts/oolite-primable-equipment-manager.js, line 228.
Re: OXP Performance tips
Fixed. Stupid mistake, not setting re-used array's length = 0Norby wrote: ↑Fri Oct 06, 2017 10:59 am@cag: I installed the full pack of your optimized oolite-*.js files and I got some errors. First occur with cim's Comms Pack:
I cannot reproduce this. Try my new set and if it recurs, please provide a list of oxp's your using.
Fixed, I hope. I fixed *a* bug in Primable Equipment, but sure it's the same!
The new set can be found here:
https://www.dropbox.com/s/cudwuq8d56epc ... s.zip?dl=0
Keep those bugs coming!
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
oolite-cloaking-device-equipment.js
code in my new set. Now everyone can play around with it.Edit: Restored to original. Link unchanged.
Last edited by cag on Sat Oct 07, 2017 8:27 pm, edited 1 time in total.
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
I did some digging and it turns out that this in not a bug of mine (whew!). Snoopers' event handler forNorby wrote: ↑Fri Oct 06, 2017 10:59 amNext is with Snoopers:Code: Select all
11:42:11.037 [script.javaScript.exception.unexpectedType] ReportJSError (OOJavaScriptEngine.m:203): ***** JavaScript exception (snoopers 2.5): TypeError: strA is undefined ...
guiScreenChanged
checks the version #'s of some oxp's. Among those are oolite-constrictor-hunt
and oolite-nova
and it's testing for version 1.77, using Svengali's CCL. The problem is that none of the Resouces\Scripts files have a version number.
Code: Select all
this.guiScreenChanged = function()
{
if(this.snoopersInit){
var requires = ['buoyRepair','1.3.2','AsteroidStorm','4.03','oolite-constrictor-hunt','1.77','oolite-nova','1.77','PlanetFall','1.51'];
var checked = this.helper.oxpVersionTest2Array(this.name,requires,1);
...
oxpVersionTest2Array
in turn calls strCompareVersion
Code: Select all
check = this.strCompareVersion(worldScripts[requires[i]].version,requires[i+1]);
.version
property. So the 1st parm, strA
, is undefined.I can't reproduce this bug. I think the reason is that I'm overwriting the originals in the Resouces\Scripts folder, where they inherit a version number from oolite itself.
worldScripts['oolite-constrictor-hunt'].version
shows up as 1.85 in my debug console. So rather than testing these files in your AddOns/Test.oxp, try putting them in the Resouces\Scripts folder (backing up first, of course ) and see if this problem goes away."Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
-
- Quite Grand Sub-Admiral
- Posts: 6681
- Joined: Wed Feb 28, 2007 7:54 am
Re: OXP Performance tips
Apologies for maybe going a bit off-topic, but the missiles detonating when player cloaks script should not be a part of this testing. The reason we specifically do not want missiles detonating is because the cloaking device should not be used as a better-than-ECM ECM. What we have now is that the missile simply loses track of the target when it cloaks and re-aquires it within seconds once the target de-cloaks. When not tracking the target due to cloaking, it simply flies on a more or less straight line.
This is not meant to start up a discussion about the cloaking device, just to point out the issue because it can certainly affect testing. If someone would like to discuss the rights or wrongs of the above, I would recommend opening a different topic in Discussion.
This is not meant to start up a discussion about the cloaking device, just to point out the issue because it can certainly affect testing. If someone would like to discuss the rights or wrongs of the above, I would recommend opening a different topic in Discussion.
- Norby
- ---- E L I T E ----
- Posts: 2577
- Joined: Mon May 20, 2013 9:53 pm
- Location: Budapest, Hungary (Mainly Agricultural Democracy, TL10)
- Contact:
Re: OXP Performance tips
Thanks cag, your latest pack fixed the problems except the
About missiles I think my script is an old one, originated from before when the core got the shiny js AI which do better job than this fix. Sorry for the extra work, at least your code is a good example how to optimize a similar js.
strA
one, which is indeed caused by worldScripts['oolite-constrictor-hunt'].version
returns nothing at me (and oolite-nova
also). The result is the same if I put the scripts into the core resources folder.About missiles I think my script is an old one, originated from before when the core got the shiny js AI which do better job than this fix. Sorry for the extra work, at least your code is a good example how to optimize a similar js.
Re: OXP Performance tips
@ Norby
and remove the line
---------------------------------------------------------------------------------------------------------------------
Here's one for the wiki. It turns out that
Not really, I rushed it (not enough coffee, no excuse). We cannot re-use the
missilesTargetingPlayer
array because it gets clobbered by the return from entitiesWithScanClass
. It's declaration should just be
Code: Select all
var missilesTargetingPlayer;
Code: Select all
missilesTargetingPlayer.length = 0;
Here's one for the wiki. It turns out that
.heading
and .vectorForward
are always identical (verified in the source code). And they are always unit vectors, so there's no need to use .direction() on them directly. If your callback uses both, just pick one. .heading
is easier to type but .vectorForward
is nearer (in Ship vs Entity) and that's one less get in your profile."Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
Added. Together with your first post about GC techniques. And, yes, I've just discovered <pre></pre> blocks...cag wrote: ↑Mon Oct 16, 2017 6:51 amHere's one for the wiki. It turns out that.heading
and.vectorForward
are always identical (verified in the source code). And they are always unit vectors, so there's no need to use .direction() on them directly. If your callback uses both, just pick one..heading
is easier to type but.vectorForward
is nearer (in Ship vs Entity) and that's one less get in your profile.
Re: OXP Performance tips
In frame callbacks (and Timers to a lesser degree), we need to reduce the amount the garbage collection (GC) has to deal with. Whether it's 50 or 200 oxp's loading, every little bit helps. My edits to the
Resouces\scripts files were meant more for demonstration, not performance, as these files are on all machines. I was pleasantly surprised by the savings. Have you noticed a new line in your logs (I forgot one log() statement):
Each object that's reused is one less for GC. The record to date for a ship is __handler_reuse = 114, __comms_reuse = 89!
I've ported some of the core vector & quaternion functions into JScript. This was made easier by the fact that both Vector3D & Quaternion object's properties can be accessed as if they were arrays (eg. position.x == position[0]).
This is about 5% slower but by keeping these property gets to a minimum, it's negligible. And I can use a single pool, vs. 1 for each object type.
Care must be taken when interfacing with oolite's objects. You access by:
This will be twice as slow as a regular assignment ('my_var = ps.position;'), as your doing 3 property gets instead of just one; both generate the same amount of garbage.
When you assign to an oolite object, NEVER use copy_vector or copy_quaternion! Always do it as normal:
This is the fastest and generates no garbage. The copy functions would create garbage (JS constructs a working object to interface with the core) and is 2 x's slower (3 x's if both are oolite objects, you'd be doing 6/8 JSObjectGetVector calls!)
You can DL the file here: https://www.dropbox.com/s/sojm6ulor13s0 ... s.zip?dl=0
You're probably wondering, what's this guy been smoking? Why bother? Well, if all your calculations are done using local arrays (which, of course, you re-use), you'll generate NO garbage (except when interfacing, which you cannot control). This will cost you a little time; your vector calculations will be about 15% slower, off-set somewhat by reduced property gets.
I profiled some code I do every frame: 4 .add(), 1 .subtract(), 4 .multiply() & 1 .direction(). It was slower by 0.015 ms! A small price to greatly reduce garbage. By doing vector work locally, I was able to reduce garbage in a 5 minute period from 60 MB to 37 MB, changing the frequency of GC from every 1m 44s to 2m 55s, with no significant drop in frame rate. For perspective, the current version that's been around for 3 years generates almost 180 MB every 5 minutes, so GC happens every 38s.
Here are a few examples:
Just remember, this is the LAST step in optimizing your oxp. Write, debug, test, profile speed, then take out the garbage.
Full disclosure: there sometimes is a loss of accuracy
There are a few situations where the accuracy of the JS calculations is less than optimal. Included in the download are my test functions and in 2 cases I had to reduce the precision from 1E-10 to 1E-6.
The first case is generating vectorForward, vectorRight & vectorUp locally, from ps.orientation. This has a lot of floating point calculations using values near zero & one and accuracy is a known issue. The core uses higher precision floats and this explains the disparity. That said, it hasn't impacted me to date, though I do round distance to meters.
The second deals with rotations; rotating the ship 180 degrees in one axis in the core (20 steps of 9 degees), repeating in a different axis locally and comparing the angles traversed. The angles, in radians, agree to 6 decimal places, good enough for what I'm doing. But I can imagine a (large) ship scraping the dock's bulkheads if its docking calculations are as imprecise!
The latest version of ReverseControl uses it, if you want to take it for a spin.
Resouces\scripts files were meant more for demonstration, not performance, as these files are on all machines. I was pleasantly surprised by the savings. Have you noticed a new line in your logs (I forgot one log() statement):
Code: Select all
[oolite-libPriorityAI]: ship died, __handler_reuse = 6, __comms_reuse = 0
I've ported some of the core vector & quaternion functions into JScript. This was made easier by the fact that both Vector3D & Quaternion object's properties can be accessed as if they were arrays (eg. position.x == position[0]).
This is about 5% slower but by keeping these property gets to a minimum, it's negligible. And I can use a single pool, vs. 1 for each object type.
Care must be taken when interfacing with oolite's objects. You access by:
Code: Select all
copy_vector( ent.position, my_var ); // my_var is an array
When you assign to an oolite object, NEVER use copy_vector or copy_quaternion! Always do it as normal:
Code: Select all
ent.orientation = my_var;
You can DL the file here: https://www.dropbox.com/s/sojm6ulor13s0 ... s.zip?dl=0
You're probably wondering, what's this guy been smoking? Why bother? Well, if all your calculations are done using local arrays (which, of course, you re-use), you'll generate NO garbage (except when interfacing, which you cannot control). This will cost you a little time; your vector calculations will be about 15% slower, off-set somewhat by reduced property gets.
I profiled some code I do every frame: 4 .add(), 1 .subtract(), 4 .multiply() & 1 .direction(). It was slower by 0.015 ms! A small price to greatly reduce garbage. By doing vector work locally, I was able to reduce garbage in a 5 minute period from 60 MB to 37 MB, changing the frequency of GC from every 1m 44s to 2m 55s, with no significant drop in frame rate. For perspective, the current version that's been around for 3 years generates almost 180 MB every 5 minutes, so GC happens every 38s.
Here are a few examples:
Code: Select all
// target_vector = ps_target.position.subtract( ps ).direction();
copy_vector( ps_target.position, vector );
subtract_vectors( vector, ps_position, target_vector );
unit_vector( target_vector, target_vector );
// effect_posn = ps_position.add( ps_vectorForward.multiply( 50 + viewPosition.z ) );
scale_vector( ps_vectorForward, (50 + viewPosition[2]), vector );
add_vectors( vector, ps_position, effect_posn );
// effect_posn = effect_posn.add( ps_vectorRight.multiply( viewPosition.x ) );
scale_vector( ps_vectorRight, viewPosition[0], vector );
add_vectors( vector, effect_posn, effect_posn );
Full disclosure: there sometimes is a loss of accuracy
There are a few situations where the accuracy of the JS calculations is less than optimal. Included in the download are my test functions and in 2 cases I had to reduce the precision from 1E-10 to 1E-6.
The first case is generating vectorForward, vectorRight & vectorUp locally, from ps.orientation. This has a lot of floating point calculations using values near zero & one and accuracy is a known issue. The core uses higher precision floats and this explains the disparity. That said, it hasn't impacted me to date, though I do round distance to meters.
The second deals with rotations; rotating the ship 180 degrees in one axis in the core (20 steps of 9 degees), repeating in a different axis locally and comparing the angles traversed. The angles, in radians, agree to 6 decimal places, good enough for what I'm doing. But I can imagine a (large) ship scraping the dock's bulkheads if its docking calculations are as imprecise!
The latest version of ReverseControl uses it, if you want to take it for a spin.
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
the new Resources\Scripts has been updated (link is unchanged)
https://www.dropbox.com/s/cudwuq8d56epc ... s.zip?dl=0
Just a couple minor bug fixes; the reason for this post is I rewrote the _paddingText() function in oolite-contracts-helpers.js
By using a few different sizes of whitespace characters, the padding string has shrunk from hundreds (over 600 in one case!) to a couple dozen, worst case.
I doubt this will impact speed or garbage but debugging mission screens with variables that size was really annoying.
In doing so, I discovered that our defaultFont doesn't support the full range of whitespace characters that JS regular expressions do. (It instead outputs question marks, which makes for a messy screen!) So I have a question for non-Windows authors: is defaultFont consistent over different OS's? I would think so, but you never know.
I tested by running a mission screen with a message withand noted which got '?'s. But it can be more easily tested by pasting this into the debug console while in space
and pausing the game. Here are my results to compare:
defaultFont supports 9 of the 16 but only 3 unique lengths.
https://www.dropbox.com/s/cudwuq8d56epc ... s.zip?dl=0
Just a couple minor bug fixes; the reason for this post is I rewrote the _paddingText() function in oolite-contracts-helpers.js
By using a few different sizes of whitespace characters, the padding string has shrunk from hundreds (over 600 in one case!) to a couple dozen, worst case.
I doubt this will impact speed or garbage but debugging mission screens with variables that size was really annoying.
In doing so, I discovered that our defaultFont doesn't support the full range of whitespace characters that JS regular expressions do. (It instead outputs question marks, which makes for a messy screen!) So I have a question for non-Windows authors: is defaultFont consistent over different OS's? I would think so, but you never know.
I tested by running a mission screen with a message with
Code: Select all
'2000' + String.fromCharCode( 0x2000 ) + '2001' + String.fromCharCode( 0x2001 ) + ...
Code: Select all
(function() {
var fromCC = String.fromCharCode;
var width = defaultFont.measureString
player.consoleMessage( '200a ' + fromCC( 0x200a ) + width( fromCC( 0x200a ) ).toFixed(3)
+ ' 2004' + fromCC( 0x2004 ) + width( fromCC( 0x2004 ) ).toFixed(3)
+ ' 2005' + fromCC( 0x2005 ) + width( fromCC( 0x2005 ) ).toFixed(3)
+ ' 2006' + fromCC( 0x2006 ) + width( fromCC( 0x2006 ) ).toFixed(3), 10 )
player.consoleMessage( '2008 ' + fromCC( 0x2008 ) + width( fromCC( 0x2008 ) ).toFixed(3)
+ ' 2009' + fromCC( 0x2009 ) + width( fromCC( 0x2009 ) ).toFixed(3)
+ ' 3000' + fromCC( 0x3000 ) + width( fromCC( 0x3000 ) ).toFixed(3)
+ ' 1680' + fromCC( 0x1680 ) + width( fromCC( 0x1680 ) ).toFixed(3), 10 )
player.consoleMessage( '180e ' + fromCC( 0x180e ) + width( fromCC( 0x180e ) ).toFixed(3)
+ ' 2000' + fromCC( 0x2000 ) + width( fromCC( 0x2000 ) ).toFixed(3)
+ ' 2001' + fromCC( 0x2001 ) + width( fromCC( 0x2001 ) ).toFixed(3)
+ ' 2002' + fromCC( 0x2002 ) + width( fromCC( 0x2002 ) ).toFixed(3), 10 )
player.consoleMessage( '2003 ' + fromCC( 0x2003 ) + width( fromCC( 0x2003 ) ).toFixed(3)
+ ' 2007' + fromCC( 0x2007 ) + width( fromCC( 0x2007 ) ).toFixed(3)
+ ' 202f' + fromCC( 0x202f ) + width( fromCC( 0x202f ) ).toFixed(3)
+ ' 205f' + fromCC( 0x205f ) + width( fromCC( 0x205f ) ).toFixed(3), 10 )
})()
Code: Select all
name unicode width defaultFont
==== ======== ===== ===========
HAIR 0x200a 0.0125 yes
THREE-PER-EM 0x2004 0.217 yes
FOUR-PER-EM 0x2005 0.217 yes
SIX-PER-EM 0x2006 0.217 yes
PUNCTUATION 0x2008 0.217 yes
THIN 0x2009 0.217 yes
IDEOGRAPHIC 0x3000 0.217 yes
OGHAM 0x1680 0.477 no
MONGOLIAN 0x180e 0.477 no
EN QUAD 0x2000 0.477 no
EM QUAD 0x2001 0.477 no
EN SPACE 0x2002 0.477 yes
EM SPACE 0x2003 0.477 yes
FIGURE 0x2007 0.477 no
NARROW 0x202f 0.477 no
MEDIUM 0x205f 0.477 no
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
Re: OXP Performance tips
I've updated vector_fns to add 2 variants on 'angle_between', namely '_angle_between_unitV' and '_angle_between_two_unitV'. [DL link is the same]cag wrote: ↑Sat Oct 21, 2017 1:41 amI've ported some of the core vector & quaternion functions into JScript.
...
You can DL the file here: https://www.dropbox.com/s/sojm6ulor13s0 ... s.zip?dl=0
Often one or both vectors are already unit vectors, either in your code or as referenced in oolite (eg. vectorForward). Using these variants eliminates redundant calculations, as 'angle_between' always converts both vectors to unit vectors. '_angle_between_unitV' assumes the 1st parm is a unit vector.
In Telescope 2.0, I no longer use 'angle_between' at all but have 6 calls to '_angle_between_unitV' and 11 to '_angle_between_two_unitV'!
"Better to be thought a fool, boy, than to open your trap and remove all doubt." - Grandma [over time, just "Shut your trap... fool"]
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.
"The only stupid questions are the ones you fail to ask." - Dad
How do I...? Nevermind.