Objective-C vs JavaScript loop performance - javascript

I have a PhoneGap mobile application that I need to generate an array of match combinations. In JavaScript side, the code hanged pretty soon when the array of which the combinations are generated from got a bit bigger. So, I thought I'll make a plugin to generate the combinations, passing the array of javascript objects to native side and loop it there.
To my surprise the following codes executes in 150 ms (JavaScript) whereas in native side (Objective-C) it takes ~1000 ms.
Does anyone know any tips for speeding up those executing times? When players exceeds 10, i.e. the length of the array of teams equals 252 it really gets slow. Those execution times mentioned above are for 10 players / 252 teams.
Here's the JavaScript code:
for (i = 0; i < GAME.teams.length; i += 1) {
for (j = i + 1; j < GAME.teams.length; j += 1) {
t1 = GAME.teams[i];
t2 = GAME.teams[j];
if ((t1.mask & t2.mask) === 0) {
GAME.matches.push({
Team1: t1,
Team2: t2
});
}
}
}
... and here's the native code:
NSArray *teams = [[NSArray alloc] initWithArray: [options objectForKey:#"teams"]];
NSMutableArray *t = [[NSMutableArray alloc] init];
int mask_t1;
int mask_t2;
for (NSInteger i = 0; i < [teams count]; i++) {
for (NSInteger j = i + 1; j < [teams count]; j++) {
mask_t1 = [[[teams objectAtIndex:i] objectForKey:#"mask"] intValue];
mask_t2 = [[[teams objectAtIndex:j] objectForKey:#"mask"] intValue];
if ((mask_t1 & mask_t2) == 0) {
[t insertObject:[teams objectAtIndex:i] atIndex:0];
[t insertObject:[teams objectAtIndex:j] atIndex:1];
/*
NSArray *newCombination = [[NSArray alloc] initWithObjects:
[teams objectAtIndex:i],
[teams objectAtIndex:j],
nil];
*/
[combinations addObject:t];
}
}
}
... the array in question (GAME.teams) looks like this:
{
count = 2;
full = 1;
list = (
{
index = 0;
mask = 1;
name = A;
score = 0;
},
{
index = 1;
mask = 2;
name = B;
score = 0;
}
);
mask = 3;
name = A;
},
{
count = 2;
full = 1;
list = (
{
index = 0;
mask = 1;
name = A;
score = 0;
},
{
index = 2;
mask = 4;
name = C;
score = 0;
}
);
mask = 5;
name = A;
},

Generally when you have a performance problem, you should profile your app using the Time Profiler instrument.
In this case, I can see some likely problems.
First of all, you're doing this in your inner loop:
[t insertObject:[teams objectAtIndex:i] atIndex:0];
[t insertObject:[teams objectAtIndex:j] atIndex:1];
You're just inserting more and more objects at the beginning of your t object. You're never emptying it out. I'm pretty sure that's not what you want, and it's probably a performance problem too.
Second, you're sending a lot of messages unnecessarily. For example, you're extracting masks O(N2) times. You can optimize this by extracting all of the masks once.
NSArray *teams = [options objectForKey:#"teams"];
NSUInteger teamCount = teams.count;
int masks[teamCount];
for (NSUInteger i = 0; i < teamCount; ++i) {
masks[i] = [[teams[i] objectForKey:#"mask"] intValue];
}
NSMutableArray *matches = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < teamCount; ++i) {
for (NSUInteger j = i + 1; j < teamCount; ++j) {
if ((masks[i] & masks[j]) == 0) {
[matches addObject:#[teams[i], teams[j]]];
}
}
}
You're still doing O(N2) iterations, but you're doing a lot less work on each iteration.

One thing to note is that
[t insertObject:[teams objectAtIndex:i] atIndex:0];
causes all of the elements in t to be shifted each time it is used. That could significantly slow things down since you're doing that twice in a loop. It's probably better to use [t addObject: ...].
Also, the NSMutableArray may be getting resized unnecessarily. If you know roughly how large it will need to be, you can initialize it with a specific capacity:
NSMutableArray *t = [[NSMutableArray alloc] initWithCapacity: size];

Related

Javascript collision explanation for b-tree (Birthday Paradox)?

I want to create a b-tree for storing some data in local storage. I was thinking of using this to find the index of an ID in a sorted list.
If I index an array normally (i.e. to append like array[20032] = 123, what's the big-O of that in Javascript arrays?).
function sortedIndex(array, value) {
var low = 0,
high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
if (array[mid] < value) low = mid + 1;
else high = mid;
}
return low;
}
When I test this with random numbers, I get some collision and it exits before 10k.
for (i = 0; i < 10000; i++) {
var r = Math.random();
array[sortedIndex(array,r)] = r;
}
This exits after a certain time (I'm assuming because of a collision).
I'm thinking it's a birthday paradox kind of thing because the collisions seem to be more likely when the list is already populated (see graph link) (but no exception is thrown...).
I wanted to see the final length of the array after many iterations, I get distribution of final lengths that look like this:
sortedList = []
listLengths = []
for (j = 0; j < 100; j++) {
for (i = 0; i < 10000; i++) {
var r = Math.random();
sortedList[sortedIndex(sortedList,r)] = r;
}
listLengths.push(sortedList.length);
}
graph of final lengths of sorted array after 1-100 iterations of appending attempts
I honestly don't want to deal with this and would also appreciate some pointers on efficient localStorage libraries.
The problem is that you're not shifting all the old elements up when you insert a new element in the array. So you'll extend the array by 1 when the new item is higher than anything else, but just overwrite an existing element when it's less than or equal to the maximum element.
array.splice will insert and move everything over to make room.
array = [];
listLengths = [];
for (j = 0; j < 100; j++) {
for (i = 0; i < 100; i++) {
var r = Math.random();
array.splice(sortedIndex(array, r), 0, r);
}
listLengths.push(array.length);
}
console.log(listLengths);
function sortedIndex(array, value) {
var low = 0,
high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
if (array[mid] < value) low = mid + 1;
else high = mid;
}
return low;
}

java 8 nashorn engine is really slow

I am running the following javascript test
var mark = java.lang.System.nanoTime() / 1000000000.0;
for(var i = 0; i != 1000; i++) {
}
var now = java.lang.System.nanoTime() / 1000000000.0;
var e = now - mark;
print(1 / e);
and get this result
27.361456496425802
this seems really slow almost a bug or something i am doing wrong. Here is the java code
try {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
String[] lines = IO.readAllLines(IO.file(root, "LoadList.txt"));
String script = "";
for(int i = 0; i != lines.length; i++) {
String file = lines[i].trim();
if(!file.startsWith("#") && file.length() != 0) {
script += IO.readAllText(IO.file(root, file));
}
}
engine.eval(script);
} catch(Exception ex) {
ex.printStackTrace();
}
does anybody know what i am doing wrong or have seen this type of problem and know how to fix if?
Thanks in advance
after doing some research it appears that java's javascript takes a little while to warm up to speed. here is an update to the test
for(var i = 0; i != 1000; i++) {
var mark = java.lang.System.nanoTime() / 1000000000.0;
for(var j = 0; j != 1000; j++) {
}
var now = java.lang.System.nanoTime() / 1000000000.0;
var e = now - mark;
print(1 / e + " fps")
}
and by the last iteration i get 99108.03064699778 fps. (it does vary the lowest one i got, on the last iteration, was around 50000 fps)

Why is my code executing far more times than it's supposed to?

I'm currently working on a poker odds generator and it's pretty much done, except for one thing. The program runs far more often than it should. I know that this is coming from the compare() function, because when I add a variable Q to keep track of the number of times the main function playPoker() has run, it produces a huge number until I comment it out - at which point it returns the exact number I'd expect.
Can any of you point out where I'm going wrong with this. I can't see why one function should lead to Q being incremented so much more than it should be. Literally, the last time I ran it the number was (32,487 instead of 100). I present the code below (without the poker hand-checking functions because they're not really important). Why is playPoker() running so many times?!
var Q = 0;
function playPoker(tableSize) {
//Create the players, the deck and the card table which stores the 5 cards the players have in common
var players = createPlayers(tableSize);
var deck = createDeck();
var cardTable = new CardTable();
//Deal each player two cards
for (i = 0; i < 2; i++) {
for (j = 0; j < players.length; j++) {
deal(deck, players[j]);
}
}
//Put five cards down on the table
for (k = 0; k < 5; k++) {
deal(deck, cardTable);
}
//Check for various winning hands here for each player
for (m = 0; m < players.length; m++) {
//Merge the player's two cards with the five cards on the table
var subjectCards = (players[m].cards).concat(cardTable.cards);
//Make an array of the values of each of the seven cards, which will be used to determine 4 of a kind, 3 of a kind and pairs
var valuesInOrder = getValuesInOrder(subjectCards);
//Create a dummy array, so that valuesInOrder remains unchanged
var straightValues = valuesInOrder.slice();
//Remove any duplicate card, meaning that the array contains only unique values (i.e. 2, 4, 5, 7, K ... NOT 2, 2, 2, 8, K, K, A)
var straightValues = straightenUp(straightValues);
//Calculate how many pairs are in the hand
var numPairs = howManyPairs(valuesInOrder, straightValues, players[m]);
//Find out whether the 5 table cards contain three cards of the same suit. If not, then a flush is impossible.
var flushPotential = threeSameSuit(cardTable.cards);
//Find out which hand each player has (i.e. straight, 3OAK, pair)
checkPokerHand(subjectCards, straightValues, valuesInOrder, flushPotential, numPairs, players[m])
}
var rankedPlayers = compare(players);
//return players;
Q++;
return Q;
}
And here's the for-loop that sets it off.
for (z = 0; z < 100; z++;) {
playPoker(4);
}
And here's the compare() function:
function compare(players) {
var remPlayers = players.slice();
var rankings = [];
var potentialWinners = [];
//Collect all the players' rankings in an array
for (i = 0; i < remPlayers.length; i++) {
rankings.push(remPlayers[i].rank);
}
//Find the highest ranking
var highestRank = getHighestValue(rankings);
//Move any players with the highest ranking to an array for potential winners
for (j = 0; j < remPlayers.length; j++) {
if (remPlayers[j].rank == highestRank) {
potentialWinners.push(remPlayers[j]);
remPlayers.splice(j, 1);
j--;
}
}
//With all potential winners gone, mark all other players with an L for losing.
for (k = 0; k < remPlayers.length; k++) {
remPlayers[k].result = 'L'
}
var foundWinner = false;
if (potentialWinners.length < 2) {
potentialWinners[0].result = 'W';
foundWinner = true;
}
//Check there is more than one potential winner. If not, the only guy in the array has won.
if (!foundWinner) {
//Loop through all players first cards and find the highest value, then delete any who don't have that, marking them with 'L'.
//If there is no single remnant, do the same for second cards, then third, then fourth, etc.
for (p = 0; p < 5; p++) {
var subRankings = [];
for (q = 0; q < potentialWinners.length; q++) {
subRankings.push(potentialWinners[q].bestHand[p]);
}
var highestSubRanking = getHighestValue(subRankings);
//Mark 'L' and remove any player who does not meet the highest subranking
for (m = 0; m < potentialWinners.length; m++) {
if (potentialWinners[m].bestHand[p] < highestSubRanking) {
potentialWinners[m].result = 'L';
potentialWinners.splice(m, 1);
}
if (potentialWinners.length < 2) {
potentialWinners[0].result = 'W';
//Set this flag to true to break the loop because a winner has been found
foundWinner = true;
break;
}
}
//Break the loop if we have found a winner
if (foundWinner) {
break;
}
//If we still haven't found a winner by the end of the 5th loop, all remaining players draw
if (p == 4) {
for (z = 0; z < potentialWinners.length; z++) {
potentialWinners[z].result = 'D';
}
}
if (foundWinner) {
break;
}
}
}
return players;
}
Try using var declarations on your variables to manage their scope within their relevant functions?

Filling up a 2D array with random numbers in javascript

I'm really sorry if anything like this has been posted here before but I couldn't find anything, I'm kinda new to the site still!
So for a while now I've been learning a bit about game development through html5 and javascript and I stumbled upon making tileset maps, I now have a tileset and an 2D array that I want to put certain tiles in (the number varies between 6 and 10 in this case).
I figured it could be a cool function to make the map choose between a small set of similar tiles so I don't have to specifically number every tile in the array(just define the type)
The method I have currently is probably the best for being able to define types but I want something that looks a bit cleaner and/or information to why my "cleaner" version dosen't work.
var ground = [
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()],
[tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile(),tile()]];
function tile() {
var y = (Math.random() * 5 | 0) + 6;
return y;
}
This is the code I've been using so far, I have to edit every element of the code with the tile() function to get a random number in each one, what I wanted to have was something like this:
for (var i = 0 ; i < 15; i++) {
for (var j = 0; j < 9; j++) {
ground[[i],[j]] = (Math.random() * 5 | 0) + 6;
}
}
to fill the array without having to add the function to each spot.
I have a feeling that I'm missing a return function or something along those lines but honestly I have no idea.
You were thinking in the right direction but there are some errors in your code ;)
You have to initialize the array first before you can push elements into it.
And you were counting i++ twice
Javascript
var ground = []; // Initialize array
for (var i = 0 ; i < 15; i++) {
ground[i] = []; // Initialize inner array
for (var j = 0; j < 9; j++) { // i++ needs to be j++
ground[i][j] = (Math.random() * 5 | 0) + 6;
}
}
Maybe even better (reusable)
function createGround(width, height){
var result = [];
for (var i = 0 ; i < width; i++) {
result[i] = [];
for (var j = 0; j < height; j++) {
result[i][j] = (Math.random() * 5 | 0) + 6;
}
}
return result;
}
// Create a new ground with width = 15 & height = 9
var ground = createGround(15, 9);
Here's a quick example. I've created a function that will take in a width and height parameter and generate the size requested. Also I placed your tile function inside generate ground to keep it private, preventing other script from invoking it.
var ground = generateGround(10, 10); //Simple usage
function generateGround(height, width)
{
var ground = [];
for (var y = 0 ; y < height; y++)
{
ground[y] = [];
for (var x = 0; x < width; x++)
{
ground[y][x] = tile();
}
}
return ground;
function tile()
{
return (Math.random() * 5 | 0) + 6;
}
}
http://jsbin.com/sukoyute/1/edit
Try removing the comma from...
ground[[i],[j]] = (Math.random() * 5 | 0) + 6;
...in your 'clean' version. Also, your incrementing 'i' in both for loops:
for (var i = 0 ; i < 15; i++) {
for (var j = 0; j < 9; i++) {
Hopefully these changes make it work for you :)

Why is a custom array reverse implementation twice as fast versus .reverse() in V8

A simple implementation for reversing an array is twice as fast compared to the built in function in Javascript, when tested in Chrome. What's V8 doing? Here is the test:
var newArr = [];
var newArrDefault = [];
for(var i = 0; i < 10000000; i++){
newArr[i] = i;
newArrDefault[i] = i;
}
var startDefault = new Date();
newArrDefault.reverse();
console.log("Built in method took " + (new Date().getTime() - startDefault.getTime()));
var start = new Date();
for(var i = 0; i < newArr.length / 2; i++){
var tmp = newArr[i];
newArr[i] = newArr[newArr.length-i-1];
newArr[newArr.length-i-1] = tmp;
}
console.log("Custom method took " + (new Date().getTime() - start.getTime()));
Results on Version 20.0.1132.47 Ubuntu 12.04 (144678):
Built in method took 149
Custom method took 71
For the fun of it, I implemented the specification like so:
var upper, upperExists, lowerExists, lowerValue, upperValue;
for(var lower = 0, len = newArr.length >>> 0, middle = Math.floor(len / 2); lower != middle; ++lower) {
upper = len - lower - 1;
lowerValue = newArr[lower];
upperValue = newArr[upper];
lowerExists = newArr.hasOwnProperty(lower);
upperExists = newArr.hasOwnProperty(upper);
if (lowerExists && upperExists) {
newArr[lower] = upperValue;
newArr[upper] = lowerValue;
} else if (upperExists) {
newArr[lower] = upperValue;
delete newArr[upper];
} else if (lowerExists) {
newArr[upper] = lowerValue;
delete newArr[lower];
}
}
The jsperf can be found here.
It includes a whole bunch of code to deal with missing entries, which is why it's so much slower than both the native and your code (some optimizations may be possible, but it won't affect the performance enough). The performance difference between your code and the native implementation wasn't very conclusive though.
Under most circumstances arrays are a contiguous block of values with no gaps in between, so you should be safe with that kind of code; as long as you know the difference :)

Categories

Resources