A few years back, I got an assignment at school, where I had to parallelize a Raytracer.
It was an easy assignment, and I really enjoyed working on it.
Today, I felt like profiling the raytracer, to see if I could get it to run any faster (without completely overhauling the code). During the profiling, I noticed something interesting:
// Sphere.Intersect
public bool Intersect(Ray ray, Intersection hit)
{
double a = ray.Dir.x * ray.Dir.x +
ray.Dir.y * ray.Dir.y +
ray.Dir.z * ray.Dir.z;
double b = 2 * (ray.Dir.x * (ray.Pos.x - Center.x) +
ray.Dir.y * (ray.Pos.y - Center.y) +
ray.Dir.z * (ray.Pos.z - Center.z));
double c = (ray.Pos.x - Center.x) * (ray.Pos.x - Center.x) +
(ray.Pos.y - Center.y) * (ray.Pos.y - Center.y) +
(ray.Pos.z - Center.z) * (ray.Pos.z - Center.z) - Radius * Radius;
// more stuff here
}
According to the profiler, 25% of the CPU time was spent on get_Dir and get_Pos, which is why, I decided to optimize the code in the following way:
// Sphere.Intersect
public bool Intersect(Ray ray, Intersection hit)
{
Vector3d dir = ray.Dir, pos = ray.Pos;
double xDir = dir.x, yDir = dir.y, zDir = dir.z,
xPos = pos.x, yPos = pos.y, zPos = pos.z,
xCen = Center.x, yCen = Center.y, zCen = Center.z;
double a = xDir * xDir +
yDir * yDir +
zDir * zDir;
double b = 2 * (xDir * (xPos - xCen) +
yDir * (yPos - yCen) +
zDir * (zPos - zCen));
double c = (xPos - xCen) * (xPos - xCen) +
(yPos - yCen) * (yPos - yCen) +
(zPos - zCen) * (zPos - zCen) - Radius * Radius;
// more stuff here
}
With astonishing results.
In the original code, running the raytracer with its default arguments (create a 1024x1024 image with only direct lightning and without AA) would take ~88 seconds.
In the modified code, the same would take a little less than 60 seconds.
I achieved a speedup of ~1.5 with only this little modification to the code.
At first, I thought the getter for Ray.Dir and Ray.Pos were doing some stuff behind the scene, that would slow the program down.
Here are the getters for both:
public Vector3d Pos
{
get { return _pos; }
}
public Vector3d Dir
{
get { return _dir; }
}
So, both return a Vector3D, and that's it.
I really wonder, how calling the getter would take that much longer, than accessing the variable directly.
Is it because of the CPU caching variables? Or maybe the overhead from calling these methods repeatedly added up? Or maybe the JIT handling the latter case better than the former? Or maybe there's something else I'm not seeing?
Any insights would be greatly appreciated.
Edit:
As #MatthewWatson suggested, I used a StopWatch to time release builds outside of the debugger. In order to get rid of noise, I ran the tests multiple times. As a result, the former code takes ~21 seconds (between 20.7 and 20.9) to finish, whereas the latter only ~19 seconds (between 19 and 19.2).
The difference has become negligible, but it is still there.
Introduction
I'd be willing to bet that the original code is so much slower because of a quirk in C# involving properties of type structs. It's not exactly intuitive, but this type of property is inherently slow. Why? Because structs are not passed by reference. So in order to access ray.Dir.x, you have to
Load local variable ray.
Call get_Dir and store the result in a temporary variable. This involves copying the entire struct, even though only the field 'x' is ever used.
Access field x from the temporary copy.
Looking at the original code, the get accessors are called 18 times. This is a huge waste, because it means that the entire struct is copied 18 times overall. In your optimized code, there are only two copies - Dir and Pos are both called only once; further access to the values only consist of the third step from above:
Access field x from the temporary copy.
To sum it up, structs and properties do not go together.
Why does C# behave this way with struct properties?
It has something to do with the fact that in C#, structs are value types. You are passing around the value itself, rather than a pointer to the value.
Why doesn't the compiler recognize that the get accessor is simply returning a field, and bypass the property alltogether?
In debug mode, optimizations like this are skipped to provide for a better debegging experience. Even in release mode, you'll find that most jitters don't often do this. I don't know exactly why, but I believe it is because the field is not always word-aligned. Modern CPUs have odd performance requirements. :-)
Related
Keeping in mind this similar question does C#/Roslyn omit multiplication by 1 in cases where a variable is hard coded to 1? The similar question's answers suggest Roslyn would convert var x = 5 * 1 to var x = 5at compile time, but I wonder if this holds true for a more complex example.
Example:
var baseAmount = 25.10M;
var multiplier = 1;
var total = baseAmount * multiplier; // does this become just "baseAmount"?
Ultimately I am trying to determine if adding * 1 or an equivalent will result in performance impacts at scale (code hit millions of times). Being explicit and verbose with the * 1 multiplication in this case would result in more readable, "self-commenting" code for people new to the codebase.
Looking at the disassembly of the following code:
public int test()
{
int baseAmount = 25;
var multiplier = 1;
var total = baseAmount * multiplier;
return total;
}
you can see that the imul is still there:
Replacing multiplier by the constant 1 removes the imul:
Changing the type of baseAmount from int to Decimal creates assembly code that is a bit more complex. With multiplier variable:
Without multiplier:
Looking at the assembly code, it seems that in both cases the multiply subroutine (next to the yellow arrow) is being called. So the compiler will not optimize the constant 1 in this case.
Issue
In unity, I have to calculate the angle of a spawning particle system according to the location where a different object hit it.
During this calculation I identify if the hit occurs on the bottom half of the impacted entity.
This evaluation fails, and produces a different result.
I debugged this (using VS2017 and latest Unity 2018 release) and found that when I perform a 'watch' on the relevant expression, it evaluates to true while when running the code itself it is evaluated to false.
How I Tried to Resolve it
Initially when facing the issue, I managed to work-around it by altering the expression inside of the condition, but now it doesn't change it.
I have tried to pull out the expression to it's own value, but it displays inconsistency regarding the result.
In the debugger it would, on rare occasions, evaluate correctly, while all other times (including not using the debugger) evaluate incorrectly.
The Relevant Code
SpriteRenderer Renderer = GetComponent<SpriteRenderer>();
Bounds Bounds = Renderer.bounds;
Vector3 Center = Bounds.center;
Vector3 HalfSize = Bounds.extents;
if (HitPosition.y != int.MinValue)
{
if (HitPosition.y < Center.y + HalfSize.y && HitPosition.y > Center.y - HalfSize.y)
{
HitPosition.y = Center.y + (HalfSize.y * (Center.y < 0 ? 1 : -1));
}
}
else
{
HitPosition.y = Random.Range(Center.y - (HalfSize.y * 0.2f), Center.y + (HalfSize.y * 0.9f));
HitPosition.x += (HitPosition.x < Center.x ? -1 : 1) * HitPosition.x / 100;
}
bool HitOnRight = HitPosition.x >= Center.x + HalfSize.x;
bool HitOnLeft = HitPosition.x <= Center.x - HalfSize.x;
float Angle = 0f;
if (Center.y - HalfSize.y - HitPosition.y >= 0) // <--- Issue
{
Angle = HitOnLeft ? 135f : (HitOnRight ? -135f : 180f);
}
else
{
Angle = HitOnLeft ? 90f : (HitOnRight ? -90f : 0f);
}
Result Expectations
Since this is a sort-of generic method, that is used accord multiple entities in the scene I expect it to evaluate differently across various
In the cases in which the evaluation issue occurs, I expect it to evaluate to true, in all other occurrences it should (and does) evaluates to false.
Following #McAden and another fellow who removed his answer (or had it removed), the issue was indeed the calculation itself.
I'll explain the reason for the issue, in the case that anyone in the future will come upon this.
Unity, when printing output using the Debug.LogXXX functions, will round all values to 2 decimal points.
I tried to find offical documentation, but as it appears it is a part of C# itself.
float (for those who come from a Java background) is identified as a Single type object, and when printed are printed using the ToString() method which narrows the result to 2 decimal places.
This can cause confusion when performing a standard Debug.LogFormat print, because it will display 2 decimal places, and in cases such as mine, the difference will be so small that the result will be rounded to 0.
After deeper debugging and further diving into the actual value of the difference, I arrived to the point where the values I got were:
Center Y Value: 5.114532
Half Y Value: 0.64
Hit Y Value: 4.474533
Difference Result: -0.0000001192
This is a less than perfect example, because you can see that it will result in -0.000001 difference there were many instances until now that resulted in what appeared to be an absolute 0.
In order to perform a proper comparison, we need to follow C# official documentation, which dictates:
In cases where a loss of precision is likely to affect the result of a comparison, you can use the following techniques instead of calling the Equals or CompareTo method:
Call the Math.Round method to ensure that both values have the same precision. The following example modifies a previous example to use this approach so that two fractional values are equivalent.
using System;
public class Example
{
public static void Main()
{
float value1 = .3333333f;
float value2 = 1.0f/3;
int precision = 7;
value1 = (float) Math.Round(value1, precision);
value2 = (float) Math.Round(value2, precision);
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
}
}
// The example displays the following output:
// 0.3333333 = 0.3333333: True
Another approach is to implement an IsApproximatelyEqual;
Test for approximate equality instead of equality. This technique requires that you define either an absolute amount by which the two values can differ but still be equal, or that you define a relative amount by which the smaller value can diverge from the larger value.
Eventually, the root cause of this issue was that I had ignored the deeper precision values represented by the variables and thus found myself not understanding why the comparison is incorrect.
Thank you for your help, hope no one runs into this type of silly issue :)
Operator precedence. Try some parentheses.
if ((Center.y - HalfSize.y - HitPosition.y) >= 0) // <--- Issue
{
Angle = HitOnLeft ? 135f : (HitOnRight ? -135f : 180f);
}
else
{
Angle = HitOnLeft ? 90f : (HitOnRight ? -90f : 0f);
}
Audio noob here and math challenged. I'm working with DirectSound which uses a -10000 to 0 range, converting that to a 0-100 scale.
I found this function here to obtain the millibels based on a percentage:
private int ConvertPercentageToMillibels(double value)
{
double attenuation = 1.0 / 1024.0 + value / 100.0 * 1023.0 / 1024.0;
double db = 10 * Math.Log10(attenuation) / Math.Log10(2);
return (int)(db * 100);
}
I need help getting the inverse of this function, basically to get the percentage based on millibels. Here is what I've got so far, which isn't working:
private double ConvertMillibelsToPercentage(int value)
{
double db = value / 100;
double attenuation = Math.Pow(10, db) / 10 * Math.Pow(10, 2);
double percentage = (1.0 * attenuation) - (1024.0 * 100.0 / 1023.0 * 1024.0);
return percentage;
}
Here you go!
private double ConvertMillibelsToPercentage(int value)
{
double exponent = ((value / 1000.0) + 10);
double numerator = 100.0 * (Math.Pow(2, exponent) - 1);
return numerator / 1023.0;
}
Answer will differ slightly due to obvious issues that arise from going between an int and a double.
EDIT: Per the teach how to fish request, here are the first mathematical steps toward arriving at the solution. I didn't show the whole thing because I didn't want to spoil allll the fun. All log functions should be considered Log base 10 unless otherwise noted:
millibels = db*100; // Beginning to work backward
millibels = 10*Log(attenuation)*(1/Log(2))*1000; // Substituting for db
millibels = 1000*Log(attenuation)/Log(2); // Simplifying
let millibels = m. Then:
m = 1000*Log(attenuation)/Log(2);
from here you can go two routes, you can either use properties of logs to find that:
m = 1000* Log_2(attenuation);// That is, log base 2 here
attenuation = 2^(m/1000);
OR you can ignore that particular property and realize:
attenuation = 10^(m*Log(2)/1000);
Try to work it out from one of the above options by plugging in the value that you know for attenuation:
attenuation = (1/1024)+(percentage/100)*(1023/1024);
And then solving for percentage. Good luck!
PS If you ever get stuck on things like this, I highly recommend going to the math stack exchange - there are some smart people there who love to solve math problems.
OR if you are particularly lazy and just want the answer, you can often simply type this stuff into Wolfram Alpha and it will "magically" give you the answer. Check this out
I have a PID controller running on a robot that is designed to make the robot steer onto a compass heading. The PID correction is recalculated/applied at a rate of 20Hz.
Although the PID controller works well in PD mode (IE, with the integral term zero'd out) even the slightest amount of integral will force the output unstable in such a way that the steering actuator is pushed to either the left or right extreme.
Code:
private static void DoPID(object o)
{
// Bring the LED up to signify frame start
BoardLED.Write(true);
// Get IMU heading
float currentHeading = (float)RazorIMU.Yaw;
// We just got the IMU heading, so we need to calculate the time from the last correction to the heading read
// *immediately*. The units don't so much matter, but we are converting Ticks to milliseconds
int deltaTime = (int)((LastCorrectionTime - DateTime.Now.Ticks) / 10000);
// Calculate error
// (let's just assume CurrentHeading really is the current GPS heading, OK?)
float error = (TargetHeading - currentHeading);
LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2");
// We calculated the error, but we need to make sure the error is set so that we will be correcting in the
// direction of least work. For example, if we are flying a heading of 2 degrees and the error is a few degrees
// to the left of that ( IE, somewhere around 360) there will be a large error and the rover will try to turn all
// the way around to correct, when it could just turn to the right a few degrees.
// In short, we are adjusting for the fact that a compass heading wraps around in a circle instead of continuing
// infinity on a line
if (error < -180)
error = error + 360;
else if (error > 180)
error = error - 360;
// Add the error calculated in this frame to the running total
SteadyError = SteadyError + (error * deltaTime);
// We need to allow for a certain amount of tolerance.
// If the abs(error) is less than the set amount, we will
// set error to 0, effectively telling the equation that the
// rover is perfectly on course.
if (MyAbs(error) < AllowError)
error = 0;
LCD.Lines[2].Text = "Error: " + error.ToString("F2");
// Calculate proportional term
float proportional = Kp * error;
// Calculate integral term
float integral = Ki * (SteadyError * deltaTime);
// Calculate derivative term
float derivative = Kd * ((error - PrevError) / deltaTime);
// Add them all together to get the correction delta
// Set the steering servo to the correction
Steering.Degree = 90 + proportional + integral + derivative;
// We have applied the correction, so we need to *immediately* record the
// absolute time for generation of deltaTime in the next frame
LastCorrectionTime = DateTime.Now.Ticks;
// At this point, the current PID frame is finished
// ------------------------------------------------------------
// Now, we need to setup for the next PID frame and close out
// The "current" error is now the previous error
// (Remember, we are done with the current frame, so in
// relative terms, the previous frame IS the "current" frame)
PrevError = error;
// Done
BoardLED.Write(false);
}
Does anyone have any idea why this is happening or how to fix it?
It looks like you are applying your time base to the integral three times.
Error is already the accumulated error since the last sample so yo don't need to multiply deltaTime times it. So I would change the code to the following.
SteadyError += error ;
SteadyError is the integral or sum of error.
So the integral should just be SteadyError * Ki
float integral = Ki * SteadyError;
Edit:
I have gone through your code again and there are several other items that I would fix in addition to the above fix.
1) You don't want delta time in milliseconds. In a normal sampled system the delta term would be one but you are putting in a value like 50 for the 20Hz rate this has the effect of increasing Ki by this factor and decreasing Kd by a factor of 50 as well. If you are worried about jitter then you need to convert delta time to a relative sample time. I would use the formula instead.
float deltaTime = (LastCorrectionTime - DateTime.Now.Ticks) / 500000.0
the 500000.0 is the number of expected ticks per sample which for 20Hz is 50ms.
2) Keep the integral term within a range.
if ( SteadyError > MaxSteadyError ) SteadyError = MaxSteadyError;
if ( SteadyError < MinSteadyError ) SteadyError = MinSteadyError;
3) Change the following code so that when error is around -180 you do not get a step in error with a small change.
if (error < -270) error += 360;
if (error > 270) error -= 360;
4) Verify Steering.Degree is receiving the correct resolution and sign.
5) Lastly yo can probably just drop deltaTime all together and calculate the differential term the following way.
float derivative = Kd * (error - PrevError);
With all of that your code becomes.
private static void DoPID(object o)
{
// Bring the LED up to signify frame start
BoardLED.Write(true);
// Get IMU heading
float currentHeading = (float)RazorIMU.Yaw;
// Calculate error
// (let's just assume CurrentHeading really is the current GPS heading, OK?)
float error = (TargetHeading - currentHeading);
LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2");
// We calculated the error, but we need to make sure the error is set
// so that we will be correcting in the
// direction of least work. For example, if we are flying a heading
// of 2 degrees and the error is a few degrees
// to the left of that ( IE, somewhere around 360) there will be a
// large error and the rover will try to turn all
// the way around to correct, when it could just turn to the right
// a few degrees.
// In short, we are adjusting for the fact that a compass heading wraps
// around in a circle instead of continuing infinity on a line
if (error < -270) error += 360;
if (error > 270) error -= 360;
// Add the error calculated in this frame to the running total
SteadyError += error;
if ( SteadyError > MaxSteadyError ) SteadyError = MaxSteadyError;
if ( SteadyError < MinSteadyError ) SteadyError = MinSteadyError;
LCD.Lines[2].Text = "Error: " + error.ToString("F2");
// Calculate proportional term
float proportional = Kp * error;
// Calculate integral term
float integral = Ki * SteadyError ;
// Calculate derivative term
float derivative = Kd * (error - PrevError) ;
// Add them all together to get the correction delta
// Set the steering servo to the correction
Steering.Degree = 90 + proportional + integral + derivative;
// At this point, the current PID frame is finished
// ------------------------------------------------------------
// Now, we need to setup for the next PID frame and close out
// The "current" error is now the previous error
// (Remember, we are done with the current frame, so in
// relative terms, the previous frame IS the "current" frame)
PrevError = error;
// Done
BoardLED.Write(false);
}
Are you initializing SteadyError (bizarre name...why not "integrator")? If it contains some random value on start-up it might never return to near zero (1e100 + 1 == 1e100).
You might be suffering from integrator windup, which ordinarily should go away, but not if it takes longer to diminish than it does for your vehicle to complete a full rotation (and windup the integrator again). The trivial solution is to impose limits on the integrator, though there are more advanced solutions (PDF, 879 kB) if your system requires.
Does Ki have the correct sign?
I would strongly discourage the use of floats for PID parameters because of their arbitrary precision. Use integers (maybe fixed point). You will have to impose limit checking, but it will be much more sane than using floats.
The integral term is already accumulated over time, multiplying by deltaTime will make it accumulate at a rate of time-squared. In fact since SteadyError is already erroneously calculated by multiplying error by deltaTime, that is time-cubed!
In SteadyError, if you are trying to compensate for an aperiodic update, it would be better to fix the aperiodicity. However, the calculation is flawed in any case. You have calculated in units of error/time whereas you want just error units. The arithmentiaclly correct way to compensate for timing jitter if really necessary would be:
SteadyError += (error * 50.0f/deltaTime);
if deltaTime remains in milliseconds and the nominal update rate is 20Hz. However deltaTime would be better calculated as a float or not converted to milliseconds at all if it is timing jitter you are trying to detect; you are needlessly discarding precision. Either way what you need is to modify the error value by the ratio of nominal time to actual time.
A good read is PID without a PhD
I'm not sure why your code isn't working, but I'm almost positive you can't test it to see why, either. You might inject a timer service so you can mock it out and see what's happening:
public interace ITimer
{
long GetCurrentTicks()
}
public class Timer : ITimer
{
public long GetCurrentTicks()
{
return DateTime.Now.Ticks;
}
}
public class TestTimer : ITimer
{
private bool firstCall = true;
private long last;
private int counter = 1000000000;
public long GetCurrentTicks()
{
if (firstCall)
last = counter * 10000;
else
last += 3500; //ticks; not sure what a good value is here
//set up for next call;
firstCall = !firstCall;
counter++;
return last;
}
}
Then, replace both calls to DateTime.Now.Ticks with GetCurrentTicks(), and you can step through the code and see what the values look like.
I have a collision resolution method in my physics engine, that goes like this:
Vector2 n1pos = n1.NonLinearSpace != null ? n1.NonLinearPosition : n1.Position;
Vector2 n2pos = n2.NonLinearSpace != null ? n2.NonLinearPosition : n2.Position;
Vector2 posDiff = n2pos - n1pos;
Vector2 posDiffNormal = posDiff;
posDiffNormal.Normalize();
float totalRadius = n1.Radius + n2.Radius;
float posDiffLength = posDiff.Length();
float interPenetration = totalRadius - posDiffLength;
float averageRestitution = (n1.RestitutionCoefficient + n2.RestitutionCoefficient) / 2;
Vector2 forceAmount = Vector2.Multiply(posDiffNormal, interPenetration);
Vector2 n1force =
(
(n1.Velocity * n1.Mass) +
(n2.Velocity * n2.Mass) +
n2.Mass * averageRestitution * (n2.Velocity - n1.Velocity)
) /
(n1.Mass + n2.Mass);
Vector2 n2force =
(
(n1.Velocity * n1.Mass) +
(n2.Velocity * n2.Mass) +
n1.Mass * averageRestitution * (n2.Velocity - n1.Velocity)
) /
(n1.Mass + n2.Mass);
n1.ApplyForce(???);
if (!n1.IsStatic)
{
n1.Position += ???;
}
n2.ApplyForce(???);
if (!n2.IsStatic)
{
n2.Position += ???;
}
Now, i can't figure out what to apply to the bodies in my engine in order to get proper coefficient of restitution working. (the ??? parts). Can someone help?
Judging by this and your other questions, you are trying to run before you can walk.
You're trying to code several unfamiliar things at once, when you should be tackling them one at a time. Try setting the coefficient of restituion to 0, so that your objects act like lumps of putty. That'll be easier to code, and once you have that working you can try elastic collisions.
No offense, but trying to write a physics engine when you haven't studied basic physics is just masochistic. You can either sit down with a textbook or experiment, but there's simply no way to get the engine working without understanding its parts.
I realize this is an old question, but I ran into the same issue and Google turned up this page. I figured I might share my findings. First, you must realize that the coefficient of restitution is a property of the collision, not of either of the bodies involved in the collision. That is, for n objects, you need to define n(n-1)/2 coefficients of restitution (one for each pair of bodies).
However, the physics engines I have looked into (Bullet, Chipmunk, and Box2d) all define the restitution as a property of the bodies. Upon the time of the collision, they combine the two bodies' coefficients of restitution into a single value and use that in the collision resolution. Obviously, this isn't physically correct. But that doesn't matter much for games: it just needs to behave in an intuitive manner. Here are the restitution functions that those physics engines use:
Bullet: restitution = body1->restitution * body2->restitution
Chipmunk: restitution = body1->restitution * body2->restitution
Box2d: restitution = max(body1->restitution, body2->restitution)
Box2d allows the users to customize the restitution function in a configuration file. Bullet and Chipmunk do not.
I recommend you select whatever restitution mixing function feels best. Just play around with it a bit and see what you like.