visionvortex.de

There's that point in software development, where the features are totally great, but the execution is still a "bit" shoddy. And colossally slow. And you try to crank the map size up to a thousand units square and you are like "Yeah, that'll be so awesome" and Unity is totally like... dead. Just why? Hwhyyyyyyy!?!

Being an artist I didn't ever want to need to think about performance optimization. And memory management. I didn't want to know what the heap is. And what the stack. And why there's a difference between the both. And quite frankly, I'm not sure If I got that into my brain already. But anyway. I was fortunate enough to being able to get my hands dirty with scripting a few years ago. So that's when I started to get into programming. That's when slowly the beauty of concepts like performance optimization unfolded. So Monday was that day where I thought to myself "You really need to start to clean up that mess". Allright. I tackled the small things first. Merged a few classes and cleaned up the errors when they arose. Until all went fine and dandy again. So, that's where the messy part started...

 Let's throw something in here. I departed from using Mono Develop some time ago. I moved to Visual Studio Community Edition with the Tools for Unity and was happy ever after. Really. Using this package made refactoring classes a breeze. Just refactoring classes didn't solve the issues with PMap. Those were:

  • Slow Performance
  • Extreme Memory usage
  • Stack overflows on large map sizes
  • Unity just closing down when crossing certain map size thresholds.

And seriously, I had no idea whatsoever where to start. So I started to read into things and found a great article about C# memory and performance tips for unity. This article made my day. And week, for that matter. The first thing I did was remove any foreach loop over a List and replaced it with an iterator access. This did wonders already. But Unity kept dying on my. Why? After opening the profiler (which i totally haven't done like ever) I saw really huge memory spikes. To get rid of those I removed my old sampler. Err... Sampler? What's bloody that?

Right. So, a Sampler is a class that I used do read and, behold, store data from my map data. The store part was the huge problem. A map Size of 1000 * 500 makes 500.000 cells to process. Some walkers need to sample a disc twice. In the worst case for every cell. Since PMap works in discrete increments a disc is essentially 8 values, times 2, 16 values times 500.000 makes 8 million data objects. After calculating this trough I was like "holy shit!". And rightfully so. In addition to that I had tons of occurrences, where I used Lists to conveniently store temporary data (like a copy of the whole map). Which isn't such a good idea with large datasets. So my plan looked like this:

  • Replace Lists with arrays wherever possible
  • Reuse Lists wherever possible
  • Size Lists to a discrete size, wherever possible

Doing all this did, again, give a huge boost. But as I said, the sampling mechanism was inherently broken. So it was time to kill the old sampler. To get back to the title of this article, I was knee deep in guts, at that time. No way anything would really compile in Unity. Huge fun! Anyway, back to business. The Sampler. My old implementation stored data internally. This was a huge mistake. Another thing was, I didn't reuse Samplers. So I wrote a class that can, after being instanced, passed a x and y value for it's position and a reference to a PMapGrid to sample from. It then would read from the map directly on query and not store any data. If I need it again, set x, y and PmapGrid and BÄM, good to go. The class internally looks like an typewriter exercise. No convenience methods are used to make this thing work. The code is as simple and as efficient as possible. Which often times aligns to lower readability and more lines. So while the solution for the Sampler is not totally elegant, it internally queries arrays in an intricate manner to expose convenient properties to work with from all the other classes.

Implementing this and moving all classes, especially the walkers, over to the new sampling scheme took a lot of work. But it was so absolutely worth it. Unity didn't crash anymore. The memory footprint is way lower and the speed gain is really great. Along the way i removed any if statements that needn't to be there. They also slow things down. I removed most safeguard checks that guard against wrong class handling. The internals of PMap work relatively clean. And only the top end classes are meant to be extended. So only there the safeguards make sense and are really necessary. Besides, it's oftentimes better to fail fast as you will get to your bugs earlier (I know this is subject to discussion and religion among coders, but hey, I'm an artist ;D ) PMap can now generate really huge maps (4k anyone?). It will just take some time.

So, that's what I spent time with for the last few days . The framework is up and running again. I already optimized some walkers to be much more efficient. Even with map sizes beyond 1k. I have a few walkers left to optimize before finishing up the SnippetStamper. After that one other walker remains, that I plan on adding before release. That would be the ValuePlacer. I'll keep you updated on that one. Another thing I absolutely must rework is the data visualizer. At the moment I am using unity GUI elements to draw the numbers. Which is slow. Like, you can't scroll the UI beyond 800 cells. So that must go away. I'll probably paint colored pixels into a map and draw this out. I've a few god ideas there and will be sure to keep everyone updated.

That's it for this week. As always, if you want to know more, drop me a line or nudge me on twitter!

Back to Top

(c) visionvortex.de 2017