- Home /
Math Optimization: Normalize
I am creating magnets with a positive and negative charge. This script takes the position of the positive and negative magnets and applies the force to a vector. This occurs each frame on many objects, and is fairly heavy. Anyone have any tips on optimizing it?
var vector:Vector2 = transform.position;
var dist1:Vector2 = vector - positivePosition;
var dist2:Vector2 = negativePosition - vector;
var div1:float = dist1.magnitude/10;
var div2:float = dist2.magnitude/10;
dist1.Normalize();
dist2.Normalize();
dist1 /= div1;
dist2 /= div2;
vector += dist1 + dist2;
thank you in advance, mike
Hi $$anonymous$$ike, I may completely misunderstand you, but wouldn't it just be basically: apply this force: (positivePosition-negativePosition) * f where "f" is a small number like 0.1 (just tune it until the result looks O$$anonymous$$). (I guess, you would apply that to both objects - if they are both sliding around.) When you say many objects, how many specifically? And do you mean your many objects "all affect each other" OR do you mean one central object, affects each of your many objects?
Answer by Peter G · Dec 13, 2011 at 08:15 PM
First, let me say those aren't real complex operations so this might not be as big a deal as you think. But if you are concerned with speed, here's a faster way of writing it that actually saves two square root operations:
given vector v;
magnitude = |v|
normalized vector = v / |v|
div = |v|/10
final = norm / div = (v / |v|) / (|v| / 10)
simplified = 10v / |v|^2
Now why is that useful. It happens that the square of the magnitude is cheaper to find than the actual magnitude because we don't have to take the square root at the end. Its just x^2 + y^2.
Hence:
var dist1:Vector2 = vector - positivePosition;
dist1 = 10 * dist1 / dist1.sqrMagnitude;
And that's your simplified equation and that might be several times faster than your original equation.
Correct simplification of the above code ;).
I've searched around a bit and found that useful summary about square-root implementations
Just to have a reference how much heavier the usual FPU squareroot is, see this WP-article about the "fast inverse square root" algorithm which is also in the ti$$anonymous$$g table. Note that this algorithm uses 4 float multiplications, 1 integer subtraction, 1 float subtraction and one bit shift and it's still around 6 times faster. If you compare a (FPU) float multiplication with a (FPU) square root the multiplication would be around 40 times faster than the square root ;)
If you can, avoid square roots ;)
+1
Interesting links.
I saw a forum thread a few years ago where guys went through and found that they could beat the speed of many of the $$anonymous$$athf functions by writing their own. $$anonymous$$any of those functions ended up on the wiki, but I don't know if they all did and it was some interesting discussion.
I ran some benchmarks a while ago using the various alternatives (magnitude, Distance, Dot(), ...) and I found that a) for the distance between two vectors, Vector3.Distance(a,b) is fastest. b) for the length of a vector, actually the "manual" version $$anonymous$$athf.Sqrt(a.x*a.x+a.y*a.y+a.z*a.z) is fastest. Which means in your case, |v|^2=v.x*v.x+v.y*v.y+v.z*v.z is the fastest method. But the difference between all variants is usually marginal. I seem to remember it was about 5% faster than v.sqr$$anonymous$$agnitude. @Bunny83: I had a look at that a whiile ago, but I was unable to reproduce it for managed code such as C#. Is there a way to do pointer cast/access in Unity's C#?
@Wolfram: Well i didn't run any benchmark or performance tests in managed code yet, but i would say the "manual" version should be the fastest in most cases.
That's how the Vector3 stuff works internally:
public static float Distance(Vector3 a, Vector3 b)
{
Vector3 vector = new Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
return $$anonymous$$athf.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
}
public static float $$anonymous$$agnitude(Vector3 a)
{
return $$anonymous$$athf.Sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}
public static float Sqr$$anonymous$$agnitude(Vector3 a)
{
return a.x * a.x + a.y * a.y + a.z * a.z;
}
public static float Dot(Vector3 lhs, Vector3 rhs)
{
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
}
@Bunny83: I meant the "fast inverse square root" - is there a way to do pointer arithmetics and casts with Unity's C#?
Answer by uway · Dec 21, 2017 at 02:05 PM
Without unsafe context you can use this implementation of normalizing vector with fast inverse square root:
//Union used by InvSqrt
[StructLayout(LayoutKind.Explicit)]
struct FloatIntUnion
{
[FieldOffset(0)]
public float x;
[FieldOffset(0)]
public int i;
}
FloatIntUnion union = new FloatIntUnion();
//Fast inverse Sqrt
float InvSqrt(float x)
{
union.x = x;
union.i = 0x5f3759df - (union.i >> 1);
x = union.x;
x = x * (1.5f - 0.5f * x * x * x);
return x;
}
//Normalize vector using fast inverse Sqrt
Vector3 FastNormalized(Vector3 src)
{
float inversedMagnitude = InvSqrt(src.sqrMagnitude);
return src * inversedMagnitude;
}