Reputation: 1640
I wish to know what would be faster to implement.
I want to have a temporary variable that will be used for some calculations inside a function. The attacker
and defender
objects will be later used and everything created inside them should be deleted.
First, I have an approach of creating a temporary dmg
variable to store the damages of attacker
and defender
.
var dmg;
dmg.attacker = 10;
dmg.defender = 10;
var battle = function (dmg, attacker, defender) {
//Some calculations using dmg.attacker and dmg.defender.
}
And a second approach, were I'm storing attacke
r and defender
dmg
inside them. There will be no problem if other part of the program catchs the attacker/defender
object before the dmg
has been deleted.
attacker.dmg = 10;
defender.dmg = 10;
var battle = function (attacker, defender) {
//Some calculations using attacker.dmg and defender.dmg.
delete attacker.dmg;
delete defender.dmg;
}
So, which implementation would be faster and why? Is there any other better options? I'm using Node 4 LTS, it there any nuances that I don't know about?
Upvotes: 1
Views: 423
Reputation: 6956
According to the result of the snippet below the first approach is much faster.
var dmg = {};
var battleDmg = function (dmg) {};
var start = new Date().getTime();
for(var i = 0; i < 100000; i++) {
dmg.attacker = 10;
dmg.defender = 10;
battleDmg(dmg);
}
console.log('dmg:' + (new Date().getTime() - start));
var attacker = {}, defender = {};
var battleAttackerVsDefender = function (attacker, defender) {
delete attacker.dmg;
delete defender.dmg;
};
start = new Date().getTime();
for(i = 0; i < 100000; i++) {
attacker.dmg = 10;
defender.dmg = 10;
battleAttackerVsDefender(attacker, defender);
}
console.log('a vs d:' + (new Date().getTime() - start));
I think it is because in the first case:
delete
operation (instead of 2 operations) is performedUpvotes: 2
Reputation:
This is not the kind of thing for which performance should be the ultimate guiding factor, especially without a profiler in hand. Let maintainability and productivity be the guiding force here until you start measuring hotspots in hindsight.
That said, just as a general rule of thumb, the more local your state changes and accesses are, typically the faster they will be. More local accesses to local variables are preferable from a performance standpoint than more access to fewer but more global variables.
Local State is Fast
Optimizing compilers do their best when the most amount of information is readily available. Effective register allocation and instruction selection is easier when analyzing the local variables of a function than globals variables with a much broader scope than the function which could be modified and accessed anywhere.
The key to efficiency often revolves around loading from slower but bigger memory (DRAM,e.g.) into faster but smaller memory (L1 cache line and then register). Making code fast means keeping and using frequently-accessed memory as much as possible inside those faster but smaller forms of memory (register, e.g.). This is easier for the compiler to do when your variables have a very local scope, since it doesn't have to look outside that scope to see whether it needs to spill the variable to the stack or can keep it inside the register, for example.
So as a general rule of thumb, prefer more local state with shorter scopes to more global states with wider scopes. That not only tends to improve efficiency but also reduce human errors*.
* Note that this is opposite from an assembly mindset. In assembly, it's helpful to reuse registers as much as possible to avoid stack spills. In languages that have variables instead of direct register access, more local variables helps the compiler figure out what registers it can reuse.
Specific Example
In this specific case, adding members and removing them from an associative structure (objects fit this case in JS) tends to be more expensive than creating one and simply discarding it as a whole. These lines:
delete attacker.dmg;
delete defender.dmg;
... specifically tend to be more costly than constructing some temporary object and simply destroying it as a whole when you're finished with it than adding members to a more global object and then removing them when done.
As a plus, this should also be a little less error-prone so you get that bonus as well. As a second plus, assuming attacker
and defender
have a fairly broad scope, this function that creates this dmg
variable would avoid side effects to those objects, and therefore would be thread-safe since it's only dealing with local states (assuming this is all it's doing and not modifying other globals).
If you're really fussy about it, it would be best to create attacker damage and defender damage as two separate scalars instead of aggregated into an object and pass both forms of damage to this battle
function. But that's really degrading the code readability, and something you should only do with a profiler in your hand that tells you this is a top hotspot.
Upvotes: 3