Optimizing Generation

Started by
31 comments, last by Pink Horror 7 years, 11 months ago

There is yet one more problem. The last laggy piece of code is


toLoad.Sort(delegate (Vector4 first, Vector4 second) { return first.w.CompareTo(second.w); });

Is there a faster way to sort objects? This one line is generating 50% of my lag.

I'm making an open source voxel space game called V0xel_Sp4ce! Help me out with the repository here: https://github.com/SpikeViper/V0xel_Sp4ce

Advertisement

Also, you'll probably want to invert your for loop order, and do z, y, x, it's more memory friendly. If you are wondering why, imagine if you had a single array that was of length 16*16*16, and think of how you're jumping around in it as you travel through your innermost for loops.

This is always a good place to look, but OP's code seems consitent in that X is both the outside loop and the most-significant ordinal (array indexer?). In other words, the X and Z "labels" are consistently swapped, but they don't seem to be mismatched in the way that usually causes the cache to be thrashed. The speedup seen was likely entirely due to getting rid of sqrt() -- or I'm not reading the code with the comprehension I think I am :)

throw table_exception("(? ???)? ? ???");

Also, you'll probably want to invert your for loop order, and do z, y, x, it's more memory friendly. If you are wondering why, imagine if you had a single array that was of length 16*16*16, and think of how you're jumping around in it as you travel through your innermost for loops.


This is always a good place to look, but OP's code seems consitent in that X is both the outside loop and the most-significant ordinal (array indexer?). In other words, the X and Z "labels" are consistently swapped, but they don't seem to be mismatched in the way that usually causes the cache to be thrashed. The speedup seen was likely entirely due to getting rid of sqrt() -- or I'm not reading the code with the comprehension I think I am :)
That part of the code is great now - I even added a chunk pooling system for less garbage collection. My remaining problem is the list.Sort function, as stated above. If you know any faster way to sort it (or better type of data to use) that would be great!

I'm making an open source voxel space game called V0xel_Sp4ce! Help me out with the repository here: https://github.com/SpikeViper/V0xel_Sp4ce

What I would actually recommend is combining those first two 3-axis loops. There's a few things you're doing here that seem strange.

Firstly, in your first loop (x, y, z) you test for null objects before assigning each index a new BlockEmpty() -- since you just allocated 'blocks' they should all be null. Since you don't ever revisit any array element, you always test, always find null, and always assign a new BlockEmpty().

Secondly, now in your second loop (tx, ty, tz), you've already gotten good advice to eliminate the sqrt(), and to hoise out the repeated expression -- and you've probably converted to a switch statement -- if you haven't, you should. The inefficiency here is that you're replacing a lot of those BlockEmpty()'s you just assigned into 'blocks' with these new BlockCore()s, BlockBedrock()s, and BlockStone()s -- BlockEmpty()'s that get replaced are never used, they just take time to created, and leave pressure on the garbage collector when you replace them.

So, by combining those two loops and making BlockEmpty() the default case, you can avoid creating any extraneous Blocks currently in those first two loops, plus the code will be simpler.

taking that even further, you overwrite some of those blocks later with cracks, caves, and ore. A possible further refinement would be to place empty (due to crack or cave) and ore blocks into 'blocks' array first (above the loop I recommend combining) and then add a test in the combined-loop just around the switch so that new blocks (from the switch) are only placed where the array element is currently null (that is, not already made a crack, cave, or ore).

Allocating instances of blocks and also iterating over the blocks array is probably the most expensive things you're doing here I would hazard a guess (surely now that the sqrts are eliminated), so reducing those as best you can is probably going to give best results.

All that said, if its working at acceptable speed now, its not always a good thing to keep rat-holing yourself over optimizing this bit of code.

throw table_exception("(? ???)? ? ???");

What I would actually recommend is combining those first two 3-axis loops. There's a few things you're doing here that seem strange.

Firstly, in your first loop (x, y, z) you test for null objects before assigning each index a new BlockEmpty() -- since you just allocated 'blocks' they should all be null. Since you don't ever revisit any array element, you always test, always find null, and always assign a new BlockEmpty().

Secondly, now in your second loop (tx, ty, tz), you've already gotten good advice to eliminate the sqrt(), and to hoise out the repeated expression -- and you've probably converted to a switch statement -- if you haven't, you should. The inefficiency here is that you're replacing a lot of those BlockEmpty()'s you just assigned into 'blocks' with these new BlockCore()s, BlockBedrock()s, and BlockStone()s -- BlockEmpty()'s that get replaced are never used, they just take time to created, and leave pressure on the garbage collector when you replace them.

So, by combining those two loops and making BlockEmpty() the default case, you can avoid creating any extraneous Blocks currently in those first two loops, plus the code will be simpler.


taking that even further, you overwrite some of those blocks later with cracks, caves, and ore. A possible further refinement would be to place empty (due to crack or cave) and ore blocks into 'blocks' array first (above the loop I recommend combining) and then add a test in the combined-loop just around the switch so that new blocks (from the switch) are only placed where the array element is currently null (that is, not already made a crack, cave, or ore).

Allocating instances of blocks and also iterating over the blocks array is probably the most expensive things you're doing here I would hazard a guess (surely now that the sqrts are eliminated), so reducing those as best you can is probably going to give best results.

All that said, if its working at acceptable speed now, its not always a good thing to keep rat-holing yourself over optimizing this bit of code.


I've already done all of those things, and also made BlockTypes so blocks don't need to be deleted, just have their type changed. The last line of code that isn't optimized is the one where I sort my list.

I'm making an open source voxel space game called V0xel_Sp4ce! Help me out with the repository here: https://github.com/SpikeViper/V0xel_Sp4ce

What's it sorting and why? I can't find any reference to that line in your bigger code listings.

There's really only a few things you can do to make sorts faster in general -- You can sort less stuff, sort less often, sort using a different algorithm, or sort items in a different kind of container. Those are all pretty particular to the situation at hand, depending on your data structures. Certain sorting algorithms might be better than others if, for example, you know your data is already mostly sorted, or if you're creating a new sorted collection from multiple, already-sorted collections.

Depends entirely on details.

throw table_exception("(? ???)? ? ???");

It's being sorted using


toLoad.Sort(delegate (Vector4 first, Vector4 second) { return first.w.CompareTo(second.w); });

And there's around 15,000 objects. They are being sorted smallest to largest by the "w" value in each Vector4.

I'm making an open source voxel space game called V0xel_Sp4ce! Help me out with the repository here: https://github.com/SpikeViper/V0xel_Sp4ce

All of those virtual functions are silly. Quite a few of them don't even use state from the object (ie, they could be static).

And the rest shouldn't work like that at all:

public virtual int Temperature()
{
  // -100 to 100
  return 0;
}

public virtual bool Light()
{
  return false; 
}

Those are fields dude, not methods.

public readonly int Temperature;
public readonly bool Light;
// etc.

Defaulting to virtual is a bad idea on C#, VM wont inline those functions. Also that Block could just be a struct just fine if instead of relying on 10 virtual functions you use fields as you should.

All of those FaceDataX methods could be static. You can just get all those "isXYZSolid" booleans first as local variables, then do the logic over them, instead of doing "IsSolid(north)" then asking the same again a few lines below. I really hope the Vector classes are structs, otherwise you're allocating a shit load of tiny objects.

Also you could make the enums "extend" byte directly, save a couple of bytes here and there.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

All of those virtual functions are silly. Quite a few of them don't even use state from the object (ie, they could be static).

And the rest shouldn't work like that at all:

public virtual int Temperature()
{
  // -100 to 100
  return 0;
}

public virtual bool Light()
{
  return false; 
}

Those are fields dude, not methods.

public readonly int Temperature;
public readonly bool Light;
// etc.

Defaulting to virtual is a bad idea on C#, VM wont inline those functions. Also that Block could just be a struct just fine if instead of relying on 10 virtual functions you use fields as you should.

All of those FaceDataX methods could be static. You can just get all those "isXYZSolid" booleans first as local variables, then do the logic over them, instead of doing "IsSolid(north)" then asking the same again a few lines below. I really hope the Vector classes are structs, otherwise you're allocating a shit load of tiny objects.

Also you could make the enums "extend" byte directly, save a couple of bytes here and there.
As stated above, I've already fixed all of this.

I'm making an open source voxel space game called V0xel_Sp4ce! Help me out with the repository here: https://github.com/SpikeViper/V0xel_Sp4ce

There is yet one more problem. The last laggy piece of code is


toLoad.Sort(delegate (Vector4 first, Vector4 second) { return first.w.CompareTo(second.w); });

Is there a faster way to sort objects? This one line is generating 50% of my lag.

I tried and failed to find the definition of "toLoad" in your previous code. Any information about the Vector4 class would also be helpful.

Why is this sorting just Vector4s? How are they linked back to the actual objects you need to sort? My C# is still a little rusty - is there something I'm missing here?

This topic is closed to new replies.

Advertisement