How Order of Operations Can Seriously Change The Code Speed? - c#

I watched a video about code optimization from Tarodev. At the end of the video, I am shocked because I saw that order of operations can seriously change the code speed. Although I tried it myself, I don't understand how can it be possible? How can Float x Float x Vector 2 times faster than Float x Vector x Float or Vector x Float x Float?
I installed Tarodev's project from GitHub and tried it myself. The result was the same as with the video.
EDIT:
Thanks for all these great answers guys. After your answers, I understood the reason and felt dumb :) Also, I'm adding a video Repository and example code at Orhtej2's suggestion

Just first of all
someVector3 * someFloat
and
someFloat * someVector3
both basically return
new Vector3(someVector3.x * someFloat, someVector3.y * someFloat, someVector3.z * someFloat)
so basically 3 float * float operations + constructor call of Vector3
And then the order matters because your operations will be resolved in the order like this
In the first case you do
(float * float) * Vector3
so in steps:
1. (float * float) => single float * operation => result: float
2. (float * Vector3) => 3 float * operations => result: Vector3
in the second you do
(float * Vector3) * float
so in steps:
1. (float * Vector3) => 3 float * operations => result: Vector3
2. (Vector3 * float) => 3 float * operations => result: Vector3
and in the third equivalent you do
(Vector2 * float) * float
so in steps
1. (Vector3 * float) => 3 float * operations => result: Vector3
2. (Vector3 * float) => 3 float * operations => result: Vector3
In the first case you have only 4 * operations while in the other two cases you have 6.
Plus in the second and third case you also are instantiating an additional Vector3 (the result from the first operation) which also costs performance and memory.

To multiply a float by a Vector3, three multiplies are required. To multiply a float by a float, clearly just one multiply is requred.
When you do:
result = a * b * c;
That means:
result = (a * b) * c;
or more explicitly:
var tmp = a * b;
result = tmp * c;
Suppose a and b are floats and c is a vector. Then a * b performs one multiply (float by float) and results in a float, then the second operation performs three multiplies (float by Vector3). That's four multiplies in total.
Now support a is a vector and b and c are floats. Then a * b performs three multiplies (Vector3 by float) and results in a Vector3, then the second operation again performs three multiplies (Vector3 by float) and results in a Vector3. That's six multiplies in total.
Now, it's possible that the compiler might be able to figure out that these are equivalent and do the cheaper four multiplies instead. But often it's not easy for it to see that. And often it's not quite equivalent. For example, rounding and overflow can depend very much on which way round you do the operations, and the compiler won't mess with them because it could introduce inaccuracies, so it trusts you and simply does exactly what you tell it.

The answer is pretty easy,follow my example:
a Vector is an object made of more than 1 value: e.g. Vector3 v = new Vector3(1,2,3)
if u multiply a number for a Vector3, you are doing 3 operations.
float n = 3.f
Vector3 v = new Vector3(1,2,3)
n*v result is a Vector3(1*n,2*n,3*n),so 3 calculations for one multiplication.
following your example in the first case you are doing n*n*v,so 1 calculation for n*n + 3 calculation for n^2 * v,for a total of 4 calculations.
in the second case you are doing n * v * n,that means 3 calculations for n*v + another 3 calculations for v*n,for a total of 6 calculations.

Vector x Scalar1 x Scalar2 => (Vector x Scalar1) x Scalar2 =>
VectorTemp x Scalar2 = EndResult
So to summarize, we had to operate two times a scalar by vector operation. meaning
2 times a Vector length multiplication.
Where in constrast
Scalar1 x Scalar2 x Vector = (Scalar1 x Scalar2) x Vector =>
Scalar3 x Vector = EndResult.
Here one Vector x Scalar operation is required. Scalar1 by Scalar2 is only multipication
So I guess the key idea here is if operations are commutative, starts with the cheapest ones.

Related

Why is my angle of 2 vectors function return NaN even though i follow the formula

I'm making a function that calculates the angle between 2 given vectors for my unity game using the dot product formula:
vector(a)*vector(b)=|vector(a)|*|vector(b)|*cos(the angle)
so I figured that the angle would equals
acos((vector(a)*vector(b))/(|vector(a)|*|vector(b)|))
Anyway here's my code:
float rotateAngle(Vector2 a,Vector2 b)
{
return Mathf.Acos((a.x * b.x + a.y * b.y) / ((Mathf.Sqrt(a.x * a.x + a.y * a.y)) * (Mathf.Sqrt(b.x * b.x + b.y * b.y)))) * (180 / Mathf.PI);
}
But when i played it the console showed NaN. I've tried and reviewed the code and the formula but returned empty-handed.
Can someone help me? Thank you in advanced!!
float.NaN is the result of undefined (for real numbers) mathematical operations such as 0 / 0 (note from the docs that x / 0 where x != 0 rather returns positive or negative infinity) or the square root of a negative value. As soon as one operant in an operation already is NaN then also the entire operation returns again NaN.
The second (square root of a negative value) can not happen here since you are using squared values so most probably your vectors have a magnitude of 0.
If you look at the Vector2 source code you will find their implementation of Vector2.Angle or Vector2.SignedAngle (which you should rather use btw as they are tested and way more efficient).
public static float Angle(Vector2 from, Vector2 to)
{
// sqrt(a) * sqrt(b) = sqrt(a * b) -- valid for real numbers
float denominator = (float)Math.Sqrt(from.sqrMagnitude * to.sqrMagnitude);
if (denominator < kEpsilonNormalSqrt)
return 0F;
float dot = Mathf.Clamp(Dot(from, to) / denominator, -1F, 1F);
return (float)Math.Acos(dot) * Mathf.Rad2Deg;
}
// Returns the signed angle in degrees between /from/ and /to/. Always returns the smallest possible angle
public static float SignedAngle(Vector2 from, Vector2 to)
{
float unsigned_angle = Angle(from, to);
float sign = Mathf.Sign(from.x * to.y - from.y * to.x);
return unsigned_angle * sign;
}
There you will find that the first thing they check is
float denominator = (float)Math.Sqrt(from.sqrMagnitude * to.sqrMagnitude);
if (denominator < kEpsilonNormalSqrt)
return 0F;
which basically makes exactly sure that both given vectors have a "big enough" magnitude, in particular one that is not 0 ;)
Long story short: Don't reinvent the wheel and rather use already built-in Vector2.Angle or Vector2.SignedAngle
NaN are typically the result of invalid mathematical operations on floating point numbers. A common source is division by zero, so my guess would be that the vector is 0,0.
I would also recommend using the built in functions for computing the normalization, Length/Magnitude, Dot etc. that will make the code much easier to read, and the compiler should be fairly good at optimizing that kind of code. If you need to do any additional optimization, only do so after you have done some measurements.

C# 2D Vector Movement - speed and direction

I'm doing a (probably simple) task, in which i want to make a drawed object move to a user-controlled (drawed too). All i have is the players X and Y coördinate, defined as respectively Xp and Yp. The object that has to move (after trigger, not included in code down here) to the 'player-object' has its coördinates defined in this.X and this.Y.
int xDirection = Xp - this.X;
int yDirection = Yp - this.Y;
int angleInDegrees = (int)Math.Atan2(xDirection, yDirection);
double radians = (Math.PI / 180) * angleInDegrees;
double xTmp = 3 * Math.Cos(radians);
int xSpeed = (int)xTmp;
double yTmp = 3 * Math.Sin(radians);
int ySpeed = (int)yTmp;
Console.WriteLine(xDirection);
Console.WriteLine(yDirection);
Console.WriteLine(xSpeed);
Console.WriteLine(ySpeed);
Console.ReadLine();
This doesn't give me the right figures, so i was wondering what may be wrong.
The toughest bit about this probably the fact that the object that has to move to the playerobject may be approached from all the sides (360 degrees) but there's no angle of approach available.
I hope to be complete with my question,
Tim
I'm betting the main problem you're seeing is this line:
int angleInDegrees = (int)Math.Atan2(xDirection, yDirection);
As #catflier mentioned, Math.Atan2 returns the angle in radians (so a number ranging from 0 to 2pi). However, you perform a cast to int which will truncate the decimal places. So if your angle was at 45 degrees, that's actually returning ~0.785398 radians. A cast to int will turn it into 0. Similarly, at 90 degrees, that's ~1.570796 radians, a cast to int will result in 1. That's significant round-off error. As I mentioned in my comment, consider changing all your types to doubles and only perform integer casts at the last point possible (I suppose your objects are positioned based on integers).
Math.Atan2 returns a value in radians, so are other c# trigonometric functions.
double angle = Math.Atan2(yDirection, xDirection);
Also make sure to force type casts to decimals:
3.0 * Math.Cos(radians);

Is it safe to multiply a float/double by 0.5 instead of divide by 2 in C# and C++?

Basically for things like this (not real code):
for(int i = 0; i < 1000000000; ++i)
{
Vert v0 = mesh.polygons[i].edges[0].vert0;
Vert v1 = mesh.polygons[i].edges[0].vert1;
Vector3 center = (v0.pos + v1.pos) / 2;
}
v0.pos is of type Vector3<float, float, float>, but could be Vector3<double, double, double>.
Is it safe to just do?:
Vector3 center = (v0.pos + v1.pos) * 0.5;
This might seem like premature optimization but this has to be as fast as possible that will be called on billions of points (point clouds).
Not a C++ expert by any means, but in C#, doing this:
var a = new Func<float, float, double>((float f1, float f2) => (f1 + f2) * 0.5);
var b = new Func<float, float, double>((float f1, float f2) => (f1 + f2) / 2);
generates IL that in the former:
Loads the arguments
Adds them (producing a single)
Converts the result to a double
Loads the double argument (0.5)
Multiplies (producing a double)
Returns the result
and the latter
Loads the arguments
Adds them (producing a single)
Loads the constant integer (2)
Divides (producing a single, because single / integer produces a single)
Converts the result to a double
Returns the result
It strikes me that the former will likely have higher precision because the multiplication is performed on 2 doubles, resulting in double precision, whereas in the latter the division is performed on a single which is upcast to a double (but still only will have the precision of the single).
This bears me out:
Console.Out.WriteLine(((double)1) * ((double)1/3));
Console.Out.WriteLine((1) / ((float)3));
produces:
0.333333333333333
0.3333333
So, 'safe', maybe, if losing precision gaining extra precision is ok.

Fourier Transformation help

I'm suppose to do some work with Fourier transformations and I'm still really confused.
I'm given a signal (in this case it is f[t] = sin(2 pi s t / N) where s = 8 and N = 128)
And I'm suppose to find the Real, Imaginary, Phase, and Magnitude.
I understand how to get the Real and Imaginary, but the Phase and the Magnitude are beyond me...
the sudo code for getting the Real and the Imaginary is:
for u = 0 to M-1 do
F[u].real = 0
F[u].imag = 0
for x = 0 to M-1 do
F[u].real += f[x] * cos(- 2 * pi * u * x / M)
F[u].imag += f[x] * sin(- 2 * pi * u * x / M)
end do
F[u].real /= M
F[u].imag /= M
end do
Now somewhere in there is the phase and the magnitude, but where?!
Thanks!
Also, some programmer-equse explination of the basics of FTs would be wonderful as well!
If you think to real and imaginary components as coordinates in an XY plane then the phase is the angle between the vector and the X+ axis, and the magnitude is the length of the vector.
To compute then you just need
magnitude = sqrt(real*real + imag*imag)
phase = atan2(imag, real)
From http://en.wikipedia.org/wiki/Complex_number#Absolute_value_and_argument:
The magnitude is sqrt(real^2 + imag^2) (where ^ denotes "squared").
The phase is atan2(imag, real) (where atan2() denotes the two-argument arctan function).
That Wikipedia article explains why better than I can do justice here.

Optimization of a distance calculation function

In my code I have to do a lot of distance calculation between pairs of lat/long values.
the code looks like this:
double result = Math.Acos(Math.Sin(lat2rad) * Math.Sin(lat1rad)
+ Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad));
(lat2rad e.g. is latitude converted to radians).
I have identified this function as the performance bottleneck of my application. Is there any way to improve this?
(I cannot use look-up tables since the coordinates are varying). I have also looked at this question where a lookup scheme like a grid is suggested, which might be a possibility.
Thanks for your time! ;-)
If your goal is to rank (compare) distances, then approximations (sin and cos table lookups) could drastically reduce your amount of computations required (implement quick reject.)
Your goal is to only proceed with the actual trigonometric computation if the difference between the approximated distances (to be ranked or compared) falls below a certain threshold.
E.g. using lookup tables with 1000 samples (i.e. sin and cos sampled every 2*pi/1000), the lookup uncertainty is at most 0.006284. Using uncertainty calculation for the parameter to ACos, the cumulated uncertainty, also be the threshold uncertainty, will be at most 0.018731.
So, if evaluating Math.Sin(lat2rad) * Math.Sin(lat1rad)
+ Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad) using sin and cos lookup tables for two coordinate-set pairs (distances) yields a certain ranking (one distance appears greater than the other based on the approximation), and the difference's modulus is greater than the threshold above, then the approximation is valid. Otherwise proceed with the actual trigonometric calculation.
Would the CORDIC algorithm work for you (in regards to speed/accuracy)?
Using inspiration from #Brann I think you can reduce the calculation a bit (Warning its a long time since I did any of this and it will need to be verified). Some sort of lookup of precalculated values probably the fastest though
You have :
1: ACOS( SIN A SIN B + COS A COS B COS(A-B) )
but 2: COS(A-B) = SIN A SIN B + COS A COS B
which is rewritten as 3: SIN A SIN B = COS(A-B) - COS A COS B
replace SIN A SIN B in 1. you have :
4: ACOS( COS(A-B) - COS A COS B + COS A COS B COS(A-B) )
You pre-calculate X = COS(A-B) and Y = COS A COS B and you put the values into 4
to give:
ACOS( X - Y + XY )
4 trig calculations instead of 6 !
Change the way you store long/lat:
struct LongLat
{
float
long,
lat,
x,y,z;
}
When creating a long/lat, also compute the (x,y,z) 3D point that represents the equivalent position on a unit sphere centred at the origin.
Now, to determine if point B is nearer to point A than point C, do the following:
// is B nearer to A than C?
bool IsNearer (LongLat A, LongLat B, LongLat C)
{
return (A.x * B.x + A.y * B.y + A.z * B.z) < (A.x * C.x + A.y * C.y + A.z * C.z);
}
and to get the distance between two points:
float Distance (LongLat A, LongLat B)
{
// radius is the size of sphere your mapping long/lats onto
return radius * acos (A.x * B.x + A.y * B.y + A.z * B.z);
}
You could remove the 'radius' term, effectively normalising the distances.
Switching to lookup tables for sin/cos/acos. Will be faster, there are alot of c/c++ fixed point libraries that also include those.
Here is code from someone else on Memoization. Which might work if the actual values used are more clustered.
Here is an SO question on Fixed Point.
What is the bottle neck? Is the the sine/cosine function calls or the arcsine call?
If your sine/cosine calls are slow, you could use the following theorem to prevent so many calls:
1 = sin(x)^2 + cos(x)^2
cos(x) = sqrt(1 - sin(x)^2)
But I like the mapping idea so that you don't have to recompute values you've already computed. Although be careful as the map could get very large very quickly.
How exact do you need the values to be?
If you round your values a bit then you could store the result of all lookups and check if thay have been used befor each calculation?
Well, since lat and lon are garenteed to be within a certain range, you could try using some form of a lookup table for you Math.* method calls. Say, a Dictionary<double,double>
I would argue that you may want to re-examine how you found that function to be the bottleneck. (IE did you profile the application?)
The equation to me seems very light weight and shouldn't cause any trouble.
Granted, I don't know your application and you say you do a lot of these calculations.
Nevertheless it is something to consider.
As someone else pointed out, are you sure this is your bottleneck?
I've done some performance testing of a similar application I'm building where I call a simple method to return a distance between two points using standard trig. 20,000 calls to it shoves it right at the top of the profiling output, yet there's no way I can make it faster... It's just the shear # of calls.
In this case, I need to reduce the # calls to it... Not that this is the bottleneck.
I use a different algorithm for calculating distance between 2 lati/longi positions, it could be lighter than yours since it only does 1 Cos call and 1 Sqrt call.
public static double GetDistanceBetweenTwoPos(double lat1, double long1, double lat2, double long2)
{
double distance = 0;
double x = 0;
double y = 0;
x = 69.1 * (lat1 - lat2);
y = 69.1 * (long1 - long2) * System.Math.Cos(lat2 / 57.3);
//calculation base : Miles
distance = System.Math.Sqrt(x * x + y * y);
//Distance calculated in Kilometres
return distance * 1.609;
}
someone has already mentioned memoisation and this is a bit similar. if you comparing the same point to many other points then it is better to precalculate parts of that equation.
instead of
double result = Math.Acos(Math.Sin(lat2rad) * Math.Sin(lat1rad)
+ Math.Cos(lat2rad) * Math.Cos(lat1rad) * Math.Cos(lon2rad - lon1rad));
have:
double result = Math.Acos(lat2rad.sin * lat1rad.sin
+ lat2rad.cos * lat1rad.cos * (lon2rad.cos * lon1rad.cos + lon1rad.sin * lon2rad.sin));
and i think that's the same formula as someone else has posted because part of the equation will disappear when you expand the brackets:)

Categories

Resources