matko,
In one of the two examples for the PaintEventArgs.Graphics-property (in
the VS 2005 documentation), the Graphics-object is "saved" to a local
variable, before being used. In the other example, no such saving is
done. Why was the Graphics-object saved in one of the examples? Is that
really necessary?
I have a story to tell -- with data. I hope that doesn't scare anyone off.
The question posed by matko has at least two aspects; 1) style and 2)
performance. Style is the first most important aspect when writing
production code. You (and your team) should pick a style and then make sure
that all code follows that style. Having all code written in the same style
will be of unmeasurable value for as long as the code lives.
At some point, performance may become an issue for some parts of the code.
But first impressions are deceiving; especially in this case.
To illustrate this point, I put together a project in MS VisualStudio 2005
(Beta 2 release) that shows the performance difference between 1) direct
access, 2) access through properties, 3) access through getters and 4)
cached values retrieved from getters. I will include the code at the end of
this post. Note that I am running the code on a 2.8GHz HP Pentium 4 Laptop.
The code adds 3 and 5 in a loop that cycles 1000000000 times. The resulting
output when running the code in the IDE using the Debug build is:
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:12.6093750 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:53.2031250 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:38.1093750 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:07.3750000 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:07.3281250 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:37.8437500 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:53.1562500 sec.
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:12.5625000 sec.
Note that I ran the code twice, once in the given order and then another
time in the reverse order as a sanity check. This output shows that direct
access is almost twice as fast as the best of the other approaches. How
much impact does running the debug code in the IDE have? The resulting
output was obtained by running the Debug build from a command prompt:
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:12.5937500 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:45.5937500 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:36.9843750 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:07.2968750 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:07.2968750 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:37.1093750 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:46.1406250 sec.
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:12.5781250 sec.
We might expect that the results should get a little better because there is
no IDE overhead. It is interesting that the worst performer improved the
most. This output shows that direct access is still the clear performance
winner, however. Now examine the output when running the Release build from
a command prompt:
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:10.7031250 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:09.7031250 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:10.6406250 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:10.7031250 sec.
[Direct Access] The sum is: 8000000000 in : 00:00:10.7031250 sec.
[Access using properties] The sum is: 8000000000 in : 00:00:10.5937500 sec.
[Access using getters] The sum is: 8000000000 in : 00:00:09.6875000 sec.
[Access using cached values from getters] The sum is: 8000000000 in :
00:00:10.7031250 sec.
Wow -- they are all nearly equivalent and the worst "debug" performer has a
slight edge over the other approaches. See what I mean. When it comes to
judging performance of code, first impressions can be deceiving. For
production code, there is essentially no performance tradeoff for choosing
one style of accessing over another for case analogous to the example code I
ran. Don't believe anyone who tells you that there is a performance impact.
Pick the coding style that is best for you and your team and then follow it.
And worry about performance once you have an application that consistently
follows that style where you have measured and identified the real
performance bottlenecks.
Regards,
Randy
Example code follows:
================================================== ====
using System;
namespace CachedVariableProfile
{
public class Program
{
static void Main(string[] args)
{
testWithCachedGetterValues();
testWithAccessUsingGetters();
testWithAccessUsingProperties();
testWithDirectAccess();
testWithDirectAccess();
testWithAccessUsingProperties();
testWithAccessUsingGetters();
testWithCachedGetterValues();
}
public static void testWithAccessUsingProperties()
{
Container dataSupplier = new Container(3, 5);
long sum = 0;
DateTime endTime;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 1000000000; i++)
{
sum += dataSupplier.MyValue.X;
sum += dataSupplier.MyValue.Y;
}
endTime = DateTime.Now;
System.Console.Out.WriteLine("[Access using properties] The sum
is: " + sum.ToString() + " in : " + (endTime - startTime) + " sec.");
}
public static void testWithAccessUsingGetters()
{
Container dataSupplier = new Container(3, 5);
long sum = 0;
DateTime endTime;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 1000000000; i++)
{
sum += dataSupplier.getX();
sum += dataSupplier.getY();
}
endTime = DateTime.Now;
System.Console.Out.WriteLine("[Access using getters] The sum is:
" + sum.ToString() + " in : " + (endTime - startTime) + " sec.");
}
public static void testWithCachedGetterValues()
{
Container dataSupplier = new Container(3, 5);
long sum = 0;
DateTime endTime;
DateTime startTime = DateTime.Now;
int x = dataSupplier.getX();
int y = dataSupplier.getY();
for (int i = 0; i < 1000000000; i++)
{
sum += x;
sum += y;
}
endTime = DateTime.Now;
System.Console.Out.WriteLine("[Access using cached values from
getters] The sum is: " + sum.ToString() + " in : " + (endTime - startTime) +
" sec.");
}
public static void testWithDirectAccess()
{
Container dataSupplier = new Container(3, 5);
long sum = 0;
DateTime endTime;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 1000000000; i++)
{
sum += dataSupplier.myValue.x;
sum += dataSupplier.myValue.y;
}
endTime = DateTime.Now;
System.Console.Out.WriteLine("[Direct Access] The sum is: " +
sum.ToString() + " in : " + (endTime - startTime) + " sec.");
}
}
public class Container
{
public Containee myValue;
public Container(int x, int y)
{
myValue = new Containee(x, y);
}
public Containee MyValue
{
get
{
return myValue;
}
}
public int X
{
get
{
return myValue.X;
}
set
{
myValue.X = value;
}
}
public int Y
{
get
{
return myValue.Y;
}
set
{
myValue.Y = value;
}
}
public Containee getMyValue()
{
return myValue;
}
public int getX()
{
return getMyValue().getX();
}
public int getY()
{
return getMyValue().getY();
}
}
public class Containee
{
public int x;
public int y;
public Containee(int x, int y)
{
X = x;
this.y = y;
}
public int X
{
get
{
return x;
}
set
{
x = value;
}
}
public int Y
{
get
{
return y;
}
set
{
y = value;
}
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
}
}