Skip to main content
gamedev.net
🔒 Locked

Highly optimized code for distance

Started by Mescalito Jan 15, 2003 at 4:50 AM 8 replies 1.4k views
Original Post
Mescalito
Mescalito
Does somebody knows how to make a high optimized code to determine distance between 2 points (3d point -> x,y,z). I though of using an inline function with assembly but applying a formula like this in assembly is hard to me (i''m not asm expert !): for 2 points A(Ax,Ay,Az) & B(Bx,By,Bz) distance² = (Ax+Bx)² + (Ay+By)² + (Az+Bz)² and then we take the distance sqrt() Thank you for any help...
Dean Harding
Dean Harding
Nope, that's as fast as you'll get it. Unless you can get it in SSE or something, but I question the value of doing that.

Instead of optimizing the distance formula, the usual trick is to work in "squares-of-distances". If you're only interested in relative distances, then instead of:

if( distance < 2 )
{
    // do something
}


try:

if( distance2 < 4 )
{
    // do something
}


See? The logic is the same, but it's a multiply instead of square root.

Edit: Not even a multiply if you're using a constant on the right

If I had my way, I'd have all of you shot!


codeka.com - Just click it.

[edited by - Dean Harding on January 15, 2003 6:36:42 AM]

Prosper/LOADED
Prosper/LOADED
Actually, it''s :

d^2 = (Ax - Bx)^2 + (Ay - By)^2 + (Az - Bz)^2

(note the - sign instead of +)

ASM won''t help you here. It would be even faster to use a C++ inline function since it may be easier for the compiler to interleave the generated opcodes with opcodes that are outside the function, which is more pipeline and super scalar friendly.

Just forget about ASM and use an inline function.

The only optimizations I can think of are these ones (remember the square root is the bottleneck here) :

- Use a custom sqrt() function. Some aproximations can be computed faster (Quake 2 code contains such a function, if memory deserves).

- Do not compute the square root. If you don''t need the actual distance and you just want to compare distances, you can use the squared distances (since A^2 > B^2 and A > 0 and B > 0 => A > B).

PS : since my english sucks and I''ve noticed you''re french, I can explain it in french if you want.
owl
owl
quote:
Original post by Prosper/LOADED
d^2 = (Ax - Bx)^2 + (Ay - By)^2 + (Az - Bz)^2

(note the - sign instead of +)

ASM won't help you here. It would be even faster to use a C++ inline function since it may be easier for the compiler to interleave the generated opcodes with opcodes that are outside the function, which is more pipeline and super scalar friendly.

Just forget about ASM and use an inline function.

The only optimizations I can think of are these ones (remember the square root is the bottleneck here) :

- Use a custom sqrt() function. Some aproximations can be computed faster (Quake 2 code contains such a function, if memory deserves).

- Do not compute the square root. If you don't need the actual distance and you just want to compare distances, you can use the squared distances (since A^2 > B^2 and A > 0 and B > 0 => A > B).

PS : since my english sucks and I've noticed you're french, I can explain it in french if you want.


he could use left shift for the ^2 to make it go faster

[edited by - xaxa on January 15, 2003 7:16:19 AM]
[size="2"]I like the Walrus best.
Brother Bob
Brother Bob
Left shift won''t help here. A left shift one step is multiplication by two. Although you can use the shift operator and addition multiple times to achieve a square, it will be WAY slower than a single multiplication. Also, if the datatypes in use are floatingpoint types, shifts won''t work at all.
Prosper/LOADED
Prosper/LOADED
quote:
Original post by xaxa
he could use left shift for the ^2 to make it go faster



Please explain how. Left shift is commonly used to multiply a number by a power of two but I fail to see how you can compute X^2 (ie. X*X) with it.
owl
owl
quote:
Original post by Prosper/LOADED
Please explain how. Left shift is commonly used to multiply a number by a power of two but I fail to see how you can compute X^2 (ie. X*X) with it.


Yes sorry, I was wrong. It only works for integer numbers power of two.

(256)^2 = (100000000)<<8 = 10000000000000000

So i guess it's useless in this case.

EDIT: I mean power (not multiple)

[edited by - xaxa on January 15, 2003 8:16:06 AM]
[size="2"]I like the Walrus best.
sardak
sardak
quote:
Original post by xaxa

(256)^2 = (100000000)<<8 = 10000000000000000




256^2 = 1<<16 = 65536
owl
owl
quote:
Original post by sardak
256^2 = 1<<16 = 65536


I meant that if you shift so many times as zeros the number has you get the ^2...

256^2 = (1 0000 0000)<<8 = 1 0000 0000 0000 0000 = 65536
[size="2"]I like the Walrus best.
Jan Wassenberg
Jan Wassenberg
Does this need to be exact?
You can get an upper bound on the distance by summing the absolute difference between components (manhattan or taxicab distance).

> Unless you can get it in SSE or something, but I question the value of doing that.
> Just forget about ASM and use an inline function.
hehe...

VC7:

  
fld DWORD PTR x1
fsub DWORD PTR x2
fld DWORD PTR y1
fsub DWORD PTR y2
fld DWORD PTR z1
fsub DWORD PTR z2
fld ST(0)
fmul ST(0), ST(1)
fld ST(2)
fmul ST(0), ST(3)
faddp ST(1), ST(0)
fld ST(3)
fmul ST(0), ST(4)
faddp ST(1), ST(0)
fstp ST(3)
fstp ST(0)
fstp ST(0)


slapped together 3DNow! code:

  
movq mm0, [x1] ; y1 x1
movq mm1, [x2] ; y2 x2
movq mm2, [z1] ; z2 z1
pfsub mm0, mm1 ; dy dx
pfnacc mm2, mm2 ; dz dz
pfmul mm0, mm0 ; dy^2 dx^2
pfmul mm2, mm2 ; dz^2 dz^2
pfacc mm0, mm0 ; dx^2+dy^2 dx^2+dy^2
pfadd mm0, mm2 ; dist^2 dist^2


a bit more than 2x as fast on my Athlon XP. Similar gains are possible with SSE, though it''s a bit more clumsy for lack of an accumulate instruction.


If you can''t avoid the sqrt, calculate it as x * rsqrt(x):
- on Athlons: pfrsqrt + pfmul (7 clock latency)
- otherwise, use a clever bit of code credited to Carmack:

  
float InvSqrt (float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x
i = 0x5f3759df - (i >> 1);
x = *(float*)&i
x = x*(1.5f - xhalf*x*x);
return x;
}

What this does in a nutshell is approximate via one iteration of Newton''s method (the line with the magic number computes an initial guess, important for convergence).

HTH
Jan

500x2
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.