javascript splice causing lag in canvas - javascript

I have a simple running game where platforms scroll from right to left. These platforms are stored in an array, and when they are off screen I use array.splice(index, 1) to remove it. This however is causing an ever so slight lag on the exact second splice is called. I have also used Array.shift() but I can still see the slight dip in frame rate as this is called. Is there a better way to remove/destroy something?
for(var x = 0; x < platforms.length; x++){
var platform = platforms[x];
platform.x -= 10;
if(platform.x + platform.width < 0){
platforms.shift();
}
}

You could just not remove items from the array.
Use a fixed size array instead (large enough to store all the items you need) and flag as canceled the items you don't want to render anymore, you could reuse those flagged slots later.
Or you could directly overwrite the elements if the domain of your problem allows it.
[EDIT]
Some more considerations in response to the comments:
The evaluations of additional flag are computations stable in time, meaning that you can foresee how much time they will need and see if they fit the frame to render at a certain frame rate. Array.splice on the other hand could trigger garbage collection and that could be some order of magnitude longer than other language control flow constructs.
Garbage collection is an intensive task and should be avoided at all costs in the main loop of a game to achieve a fluid framerate. There are some resources and other questions here on SO which elaborate on this point, for example: http://buildnewgames.com/garbage-collector-friendly-code/

shift and splice are "slow" functions. They possibly rebuild your whole array.
Imagine having an area with 1000 items. A shift could possibly be 'create a new array with all items, except the first'. If your first 100 items now result in a shift, you rebuild 100 arrays, with 900-1000 items, which will result in about 100.000 inserts, 100 new array allocations.
for(var i = 0; i < array.length; i++)
{
if (array[i] == ....)
{
var newArray = new Array(array.length - 1);
for(var o = 1; o < array.length; o++)
newArray[o - 1] = array[o];
array = newArray
}
}
worst case scenario, with a length of 1000, this will result in:
for ( i = 0 to 1000 )
for ( o = i to 1000 )
recreate the array
so that would be 0.5million iterations and recreations of the array. While this could be fixed with either 1 iteration (dynamicly sized array) or with 2 passes (fixed sized array):
// dynamic size
var newArray = new Array();
for(var i = 0; i < array.length; i++)
{
if (array[i] != ....)
newArray.push(array[i]);
}
array = newArray;
// fixed size
var cnt = 0;
for(var i = 0; i < array.length; i++)
if (array[i] != ....)
cnt++;
var newArray = new Array(cnt);
for(var i = 0, o = 0; i < array.length; i++)
if (array[i] != ....)
newArray[o++] = array[i];
array = newArray;
and another simple optimization for your for loops (which obviously wont work if you modify the array in the for loop):
for(var i = 0, l = array.length; i < l; i++)
(yes, i am aware that some numbers may be off. but it gets the point across.)

Related

How to explain unexepctable perfomance in js-array loop?

Lets say we have an array of 200 000 elements for example...
Now we want to iterate it in different ways and check the fastest one. I've heard that if we will save array.length in variable before loop we will reduce execution time, so i tried the code below
let sum = 0
for (let i = 0; i < arr.length; ++i) sum += arr[i]
against
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) sum += arr[i]
But i got the same result as if in both cases js reads length value just once in the very beginning.
Then i decided to check, what if during loop we will change an array, removing last element.
let sum = 0
for (let i = 0; i < arr.length; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
against
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
So i expected that second case now should work faster because in first case js inevitably should check array.length each time and i was much suprised that it is not works faster but even slower - from 10 to 15 %. For me it is unexplainable. Any ideas?
Tests: https://jsbench.me/tfkefwjuw2
The problem is that
let sum = 0
for (let i = 0, l = arr.length; i < l; ++i) {
sum += arr[i]
if (i === 100) arr.pop()
}
is now incorrect, as it loops beyond the end of the array. The sum will be NaN in the end. The correct solution would have l = arr.length - 1 so that this doesn't happen.
Now why does it becomes so slow? Because array accesses in javascript are only fast (get compiled to a fast path with pointer addressing) when the element exists. When you miss, the code will get de-optimised. Since JSbench runs the same code multiple times, so even if the deoptimisation happens only at the last of 200000 iterations, the subsequent runs will be much slower.
See this talk for details, which even explicitly spells out "Avoid out-of-bounds reads" on one slide. In general, don't use non-idiomatic code to improve performance. i < arr.length is idiomatic, and JS engines will optimise it well.

How can one maintain multiple lists of array indexes whose values are used by a function that outputs an array of results?

Building a sparsely-connected neural network from scratch in Javascript.
I have a 2d array. Each object in the second dimension (array[x][y]) holds 2 arrays, starts:[] and ends:[]. Each object in these arrays holds value and weight. Ends are incoming connections. Starts are outgoing connections. So, when a "synapse" is created, it is stored in the starts array of the pre-synaptic neuron and the ends array of the post-synaptic neuron. (Neurons aren't actual objects, just cells in the array).
My current predict function:
predict(array, values){
var result = [];
if(values.length <= array[0].length){
for(var a = 0; a < values.length; a++){
for(var b = 0; b < array[0][a].starts.length; b++){
array[0][a].starts[b].value = values[a];
}
}
for(var c = 0; c < array.length; c++){
for(var d = 0; d < array[c].length; d++){
var sum = 0;
for(var e = 0; e < array[c][d].ends.length; e++){
sum += array[c][d].ends[e].weight * array[c][d].ends[e].value;
}
}
for(var f = 0; f < array[c][d].starts[f].length; f++){
array[c][d].starts[f].value = util.sigmoid(sum);
}
}
for(var g = 0; g < array[array.length-1].length; g++){
var sum = 0;
for(var h = 0; h < array[array.length-1][g].ends.length; h++){
sum += util.sigmoid(array[array.length-1][g].ends[h].weight * array[array.length-1][g].ends[h].value;
}
result.push(util.sigmoid(sum);
}
}
else{
log.add("### PREDICT ### Not enough input neurons. Afferent growth signal.");
}
return result;
}
This works fine if I'm looking for a boolean output (0 or 1). I, however, have multiple outputs and I only want to back-propagate through the chain of weights that is relevant to each output. I'm aware that the same weight will appear in multiple chains - this is something that I want to test. I've read somewhere that actual back-propagation is inefficient, but I think that it's required here.
I would like to avoid looping through the entire network again to find the chain if possible. Also, if you see anything wrong with my predict function, please let me know!
For some context, I've made a little game. The game has a player box and a target box. I would like to input playerX, playerY, targetX, and targetY. Outputs are Down, Left, Up, and Right (directions the player can move). So, if input is 1, 1, 5, 5 ... and the "Up" output node is "heaviest", I want to correct the weights in the chain that led to the Up output and reinforce the weights in the chain that led to the Down output.

Is this the right way to iterate through an array?

Here is the code in question:
var L1 = [];
var Q1 = [];
function populateListOne() {
var limit = prompt("please enter a number you would like to fill L1 to.");
for (i = 2; i <= limit; i++) {
L1[i] = i;
}
for (n = 2; n <= L1.length; n++) {
var count = 2;
if (n == count) {
var index = L1.indexOf(n);
L1.splice(index, 1);
Q1[n] = n;
count = count + 1;
}
for (j = 0; j <= L1.length; j++) {
if (L1[j] % 2 == 0) {
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
}
document.getElementById("demo").innerHTML = "iteration " + "1" + ": " + L1 + " Q1 = " + Q1;
}
I’m currently working on a homework assignment where I have to setup a queue. All is explained in my JSFiddle.
Problem description
Essentially, the part I’m stuck on is iterating through each instance of the array and then taking the value out if the modulus is identical to 0. However, as you can see when I run the program, it doesn’t work out that way. I know the problem is in the second for loop I just don’t see what I’m doing wrong.
The way I read it is, if j is less than the length of the array, increment. Then, if the value of the index of L1[j] modulus 2 is identical to 0, set the value of secondIndex to whatever the index of j is. Then splice it out. So, theoretically, only numbers divisible by two should be removed.
Input
A single number limit, which will be used to fill array L1.
L1 will be initialized with values 2, 3, ... limit.
Process
Get the starting element of array L1 and place it in array Q1.
Using that element, remove all values in array L1 that are divisible by that number.
Repeat until array L1 is empty.
You're going to have issues with looping over an array if you're changing the array within the loop. To help with this, I tend to iterate from back to front (also note: iterate from array.length - 1 as the length element does not exist, arrays are key'd from 0):
for(j = L1.length - 1; j >=0 ; j--)
For your first loop, you miss the elements L1[0] and L1[1], so I would change the first loop to:
L1 = [];
for(i = 2; i <= limit; i++)
{
L1.push(i);
}
In this section:
for(j = 0; j <= L1.length; j++){
if(L1[j] % 2 == 0)
{
var secondIndex = L1.indexOf(j);
L1.splice(secondIndex, 1);
}
}
you should splice with j instead of secondIndex.
Change L1.splice(secondIndex, 1); to L1.splice(j, 1);
Array indices and putting entries
You initial code used an array that was initialized to start at index 2. To avoid confusion, of what index to start at, start with index 0 and iterate until array.length instead of a predefined value limit to ensure that you go through each element.
The following still works but will be more of a headache because you need remember where to start and when you will end.
for (i = 2; i <= limit; i++) {
L1[i] = i; // 'i' will begin at two!
}
Here's a better way:
for (i = 2; i <= limit; i++) {
// 'i' starts at 2 and since L1 is an empty array,
// pushing elements into it will start index at 0!
L1.push(i);
}
Use pop and slice when getting values
When you need to take a peek at what value is at the start of your array, you can do so by using L1[0] if you followed my advice above regarding array keys.
However, when you are sure about needing to remove the starting element of the array, use Array.slice(idx, amt). idx specifies which index to start at, and amt specifies how many elements to remove beginning at that index (inclusive).
// Go to 1st element in L1. Remove (1 element at index 0) from L1.
var current = L1.splice(0, 1);
Use the appropriate loops
To make your life easier, use the appropriate loops when necessary. For loops are used when you know exactly how many times you will iterate. Use while loops when you are expecting an event.
In your case, 'repeat until L1 is empty' directly translates to:
do {
// divisibility checking
} while (L1.length > 0);
JSFiddle
Here's a complete JS fiddle with in-line comments that does exactly what you said.

Javascript loop infinitely up till a certain condition

// Contains a list of items in each set.
var sets[0] = [1,2,3,4,5,6,7,8,9],
sets[1] = [10,11,12,13,14,15,16,17,18],
sets[2] = [19,20,21,22,23,25,26,27]
// Contains the mins associated to each set item.
var setTimes[0] = [15,15,15,15,15,15,15,15,15],
setTimes[1] = [16,12,11,15,13,15,15,15,14],
setTimes[2] = [16,12,11,15,13,12,11,15,13]
I've got a set of arrays as given above. The sets array has a data set of values. This array can have n number of items in it. Ex, sets[n].
Each sets array has an equivalent setTimes array that has minutes stored in it. setTimes[0][0] is 15min and is the number of minutes for sets[0][0].
Given a set item(ex 12), I'd like to:
Find out which set array does the given number belong to? In our case, since 12 was the item, it belongs to sets[1].
Once I have this, I'd like to get the sum of all mins from the setTimes array for the current sets index and also the next index. In our case, that would be the sum of setTimes[1] and setTimes[2].
In the event we reach the end of sets array, I'd like to get the sum of the first set array.
For ex,
- if I pass 12, I'll need to get the sum of setTimes[1] and setTimes[2]
- If I pass 23, I'll need to get the sum of setTimes[2] and setTimes[0]
Here is the loop I've been thinking, would like to know if there is a better way of doing this.
function computeTotalMin(givenItem)
{
// represents how many sets to loop thorough. I need 2.
for (x = 0; x <= 1; x++)
{
for(i = 0; i < sets.length; i++)
{
// checking to see if i value is on the last index of the sets array.
if(i === sets.length - 1)
{
i = 0;
var item = sets[i].indexOf(givenItem);
if(item !== -1)
{
// Loops through all mins from setTimes[i] array
for(j = 0; j < setTimes[i].length; j++)
{
var total = total + setTimes[j];
}
}
}
}
}
}
You don't need two nested loops for continuing at the end. You should have a single loop that iterates the number of sets you're interested in (2), and has an index (starting at the one set you've found). Inside that loop, you'd make a modulo operation on the index; to get back to the start when you've reached the end. By only looping over the count, not the (resettable) index, you won't get into an infinite loop.
You also should divide your program in just those tasks that you've textually described (find this, then do that), instead of munching everything in one huge nested control structure.
function computeTotalMin(givenItem) {
var setIndex = -1;
for (; setIndex < sets.length; setIndex++)
if (sets[setIndex].indexOf(givenItem) > -1)
break;
if (setIndex == sets.length)
return null; // givenItem found in none of the sets
var sum = 0;
for (var count = 0; count < 2; count++) {
for (var i=0; i<setTimes[setIndex].length; i++)
sum += setTimes[setIndex][i];
setIndex++; // go to next set
setIndex %= sets.length; // which might be 0
// alternatively: if (setIndex == sets.length) setIndex = 0;
}
return sum;
}

add element to ordered array in CoffeeScript

I have a sorted array, which I add elements to as the server gives them to me. The trouble I'm having is determining where to place my new element and then placing it in the same loop
in javascript this would look like this
for(var i = 0; i < array.length; ++i){
if( element_to_add < array[i]){
array.splice(i,0,element_to_add);
break;
}
}
The problem is that in coffee script I dont have access to the counter, so I cant tell it to splice my array at the desired index.
How can I add an element to a sorted array in CoffeeScript?
The default for loop returns the index as well:
a = [1, 2, 3]
item = 2
for elem, index in a
if elem >= item
a.splice index, 0, item
break
You may want to do a binary search instead.
If you are using Underscore.js (very recommended for these kind of array manipulations), _.sortedIndex, which returns the index at which a value should be inserted into an array to keep it ordered, can come very handy:
sortedInsert = (arr, val) ->
arr.splice (_.sortedIndex arr, val), 0, val
arr
If you're not using Underscore, making your own sortedIndex is not that hard either; is's basically a binary search (if you want to keep its complexity as O(log n)):
sortedIndex = (arr, val) ->
low = 0
high = arr.length
while low < high
mid = Math.floor (low + high) / 2
if arr[mid] < val then low = mid + 1 else high = mid
low
If I understood you correctly, why not save the position, like so,
var pos=-1;
for(var i = 0; i < array.length; ++i){
if( element_to_add < array[i]){
pos=i; break;
}
}
if(pos<0)
array.push(element_to_add);
else array.splice(pos,0,element_to_add);

Categories

Resources