When we use out or ref inside calculations, with multiple assignments and reading from it, what drawbacks does it have? Will it hurt performance?
static bool TrySomeFunction(int x, int y, out int result)
{
result = 8;
for (int i = 0; i < x; i++)
{
result += result + x;
if (result == y)
return false;
}
return true;
}
Or should we better be using additional variable:
static bool TrySomeFunction(int x, int y, out int result)
{
int temp = 8;
for (int i = 0; i < x; i++)
{
temp += temp + x;
if (temp == y)
{
result = 0;
return false;
}
}
result = temp;
return true;
}
Update: changed function name from SomeFunction to make it more clear for intended use.
It turns out that the more calculations we do the more the difference between the performance of both.
I believe this is expected, since here we see an extra level of indirection. ldind and stind operations used to get/set the value for out parameter (indirectly) and ldoc with stloc used to get/set values for local variables.
I think that compiler can't do any optimizations here (at least convert UseOutExtensively to DontUseOutExtensively), because this might change the behavior of the method if some other thread writes to the location of out parameter at the same time the function is executed.
Let me a bit simplify your function so that we concentrate on what we're interested in only:
void UseOutExtensively(out int result)
{
result = 0;
for (int i = 0; i < 100; i++)
{
int temp = result;
result = temp;
}
}
void DontUseOutExtensively(out int result)
{
int temp = 8;
for (int i = 0; i < 100; i++)
{
int anotherTemp = temp;
temp = anotherTemp;
}
result = temp;
}
So the functions don't do anything useful, they just swap the same value between the variables. Thus we don't have complex additions and conditions, only get/set an out variable and get/set a local variable.
So the test program is the following:
int Iterations = 10000000; // we'll try 10^7, 10^8 && 10^9
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
UseOutExtensively(out int result);
Console.WriteLine("Using out extensively: {0}",
sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
DontUseOutExtensively(out int result);
Console.WriteLine("Don't use out extensively: {0}",
sw.ElapsedMilliseconds);
Results:
| Iterations | UseOutExtensively | DontUseOutExtensively |
|---|---|---|
| 10^7 | 918 | 330 |
| 10^8 | 8850 | 3331 |
| 10^9 | 92009 | 34823 |
We see the more operations we perform the more the difference in performance is noticeable.