Why should I create a local version of an object?

M

matko

Hi!

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?

Example #1:

private void pictureBox1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Create a local version of the graphics object for the PictureBox.
Graphics g = e.Graphics;

// Draw a string on the PictureBox.
[snip]
// Draw a line in the PictureBox.
[snip]
}

Example #2:

// Uses e.Graphics directly without "creating a local version"...
private void Form1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Draw the rectangle...
e.Graphics.DrawRectangle(new Pen(Color.Blue, PenWidth), RcDraw);
}
 
G

Guest

Although you have said, "saved" in the first example, basically in
first example, you are creating a local variable that "points" to the
reference of "e.Graphics". A lot of people tend to go with it because
it saves you from having to write "e.Graphics" all the time.

Also, it would be a good practice to use "using" statement around
"Grahpics g = e.Grahpics" or call "Dispose" method directly (since
Graphics Class implements IDisposable interface).
 
M

Michael C

matko said:
// Create a local version of the graphics object for the PictureBox.
Graphics g = e.Graphics;

If doesn't make a huge difference in the way in which the code works but it
saves some time because you don't have to retrieve the Graphics object from
the event args over and over.

Michael
 
G

Guest

matko, a Graphics object is an instance of a class (not a struct) so it is a
reference type. As a result, code such as this:

Graphics g = e.Graphics;

.... doesn't copy the Graphics object--it copies a reference to that object.
In Debug mode at least, this will give you a slight performance boost since
it avoids any subsequent calls to the property "getter" (which is actually a
function call in IL, to a method with the special name "get_Graphics").

In Release mode, where optimization is normally enabled, the C# or JIT
compiler may decide to cache the result value anyway. It all depends on
whether the compiler "knows" that the Graphics property returns an invariant
result within each invocation of a Paint event handler, and on whether the
optimizer decides that caching that value is the best use of stack memory or
an available register.

--best regards
 
R

Randy A. Ynchausti

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;
}
}
}
 
S

Steven Nagy

Randy, thats the best post to ever hit this news group, except that
once when that girl ment to post to another group about her other girl
friend.... you know the one I'm talking about....
 
M

Marc Gravell

Re "using" - really?

I don't tend to do this with e.Graphics, since I assume that since I am
using a property getter, I am accessing an object that already exists and is
managed for a duration by the containing object (under the methodology that
property getters shouldn't normally change internal state - where-as
functions may). My assumption is this graphics object may need to be passed
to other code (other subscribers to the same event, perhaps), and so this is
going to be disposed of by the method firing the event.

However, if I had called CreateGraphics, I would always use using.

Checking the documentation and examples for CreateGraphics() and
PaintEventArgs, they seem consistent with what I have said above... I also
notice that PaintEventArgs is disposable, and I suspect (without checking)
that this disposes of the graphics object.

So - is disposing of e.Graphics really a good idea?

Marc
 
J

Jon Skeet [C# MVP]

Marc said:
Re "using" - really?

I don't tend to do this with e.Graphics, since I assume that since I am
using a property getter, I am accessing an object that already exists and is
managed for a duration by the containing object (under the methodology that
property getters shouldn't normally change internal state - where-as
functions may). My assumption is this graphics object may need to be passed
to other code (other subscribers to the same event, perhaps), and so this is
going to be disposed of by the method firing the event.

However, if I had called CreateGraphics, I would always use using.

Your understanding is exactly the same as mine. Unless I create an
object or the docs explicitly say that I'm responsible for it, I don't
think I should be disposing of it.

Jon
 
M

matko

Wow!
Thank you!

My question has been answered!
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;
}
}
}
 
G

Guest

Randy A. Ynchausti said:
[ results of an intelligently written benchmark test which he carried out instead of just guessing ]

Those seem like typical results of release-mode compiler optimization. The
finished code apparently makes one call to get_Graphics, caches the invariant
result and bypasses all subsequent calls to the property accessor. Normal
debug-mode code can't do things like that, of course; when such shenanigans
are occurring, you can't step through the source code and see corresponding
results on a statement-by-statement basis.

Here's the thing, though: Wherever one particular optimization is clearly
the best way to speed up a function, it is very likely to be carried out. But
where a wider variety of optimizations are possible, the benefits and
resource costs (use of memory, CPU registers, etc.) are weighed against one
another and choices are made. Some functions might have many more expressions
to be evaluated, where those expressions also have common invariant elements.
In that case the optimizer's priorities might well skew in a different
direction.

In other words, we are never guaranteed that a compiler will perform any one
particular optimization even when we write the exact same source code. It
depends on all the other code that the optimizer is considering at the same
time.

Specifically, in a more complex function one might get some performance
benefit some of the time by "manually" caching the reference to the Graphics
object in a stack-based local variable. In those cases where the compiler is
already performing that optimization for itself, as you've shown, that
maneuver causes no loss in performance. So it may indeed be the "best
practice," even though in many cases (including the one which you tested) it
yields no benefit. Tests like yours should be performed on functions with a
greater range of complexity before any firm conclusions are drawn as to a
"best practice."

In the meantime, it's nice to have a peek at what optimizations the current
..NET compilers can perform when the context allows them to do so. And it's
very nice that someone would go as far as you did to investigate someone
else's "simple" question.

--best regards
 
B

Bruce Wood

As a point of style, I tend to use the "local variable" variation in
many circumstances, not for performance reasons, but because if you do
long chains of direct access then you can't see transient results in
the debugger.

For example, if you get a NullReferenceException on this line of code:

e.Graphics.DrawRectangle(new Pen(Color.Blue, PenWidth), RcDraw);

is it because "e" is null, or because "e.Graphics" is null? (Of course,
neither should be... I'm just illustrating the problem here.)

When I'm coding frantically away, I often write lines like the one
above, but then I find that later, while debugging, I introduce local
variables for intermediate results because it makes it easier to
diagnose problems.

That said, in the case of e.Graphics I probably wouldn't bother,
because the likelihood that e or e.Graphics would be null is slim to
none, and there's nothing inside a Graphics object that's of any
interest to me when I'm debugging.
 
R

Randy A. Ynchausti

Steven,
Randy, thats the best post to ever hit this news group, except that
once when that girl ment to post to another group about her other girl
friend.... you know the one I'm talking about....

I have been laughing for an hour thinking about your response. Great sense
of humor! Thanks.

Regards,

Randy
 
S

Steven Nagy

Great sense of humor! Thanks.
Unfortunately my wife doesn't usually agree.

If only my C# skills were as good as my sense of humour...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top