PPQ_Purple
Purple Cube
- Joined
- Oct 11, 2008
- Messages
- 5,454
Speaking as someone with a little bit of C++ experience I'll share my thoughts. Now, these are my thoughts and just that, nothing more. They are NOT the absolute truth of the matter. I have been working with C++ for some 20 years now but that does not mean I know everything. But they are mine and I figured you'd be interested:
Therefore I assume you are talking about memory and paging. Now that is a much more serious concern. But there too the size of a class just isn't a concern. You just aren't going to be filling up your RAM with code but with assets, mostly textures but also data of various types. And you just can't really get away from that. It does not matter if the data about your unit is split into 3 classes or all in one if it has to all be loaded concurrently for the program to get it's information. In fact, such division might make page faults more not less frequent.
So any memory optimizations should be directed not at the DLL but at minimizing those in terms of size, numbers and sharing where ever possible. Well that and looking into if the game really does just serialize ALL the XML and keep it in a giant clump somewhere in memory. That might need looking into. But again, carefully.
You only get significant overhead when you get to expanding the vector by adding elements or shrink the vector size. In those situations your program quite literally copies the whole array to a different larger one element by element. But unless you are doing that often you shouldn't worry about array vs vector performance. And if you are doing that often enough for it to be a concern you should consider using a different data structure altogether like a linked list. But that complicates access a lot.
So just saying vectors = bad is not correct. In fact, for the sort of applications I've seen CIV use them they are the right choice.
I just hope that CIV does not actually use C style code with malloc instead of new. But that's about it.
Not that I would be opposed to a more multi threaded DLL. But I am saying it is NOT going to be easy or simple to do and that the questions you should really be looking at are stuff like "how can I make the AI consider some of its unit actions in parallel" instead of "how do I make some small 100 item loop go in parallel".
So this is a good idea but it would need to be tested.
Modern processors have caches which are large enough that this is not a concern. It wasn't really since like the x86 days. For reference I have an old laptop from like 10 years ago and it has an intel core i5 CPU that has a total of 4MB of L3 cache. You are not going to reasonably fill up that sort of space with code before multitasking makes you clear out.Massive classes - the bigger an object is in memory the less of it fits in cache.
Therefore I assume you are talking about memory and paging. Now that is a much more serious concern. But there too the size of a class just isn't a concern. You just aren't going to be filling up your RAM with code but with assets, mostly textures but also data of various types. And you just can't really get away from that. It does not matter if the data about your unit is split into 3 classes or all in one if it has to all be loaded concurrently for the program to get it's information. In fact, such division might make page faults more not less frequent.
So any memory optimizations should be directed not at the DLL but at minimizing those in terms of size, numbers and sharing where ever possible. Well that and looking into if the game really does just serialize ALL the XML and keep it in a giant clump somewhere in memory. That might need looking into. But again, carefully.
Yes and no. C++ vectors are just arrays with additional code added on to make them expandable. And I mean that quite literally. It's in the specification. What this means is that your typical CRUD operations aren't going to have worse performance than if you'd have used an array.Vectors of pointers - this is bad performance wise in almost all cases. Of course some design requirements might make this impossible, but, as an example, the replacement system, where this is most used, is NOT one of them. At least as far as I can tell thus far from my surface reading of the code).
You only get significant overhead when you get to expanding the vector by adding elements or shrink the vector size. In those situations your program quite literally copies the whole array to a different larger one element by element. But unless you are doing that often you shouldn't worry about array vs vector performance. And if you are doing that often enough for it to be a concern you should consider using a different data structure altogether like a linked list. But that complicates access a lot.
So just saying vectors = bad is not correct. In fact, for the sort of applications I've seen CIV use them they are the right choice.
I am curious as to what else you would use. I mean, this is C++ we are talking about. Pointers of some description are literally the correct way to hold references to non primitive types. Unless you mean they are using raw pointers everywhere in which case that's not a performance concern but a memory management one.Pointers in general - they shouldn't be the default for how to instantiate an object, but they seem to be in this code, so I'm going to look into that. Maybe most or all uses are the correct choice, I shall see.
I just hope that CIV does not actually use C style code with malloc instead of new. But that's about it.
I can not say anything negative about wanting methods to not have side effects.Const correctness - yes the optimizer can in fact perform better when it knows that code doesn't have side effects. It will also lead to much easier ability to apply...
The problem with concurrency is that it's not that simple. Sure, you can parallelize a loop here and there but that isn't going to net you much of a result in the big picture. In order to make proper use of parallelization you have to actually consider the entire algorithm that the method in question uses as well as those of the entire calling chain both front and back and change them into something that makes sense to run in parallel. It's a much larger project than just throwing in a few threads to speed up a loop. Indeed, taking the shallow approach will probably actually leave you worse off because threading it self requires a certain amount of overhead.Data parallelization - once you know that the contents of a for loop *actually* don't have side effects (not that const actually guarantees it, but it is most of the way there), you can run it on more than one thread so it finishes faster. I don't know how many places could currently benefit from this, but some places that currently can't might be refactored such that they could.
Not that I would be opposed to a more multi threaded DLL. But I am saying it is NOT going to be easy or simple to do and that the questions you should really be looking at are stuff like "how can I make the AI consider some of its unit actions in parallel" instead of "how do I make some small 100 item loop go in parallel".
Now this is something to look into. From what I have seen in modded CIV DLL code there is both a lot of redundant branching and a lack of result caching going on there.Branching - its slow. Especially convoluted branching will hurt the branch predictor and the optimizer. I already noticed plenty of redundant branching as well, where constraints are checked multiple times.
Now THIS is exactly the sort of thing you should definitively be looking into.Bake the static portions of the AI scoring - a large part of the valuation that the AI gives to buildings when deciding which to build is based off their raw numbers and not contingent on any external factors, and thus can be pre-computed, instead of computed for every city, for every building, every turn. Probably the same is true of unit builds.
Would long term planning not reduce perceived performance by making the "planning" turns take longer? My concern is that even if you managed to cut down the absolute total time if that meant every Nth turn took way longer to finish than the rest that would make for a worse overall user experience.Better utilization of caches once they are formed, e.g. longer term city build planning for the AI.
So this is a good idea but it would need to be tested.
As long as the overhead to track this is not significant I agree. Just always remember to keep track of the overhead.More accurate cache invalidation rules - caches don't need to be cleared at the start of a round, instead they can be cleared or partially cleared when values known to effect them have changed.
I am not opposed to the idea but must warn you based on my experience that you should plan this out carefully lest you create a monster class that does everything and results in more overhead than you gain by using it. This is a serious concern to keep track.Multi-level cache with standardized representation, from which invalidation rules can be derived automatically - this would be the ultimate way to model the caches. Not a trivial task.
This sounds about right honestly.Queue multiple buildings - the AI is already evaluating and assigning a score to every possible building every time it changes production. Why not just put the top 5 rated buildings in the queue straight away? Then decide each turn if the AI feels strongly about clearing the rest of the queue due to some significant change in circumstances (war, tech with an un-built wonder in it etc.) This is how most humans play after all.