Fast Inverse Square Root
timmmm.github.io
timmmm.github.io
https://software.intel.com/sites/landingpage/IntrinsicsGuide...
Though you can still have vectorised support for regular precision for AVX2.
https://software.intel.com/sites/landingpage/IntrinsicsGuide...
That way you can quickly get the precision that you want ;)
You can always compute newton's method afterwards to improve the accuracy. But getting the maximum accuracy from one cycle is probably best.
With such an instruction, you could exponentially increase the precision in 2 cycles by just using the same instruction twice.
You can't do that on Intel's hardware. As you mention, you'd need to roll your own multi-SIMD-instruction rsqrt newton iteration complex loop, and use it after the first SIMD call.
That's really sad. There is hardware to perform Newton iterations on Intel CPUs, that's how that instruction is implemented, but the ISA only exposes this hardware via the "do a 14-bit rsqrt operation", which means that you can't really use it to increase precision if that does not suffice for your app.
So all the major platforms, CPUs and GPUs, implement the fast reciprocal square root to decent amounts of accuracy, without any need of bit-twiddling anymore.
https://ridiculousfish.com/blog/posts/labor-of-division-epis... and https://ridiculousfish.com/blog/posts/labor-of-division-epis... also explain this (and are probably better written than my blogpost).
For anyone curious like I was:
Minor nit, there's a typo here in the power-of-two example:
uint divide(uint n) {
return n << p;
}
the shifts should be to the right, obviously.A trick here is that one often doesn't have to do the square root. For instance, if you want something to happen when an object is 5 units away from another object, it's normal to do
if sqrt( (x2-x1)^2 - (y2-y1)^2 ) < 5 then ...
but instead you can do if (x2-x1)^2 - (y2-y1)^2 < 5^2 then ...
trading a sqrt for a squaring. And often the square of the distance can be cached or even a constant. So a lot of libraries (for instance vector library from libgdx[0]) contain a dst function, but also a dst2 function that skips the squaring to save some cycles when not needed.[0]: https://libgdx.badlogicgames.com/ci/nightlies/docs/api/com/b...
if (x2-x1) - (y2-y1) < (x4-x3) - (y4-y3) then ...
I once used this in a path tracer to speed things up a little. The results where less accurate but sometimes this can be used as trade-off.I think I'm missing a reference/joke =/
It is very common in the embedded world and in hardware. I routinely program a CPU which has no FPU, so any arithmetic will have to be done in fixed point.
It's perfectly possible to get work done with fixed point arith, it just requires a bit more thinking through so you don't run out of bits. Usually, I write unit tests where I compile my code for host architecture and compare the fixed point result to host's floating point. Then I can set a precise error margin for my XP approximation.
You're not the only one to be confused by the name. An awful choice of name, in my opinion.
i = * ( long * ) &y; // evil floating point bit level hacking
Learned this recently with Arm AC6.
[Also, this kind of genius analytic approximation of hot functions that do math makes me all tingly]
union { int i; float f } u { .f = 1.23f };
int i = u.i;
Another way is a memcpy, which I believe is the most defined way to do type punning int i;
float f;
memcpy(&i, &f, 4);
But you also have to assume the size of those primitive types but that's pretty safe in modern C/C++.For more information, see the C++20 final draft[0§7.6.1.9]
It’s C++ that’s lacking this feature. Not C.
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Typ...
> you also have to assume the size of those primitive types
This usually works though there's some situations where you could still be surprised. For instance if you're programming embedded processors with avr-gcc you'll have sizeof(double)==4
> But floating point numbers are always greater than LNS numbers
Unless I'm missing something, they are never smaller, but they can be equal (at powers of two).
> E.g. x1/2x^{1/2}x1/2, x−8x^{-8}x−8, x2x^2x2, though you probably wouldn't use it for positive exponents since you can just use multiplication to get an exact answer.
1/2 is positive. As is 1/3.
Ha, funnily enough I did think of that, but I was trying to keep the explanation short and simple (and a bit hand-wavy).
> 1/2 is positive. As is 1/3
Oops, fixed, thanks!
float InvSqrt(float x)
{
long yl;
float y;
yl = 0x5f3759df - ((*(long *) &x) >> 1);
y = *(float *) &yl;
return y * (1.5F - (x * 0.5F * y * y));
} float rsqrt(float number) { return 1.0f / sqrtf(number); }While this may look disappointing, it should be clear that adding 5% (or even 0.1%) error to inverse square root calculations is not something compilers are in the business of.
Now, when you give the compiler more leeway (and -ffast-math is substantial leeway for anything less ephemeral than triangle normals on screen), you get much more interesting things:
Turns out x86 has a (microcoded) instruction for this (and newer processors also have vectorized versions[1] of it). The compiler adds Newton-Rhapson for good measure.
[1]: https://uops.info/html-lat/KBL/VRSQRTSS_XMM_XMM_XMM-Measurem...
If only 1% of cpu time was spent on slow square root, and this sped it up by 100%, it would be barely worth it.
Also, a "mere" 1% speedup seems trivial for most general coding but squeezing 1% out of optimized game code is like blood from a stone. Add a bunch of similar tricks together and you can see 10-20% overall improvement which is huge.
Over 1,000%.
x87 fsqrt took ~70 cycles on the Pentium MMX x87, x87 fdiv was ~39 cycles - so doing inverse square root on x87 was ~109 cycles. The fast inverse square root routine was in the vicinity of 10 cycles.
In Quake, each vertex needed to have its normal vector calculated, which resulted in... I dunno, 100,000 inverse square root calculations per second, assuming you're pushing 100,000 polygons per second. Assuming a 100MHz CPU, the x87 inverse square root calculations would consume 11% of the CPU cycles, the fast inverse square root routine would consume 1% of the CPU cycles.
If you can do 10,000 things with 1% cpu time, and you are now able to do 5,000 more things, is it still not worth it?
But not searching to see if something had been posted before on a forum is a bit different[0]