Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / Optimization of Chunk Loading

Author
Message
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 26th Jul 2011 03:37 Edited at: 26th Jul 2011 03:40
I've (finally) gotten my chunk based loading system working, but I've found that it's not nearly as optimized as it would need to be. Using the code I have now there is a very noticeable one second pause whenever a chunk update needs to be done (moving from one chunk to another). This is a pretty big concern for me since as of right now my game only has to create objects for one of four layers used in the world data array, so the problem will only get worse as I continue to work on the game. For a bit of background, I'm using object plains in a 2d game with a "platformer" view.

I'm hoping to get some advice on what I can do to optimize my code. I've managed to narrow the primary cause of the slowdown to two functions: the ones used to create and delete tiled objects. I have a few ideas on what could be done:

1. There are several places where I use nested IF/ELSE statements that would also work as SELECT/CASE commands. Which of the two is faster, or is there any difference?

2. The problem may be inherent to the fact that I create an individual object for every single tile. I don't know if there's a better way to do this; however, seeing as the world data is random and can be changed at any time by the player I kind of doubt there is. Is there anything better than object plains to represent 2d data?

3. Would it be possible to stagger how many objects are loaded in a single "step" of the program? Right now I believe it tries to create/delete them all at once, so the program has to pause for a bit so it can finish. I don't have any experiencing implementing something like this so tutorials would be awesome!


If someone wants to take a look at the code that's causing the slow down and provide some optimization tips I'd much appreciate it, especially since I can carry on the knowledge to the rest of my code

Delete tile objects


Create tile object




I do have a final random question while I'm at it...

At this point I plan to save all of my map data in one huge array, possibly as large as 16,000 x 8,000 x 4. This is because I've found file loading with Darkbasic Pro to be exceptionally slow, though it may just be a problem caused by my coding. Are there any inherent downsides to having such massive amounts of data in an active array at once?
SJPro
23
Years of Service
User Offline
Joined: 3rd Jul 2003
Location:
Posted: 26th Jul 2011 13:05
Instead of creating and deleting the objects you can create a set number of tiles on the first load. Then keep track of which are 'in use' (instead of creating and deleting) and then just re-texture and position as needed.

To stagger the load you can put a cap on how many object are loaded per loop. This may results in slow-down for a brief moment depending on how it is constructed. Another point worth considering it the optimal size of chunks to load.

www.freewarehub.net
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 26th Jul 2011 21:22 Edited at: 26th Jul 2011 21:22
@SJPro

Thanks for your feedback

Moving/retexturing the objects instead of creating/deleting them is a fantastic idea; I'll definitely take some time to work out how I could implement it. Concerning the staggering of how many objects are loaded per loop, do you have a rough code example of how something like that could be implemented? It's the first time I've had to use this sort of thing and can't quite wrap my head around it. Would I simply use a timer? To your last point, my chunks used to be 32 x 32 tiles wide, which I found to be unnecessarily large. I changed it to 16 x 16 which works much faster than the other size, and I don't think I can go much smaller since players would start seeing chunks that haven't loaded yet.
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 26th Jul 2011 21:25
My initial advice: Measure first, then modify. Do you know and can
you prove which pieces of code need to be optimised?

However, here are a few first impressions/obvious points:

* Change your arr_Instance_MasterList to be sorted by x, then y, then z and replace your linear search in your delete function with a binary search. Over 1000 items, insertion will be slower (ave 10x current), but deletion will be faster (ave 50x current).

* Don't use very high object numbers (you can pick from 2,999,999!), and start with your highest object id first.

DBPro uses an array internally for objects which get reallocated when required - creating object 1,000,000 will create an array with that many elements, which can take some time. If you have objects 1 to 5 and create object 6, a new copy of the array is created with enough space for 6 object, then again for the 7th object etc.

Instead, if you know that you won't create more than 1000 objects, allocate object 1000 first, then any other objects with lower object id's - the
initial array will be sized to hold 1000 objects and allocated once, but then no more allocations will take place as you create the rest.

* When looping over objects, don't recheck objects that you checked last time and that you know are almost guaranteed to still exist.

* Instance objects instead of creating them from scratch. Make up an example of each of your plains and exclude them from rendering (EXCLUDE OBJECT ON). Then instead of creating a new object for each plain, use INSTANCE OBJECT to create them instead from your example object. The gain from this may be minimal, or even nothing at all though.

Some of those suggestions can be replaced by the use of plug-ins, but I saw in your last post that you were trying to do without so I'll say no more.

SJPro
23
Years of Service
User Offline
Joined: 3rd Jul 2003
Location:
Posted: 27th Jul 2011 16:43
Just re-read your post properly and realised that you narrowed it down to your whole create/delete functions that are slow. I mistook it to mean just the create/delete object functions are slowest part so my suggestion of retexturing and moving may have little to no effect.

www.freewarehub.net
Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 27th Jul 2011 22:05 Edited at: 27th Jul 2011 22:05
@IanM

Thanks for your reply!

* The only testing I know how to do is personal observation. I imagine there are ways to get actual data of what's faster/slower but I don't have any experience using something like that

* I'm not familiar with binary searches or how I would sort the data in arrays based on a particular value. The only mention of binary searches I can find is in this topic: http://forum.thegamecreators.com/?m=forum_view&t=144346&b=1. Reading through it I've surmised that the code below is an example of a binary search (specifically the FOR/NEXT loop). Would I just need to change my code so the FOR/NEXT loop goes through every single element in arr_Instance_MasterList and then use three IF/ENDIF commands to compare the X/Y/Z values? Then again the way I've described it sounds pretty much exactly like what I'm doing now, except I'm using a REPEAT/UNTIL loop instead of FOR/NEXT.



* Huh, now I know why exactly it's better to have lower numbers for arrays! Time for some calculations to figure out how many objects I'll realistically have at once.

* I'm not quite sure what you're referring to concerning rechecking objects that I checked last time. The only place I can think of right now where that might happen is the code to find an unused object number. Could you clarify this bit?

* I'll definitely give that a try, though I do wonder if I'd get better performance form SJPro's suggestion. Time for more testing


@SJPro

At the time of posting I had only checked my user made function to make sure it was the creation/deletion of objects that was causing the slow down, rather than the other bits involved (such as figuring out which chunks needed to be loaded/deleted, etc). Since then I've tested it further and found that it is in fact the Darkbasic commands that are causing most of the hangup. I haven't had time to test your idea of just moving/re-texturing objects, though I plan to along with Ian's suggestion of using INSTANCE OBJECT.
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 27th Jul 2011 23:47 Edited at: 27th Jul 2011 23:50
That binary search code is deceptive, which is what makes it look like the linear search you've already written - you've seen the for/next loop, but it's the bits in the middle that are important.

Your search starts at the beginning of the array and steps forward until it finds a match - in 1000 items, where the item you are searching for exists, you will average 500 checks per deletion.

Binary search is a relatively simple concept:
You start with a sorted array, and 2 markers - one marker indicates the first item (let's call it 'low'), and the other the last item ('high').

You calculate a point in the exact middle of those two markers, and determine whether the item you are looking for is below that point, or above that point, or an exact match. If's it's an exact match, then you are done. If it is in the lower half, you reset 'high' to the middle point, otherwise you reset 'low' to the middle point - repeat until your low and high points match, and that's the insertion point.

In 1000 items, you will find an exact match in 10 or less checks.

Here's an example using strings:


Quote: "rechecking objects"

I mean exactly what you've stated. Using a random starting point, and then checking from there is a reasonable thing to do - in fact my own plug-ins did the same thing until I came across a much better method (which you can't duplicate easily).

The point is that if instead of starting from a random point each time (in your hopefully massively reduced range of object id's), it's actually better to NOT use a random start point, but instead to remember where you stopped searching last time and continue from that point, wrapping back to id 1 when you hit the end of your range (if you impose that kind of limit).

The standard function I see, pretty much all of the time, is this:


Calling this code when you have 0 objects and are creating 1000 objects will result in the OBJECT EXIST() function being called 500,500 times.

A better method is this:

... which follows the method I described earlier. Using the pattern of 'load everything, delete everything, repeat', it will tend to find a free object id on the very first call of OBJECT EXIST(). Even when randomly deleting items, it will eventually hit runs of free objects, and then revert to single OBJECT EXIST() calls.

Of course, the very best is the FIND FREE OBJECT() function from my plug-ins

Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 28th Jul 2011 21:00 Edited at: 28th Jul 2011 21:15
@IanM

Thanks for clearing up what a binary search is. It sounds like quite a neat tactic, I'm sure I'll find plenty of uses for it later on as well. Is there a general rule of thumb for when it's better to use a binary search vs. a linear search?

There are two parts I'm a little confused on with them. The first is how I would go about ensuring the array in question is sorted, and then what exactly do I compare (in your example you have "Target"). Do I simply combine the X, Y and Z values into a single value (10, 10, 5 would become 10105)?


I tried implementing the new system to find an object ID but have run into some issues. In your version you make the ID search code a separate function, but I was hoping to have it right in with the rest of the object creation code. Am I right in believing that using a FOR/NEXT loop in this situation wouldn't be as efficient as a REPEAT/UNTIL loop? I've ended up with this conclusion since a FOR/NEXT loop has to keep going even if a usable object ID has been found already.

The second bit is that the instances are much darker than the object itself for some reason. I've attached a screenshot of the comparison. Any idea why this might be happening?


EDIT: Concerning sorting arrays it occurred to me that I could use a FOR/NEXT loop to first sort by X, then Y and then Z. If X in position 1 of the array is greater than in position 2, flip the two. This doesn't sound like an efficient way to do it though (would probably be better off just using a linear search since I'd have to resort it for every now object that gets added).
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 28th Jul 2011 23:53
If you check out the code I posted, you'll see that when I want to insert an item into the array I simply do a binary search to find the insertion point, then use ARRAY INSERT AT ELEMENT to insert at that point - no sorting required, as the array is built sorted automatically!

Comparing multiple fields from UDT's for this kind of purpose is actually simpler than you think - what you do is compare the first field in your first UDT with the same field in your second UDT to see whether it's less than, equal to, or greater than that second field. If it's 'less than', or 'greater than', then you already know which one of the two comes first. If it's 'equal to', then you carry out the same comparison with the second field, and the third, and so on.

Here are two examples. The first, is a straight in-line comparison:


... and here's the equivalent and IMO neater code in a function:


Quote: "Is there a general rule of thumb for when it's better to use a binary search vs. a linear search?"

Algorithmically, no - a binary search will always be at least as fast, on average, as a linear search.

Quote: "but I was hoping to have it right in with the rest of the object creation code"

Why?
It's far more useful as a function, simplifies the code that calls it, and not too much slower.

Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 2nd Aug 2011 01:11 Edited at: 2nd Aug 2011 01:12
Sorry for the delayed reply, ran into a problem and have been tinkering with the code for the past few days to see if I can sort it out. No luck yet sadly. When I move in a positive X or Y direction the chunks do delete themselves, with some random objects left over if I'm moving in a positive Y direction from the first chunks. Exporting the data in arr_Instance_MasterList shows that everything is ordered perfectly correct: X first, Y second, Z last.

Is there anything obviously wrong with my binary search code?

Binary search while creating an object.


Binary search while deleting an object.
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 2nd Aug 2011 01:29
Quote: "Is there anything obviously wrong with my binary search code?"

Yes, you aren't resetting the 'var_Comparison_Flag' to zero when you start your comparisons. Don't forget to fix it in both places, or as I suggested, wrap it into a function so that there's only one copy to get working - this kind of optimisation at this point is just slowing you down.

Apart from that, you haven't shown what you do when an exact match is made after the loop exits (var_Comparison_Flag = 0), or if a match isn't made (var_Comparison_Flag = -1 or 1).

Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 4th Aug 2011 06:46 Edited at: 4th Aug 2011 06:46
Ah good catch, thanks for that. As to what happens when a match is/isn't made I thought the WHILE/ENDWHILE loop took care of that? In the binary search code you provided (part of it is below) it looks like the only way the program knows when it's found an exact match is when High - Low > 1. The program keeps going until an exact match is made, at which point "High" is the value of the exact match.




I've used that same check in my program, and the series of IF/ELSE statements comparing whether it's higher or lower is there as a more complex version of "if SearchArray(Middle) < Target." Thinking about it now I realize I don't need half of the IF/ELSE statements since the only thing I want to know is if the middle is lower than the exact match.

After the program exits I run the rest of the object creation/deletion code, since I assume that it will only exit the WHILE/ENDWHILE loop if an exact match has been found. In the case of object deletion (where the errors are appearing) the next thing to run is the DELETE OBJECT command. In the case of object creation it's just a little more complicated since I need to check if the "high" value is actually one higher than the current array length. Here's that code.




Am I misunderstanding your binary search code and how it functions, or is my implementation simply incorrect?
IanM
Retired Moderator
23
Years of Service
User Offline
Joined: 11th Sep 2002
Location: In my moon base
Posted: 4th Aug 2011 23:12
Quote: "I thought the WHILE/ENDWHILE loop took care of that?"

It depends - if you insert unique data each time, then yes. If you update existing items in the array, or want to delete the item you locate, then you need a final check to see if you should update/delete or insert. I haven't taken the time to fully understand what you are doing, so I can't say.

If you look in the full example code of binary search/insert/update, you'll see that there's a final check if the program has selected 'not nearest' (or exact match if present) that checks for inequality before returning the result - perhaps I should have named it 'exact' and reversed the meaning of the flag.

Alaror
15
Years of Service
User Offline
Joined: 9th May 2011
Location:
Posted: 5th Aug 2011 20:32 Edited at: 5th Aug 2011 20:33
Alright, thanks for clearing it up.

I'm not sure what went wrong with my early tests but I did some new ones and found that the data is in fact not being inserted properly in the creation function. Everything is being sorted by Y instead of X. I tried reversing the position of X and Z in the code that compared whether the values were less than var_Array_Mid (so that Z was compared first, then Y, then X), but it didn't seem to change anything.

My first inclination is that when arr_Instance_MasterList(var_Array_High).atr_PositionX = var_PositionX, I should somehow adjust the values of Low and High so that Low is the first instance of that specific X value appearing and High is the last instance of it appearing. For example, if I'm looking for the value X = 1000 and Y = 10 in an array that has ten values--the X of 1 through 4 equaling 1000 and 5 through 10 equaling 2000--then it should change low to position 1 and high to position 4. The next time around (while searching for the Y value) it should only check positions 1 through 4. Since we want something that has X = 1000 and Y = 10 there's no need to check 5 through 10, seeing as the X value for all those is 2000.

The only way I can think of finding the new Low and High value is that once we've found a place where X is equal to the value we want we do two linear searches from that position. To find the Low value we loop Current_Value - 1 until it no longer equals the X value we want. To find the High value it's the same except Current_Value + 1.

Am I making it more complicated then it needs to be? Is this essentially what your code below achieves?

Login to post a reply

Server time is: 2026-07-10 13:44:15
Your offset time is: 2026-07-10 13:44:15