Juliean said:
Not in my tests. In the third reply, I implemented exactly your structure, and it did show the same characteristics that I measured for std::list, albeit faster by a small margin comparatively. So, if you are not also measuring those slowdowns with 1000 elements that I did (which already came close to 10x for just iteration), then IDK. Or maybe its because you are still measuring overall speed, probably?
I think i missed that third reply. I only remember your recent paste pin comparing std::list vs. std::vector.
But anyway, all my measures are (at least) about the whole collision detection, which is iteration, checking if the pair has been already processed, calculating intersection, appending the pair data to draw a line (preallocated fixed sized buffer for my visuals).
That's why i won't see a large difference between various iteration methods. But in practice it's always like this. You do some work on the objects you iterate, and this work causes it's own good or bad memory access patterns. And almost always the work you do costs way more than iterating the work items.
Thus, optimizing for memory access is quite difficult, because the feedback is bad. I have no profiler to tell me bandwidth, or % of cache misses, so i can only guess.
Isolated, synthetic tests as you did give good feedback, but don't tell much about practice. It may well be that in practice prefetch has enough time to catch up and compensate while you do the actual work.
And finally, even if some bottleneck shows, rearranging data structures to test out alternative access patterns, simply isn't practical with a C based language. The changes you'd need to do are always too heavy.
So you can only hope the initial design of your structs gives good performance, not a bad one. Otherwise you'll never know anyway.
The few times i did such experiments the outcome mostly showed no real difference, which is very good, considering we can't do this often due to effort.
On GPU the situation is very different, though. There it is totally worth to experiment with different data layouts. The difference is big, but unfortunately it also varies widely across different architectures, so you're fucked again.
Regarding lists vs. vectors, it's clear that a list forms just a sequence, so ofc. we'll prefer to reproduce this sequence in memory too, preferring the vector. If we can.
But once we need trees, graphs, or higher dimensions, sequential access is no longer possible no matter what.
And that's where the real question comes up: Do we accept the cache misses, or do we choose a different algorithm with worse time complexity, but better access patterns?
That's a really good question. Obviously the answer always depends. And obviously some pointless flame wars between general preferences, and perception of what people might do often wrong, may come up.
But i do think that we can give a general answer, which is to find a good mix of both. Usually that's complexity on outer loops, and brute force in inner loops.
That's the one and only general performance that i can give.
Juliean said:
So perhaps there is something about std::vector being bad here as well.
Yes. Allocations will always happen, and you have little control about when. You can try to minimize the problem, but you can't solve it. There is a price for convenience and ease of use.
So outside of my little applet code given here, i would not use it for a collision detection system of a game engine. Because idk which sorts of games and requirements will come up using it.
I would also optimize for a large N, and accept slight disadvantages for small N. For the same reasons, plus i want to achieve scaling. So i do not need to rework the system after some years, just because games became bigger again.
In this sense i see your current solution more as addressing specific game logic, but no general collision / spatial query system.
But i don't think that's a mistake, because it works. Contrary, a general system takes more effort, likely adds features you do not currently need, and worst: It still very likely turns up limited and specific in the future, e.g. once you want to expand to infinite worlds.
Juliean said:
I'm afraid I will resist being “cured”
You ignore the fact that sometimes you just need a list and have no choice. It remains a useful tool. There was a free algo. / data structures book linked on the wiki page i've looked up for definition, and the very first topic of the book was linked lists.
Personally i have used double linked lists a lot for tools in the early days. (And i did allocate each object with new.)
Nowadays i don't use it anymore at all, with some rare exceptions. But a tiled 2D game would be such exception. I would use the proposed algorithm here without worries, and i'll keep recommending it. Its the simplest after brute force, and the fastest.
My reason to neglect lists for most was not iteration performance, but rather the expansion to use trees for larger N, or graphs for higher complexity.
I won't subjugate to hardware dictatorship. :D