Speedup collision checking by avoiding expensive sqrt()#25
Conversation
Test timings show a >5% speedup for large solutions.
|
This code isn't like that for no reason; it has been carefully copied from a decompiled version of the game's actual collision detection functions, and it should perform the same operations (including strange casts to and from |
|
Hmm. That will be difficult to do, because the only place that you could see a difference between |
|
Good catch on the compiler warning, though; I just landed a more thorough fix in 7a5de77. |
|
To elaborate more on my previous point: Although |
|
I believe C# calls into the C math functions in its runtime implementation. So using platform |
|
Anyway, I am definitely interested in changes like this, but the reason I haven't done it is because I want to be able to say the code works (at least in the details of how the numbers are computed) the same way as the code in the game. |
These are common in code written in languages like C or C# where the default floating-point type is
|
Right, it's as exact as possible, but that still may not be exact. Because OM runs on Windows and the stdlib it's linking against is the Windows ones, while a common scenario for omsim would be linking against a linux libc (for instance). If you are not actively testing the edge cases already, you do not have any of the guarantees you are hoping for. (I have a relatively deep experience with floating-point reproducibility and knowledge of people working on games having issues with it, especially in a multi-platform context, so I'm not just blowing smoke here.) |
|
To be clear, I don't want to knock what you have. You have a robust set of test cases to verify the behavior, which IMO is exactly what you should have. But what you are saying you want is something much, much stronger, which is bit-accuracy to the game. This implies being exactly correct in all of the extremely buggy corner cases of the game, such as getting overclocking exactly right and farlands collision exactly right. Not only is that very hard, it's also unnecessary (you're forbidding such solutions already). |
|
To be honest, I'd love a way to actually test all the gnarly edge cases. The |
|
I'm certain that it can happen; you just need a careful computer search to find the right set of parameters. Probably such a thing would be excellent fodder for further more-robust tests. :) |
|
Specifically, the issue is that sqrt loses information. Simple demonstration: As a result, at the very edges there will be differences between |
|
Interestingly, at least on my computer, the most common case of atom/atom collision does seem to pass an exhaustive check: #include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
static const float atomRadius = 29;
static int check1(float f)
{
return sqrtf(f) < 2 * atomRadius;
}
static int check2(float f)
{
return f < 4 * atomRadius * atomRadius;
}
int main()
{
uint32_t i = 0;
do {
union { uint32_t i; float f; } u = { .i = i };
if (isnan(u.f) || u.f < 0)
continue;
if (check1(u.f) != check2(u.f))
printf("%" PRIu32 " - %f (%d %d)\n", i, u.f, check1(u.f), check2(u.f));
} while (++i != 0);
} |
|
According to my math, one number should fail for 20 + 20. Edit: Also 15 + 29 |
|
Yeah, I was just typing out this comment -- testing all the different combinations of atoms with other things, there seems to be a single problematic squared distance: 1935.999878 (0x44F1FFFF). This would give a spurious collision between a normal atom (radius 29) and an atom spawning from a glyph (radius 15). |
|
Using the faster check only for 29 + 29 would probably still be a substantial speed-up, though! |
|
I think the first step now that we know about this would be to try and construct a test that creates that exact distance, and see what happens in-game. :D |
|
(I see that the code does do some tests of the form |
|
I tried too to get this in in #10 , good luck. |
|
OK, so first step, I did a search for all floats of the form |
Test timings show a >5% speedup for large solutions.
sqrtis a slow function, and should be avoided in performance-sensitive code. This transformation to usexy_len2instead saves a noticable amount of time. I also cleaned up a few functions to use the float versions, and fixed a compiler warning.Here's a comparison of test timings, filtered to those that take more than 0.1 seconds on my machine:
Before:
After: