C# very optimisation

  • Thread starter Thread starter Ennixo
  • Start date Start date
Like what? (seriously - I'm not trying to be argumentative on that
point - I'd really like to know what the small difference is).

For any type T, post and prefix increment must (roughly) the following
definition, exemplified as something rather similar to an iterator here:

struct T {
T(const T& t_): /* copy state */ {}
const T& operator=(const T& other)
{ /* copy state*/ ; return *this; }
void next() { /* move to next position */ }

const T& operator++() { next(); return *this; }
const T operator++(int) { T old(*this); next(); return old; }
}

The difference is

- a copy-construction of T -- T::T(const T&)
- a return-by-value of T -- T::operator=(const T&)

C++ allows the compiler to replace the return-by-value with a
copy-construction into space allocated by the caller, and is also
allowed to remove the double copy-construction resulting after the
previous optimization. This means that in general the effective
difference is:

- copy-construction of T -- T::T(const T&)

If the *definition* of T::T(const T&) is available at the call-site of
T::operator++(int) then *that* is a candidate for futher optimization,
so the difference mostly hinges on:

- the expense of T::T(const T&)
- the compilers ability to do optimizations, especially on T::T(const T&)

Since most iterators are implemented as thin wrappers around pointer
manipulation, and declared inline, using ++i or i++ as statements
doesn't matter to performance at all, even on iterators. So don't expect
the performance of your program to change because you change from i++ to
++i.

That, however, is not really a good reason to just using i++ generally.

If you think "i++" is a more readable *statement* than "++i" -- that's
fine by me, but consider whether

for (IT it = begin; it != end; it++)

is really more readable than

for (IT it = begin; it != end; ++it)

or if it's just that you've always just seen: "i++" and thus like it to
stay that way.
 
No, "++i" and "i++" are *both* expressions. "i = i + 1" and "i += 1",
are statements. Using "++i" as a statement is equivalent to "i = i + 1",
or "i += 1".

Remember that the syntax of "for" is

'for' '(' STMT ';' EXPR ';' STMT ')' BLOCK_OR_STMT

not:

'for' '(' STMT ';' EXPR ';' EXPR ')' BLOCK_OR_STMT

Since when is i+=1 not an expression???
 
My god, it is... the horror!

Never seen "i+=e", used as an expression though.

Using assignments as expressions isn't the most common language pattern.
The most common use I think is the case where you wan't to break on a
certain value, for example:

while ((line = sr.ReadLine()) != null) { /* process line */ }

I have never seen i+=e either though. Now that I think about it, writing
i+=1 is pretty much the same as writing ++i, except that ++i never requires
paranthesis around it.
 
cody said:
Since when is i+=1 not an expression???

i++ and ++i are marked expressions I think because it would make crappy
C coders feel at home, so they could continue writing awful code like:

foo = arr[i++];

or worse:

foo = arr[++i];

i+=1 is short for an assignment, like foo = arr; is too. You have a
point that it's not consistent with i++ which is the same as i+=1, and
I'm with you on that, but I think you can see this as one of the silly
things of C#, like the 'break;' statement after a case block which is
mandatory though fall-through is not supported (so 'break;' is a
complete useless statement in a case block.)

Frans

--
 
Well is does have one use

switch(i)
{
case 0:
// Do some stuff
break;

case 1:
case 2:
case 3:
// Do some other stuff
break;
case 4:
break; // this explcitly stops us joining case 5 but states we don;t want to do anything
case 5:
// yet more stuff
break;
default:
// raise error as we should never get here
break;
}

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

I'm with you on that, but I think you can see this as one of the silly
things of C#, like the 'break;' statement after a case block which is
mandatory though fall-through is not supported (so 'break;' is a
complete useless statement in a case block.)

Frans
 
If your app isn't performing well enough, get a profiler and find out
exactly where the bottleneck is. At that point, and only at that point,
should you be really worried about micro-optimisations.

Haven't you ever noticed C# doing funny things though?

For instance, in profiling that I've done for my components, I've noticed a
*severe* performance hit when I use "this.method()" instead of just
"method()". You wouldn't think it would be significantly slower, but it
really seems to be from my profiling tests.

Another way to make your code faster is to never use foreach loops.

foreach (someObj obj in myArrayList)
obj.method()

can be performed much faster if done as:

for (int cnt = 0; cnt < myArrayList.Length; cnt++)
((someObj) myArrayList[cnt]).method()

I think that's the sort of micro-optimizations he's asking for.

I, too, would be interested in such a list if anyone knows of one.

There's more than one way to do it, but unfortunately, so many of the
possible ways are very bad. I want to know the *right* way to do something,
and I've found very little information on how to do it.

--clint
 
Clint Herron said:
Haven't you ever noticed C# doing funny things though?

For instance, in profiling that I've done for my components, I've noticed a
*severe* performance hit when I use "this.method()" instead of just
"method()". You wouldn't think it would be significantly slower, but it
really seems to be from my profiling tests.

Could you post a short but complete program which demonstrates that?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.

I wouldn't expect the binaries to be any different at all.
Another way to make your code faster is to never use foreach loops.

Not significantly, IME.
foreach (someObj obj in myArrayList)
obj.method()

can be performed much faster if done as:

for (int cnt = 0; cnt < myArrayList.Length; cnt++)
((someObj) myArrayList[cnt]).method()

What exactly do you mean by "much" faster?
I think that's the sort of micro-optimizations he's asking for.

I, too, would be interested in such a list if anyone knows of one.

There's more than one way to do it, but unfortunately, so many of the
possible ways are very bad. I want to know the *right* way to do something,
and I've found very little information on how to do it.

The right way to do something is the most readable way - look how much
more readable it is to use foreach than to use the for loop. Very, very
few applications have bottlenecks due to this kind of usage rather than
algorithmic or IO-based bottlenecks.
 
"Never use foreach loops" -- that advice is specific to the collection.
ArrayList used a class for an enumerator, so it is slower that way. But if
you implement your enumerator as a struct the foreach loop is as fast as the
indexer loop, when creating your own array-list type.

The standard design guideline from Microsoft now is to use a struct for an
enumerator type. This came as a result of experience with ArrayList. This is
another example where a struct must be mutable for performance.

Regards,
Frank Hileman

check out VG.net: http://www.vgdotnet.com
Animated vector graphics system
Integrated Visual Studio .NET graphics editor


Clint Herron said:
If your app isn't performing well enough, get a profiler and find out
exactly where the bottleneck is. At that point, and only at that point,
should you be really worried about micro-optimisations.

Haven't you ever noticed C# doing funny things though?

For instance, in profiling that I've done for my components, I've noticed
a
*severe* performance hit when I use "this.method()" instead of just
"method()". You wouldn't think it would be significantly slower, but it
really seems to be from my profiling tests.

Another way to make your code faster is to never use foreach loops.

foreach (someObj obj in myArrayList)
obj.method()

can be performed much faster if done as:

for (int cnt = 0; cnt < myArrayList.Length; cnt++)
((someObj) myArrayList[cnt]).method()

I think that's the sort of micro-optimizations he's asking for.

I, too, would be interested in such a list if anyone knows of one.

There's more than one way to do it, but unfortunately, so many of the
possible ways are very bad. I want to know the *right* way to do
something,
and I've found very little information on how to do it.

--clint
 
Hey Jon! Thanks for the reply!
Could you post a short but complete program which demonstrates that?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.

Hrm. You know, I can't reproduce this right now. That's odd. I'm sure I saw
performance hits on my code, and noticed a significant improvement in my
profiler runs when I changed this before.

It might have had something to do with inherited classes, but I dunno'.
Okay, well you got me on this one, I can't show it right now. Hrm. I'll keep
stewing on this one.
I wouldn't expect the binaries to be any different at all.


Not significantly, IME.

What exactly do you mean by "much" faster?

On the order of 250% (my results right now are showing around 278%)? Would
you consider that significant?

For instance, I have an application where I'm calculating high resolution
phase and magnitude response curves in real-time, so I've got thousands of
data points that I'm looping through at 15-20 fps -- so spending a lot of
unnecessary time on MoveNext is a big deal to me.

Here's a small sample app:
using System;
using System.Collections;

namespace SpeedTest
{
class MainClass
{
public static void Main(string[] args)
{
ArrayList randVals = new ArrayList(1000000);
Random myRand = new Random();

for (int cnt = 0; cnt < 1000000; cnt++)
randVals.Add (myRand.Next());

int minVal = 0;
System.DateTime Time1;
System.DateTime Time2;

System.DateTime InitTime = System.DateTime.Now;

for (int cnt = 0; cnt < 100; cnt++)
minVal = Min1(randVals);

Console.WriteLine("Min value is: " + minVal.ToString());
Time1 = System.DateTime.Now;

for (int cnt = 0; cnt < 100; cnt++)
minVal = Min2(randVals);

Console.WriteLine("Min value is: " + minVal.ToString());
Time2 = System.DateTime.Now;

System.TimeSpan TimeEl1 = Time1 - InitTime;
System.TimeSpan TimeEl2 = Time2 - Time1;

Console.WriteLine("First search took: " + TimeEl1.ToString());
Console.WriteLine("Second search took: " + TimeEl2.ToString());
}

public static int Min1(ArrayList vals)
{
int currentMin = (int) vals[0];

for (int cnt = 0; cnt < vals.Count; cnt++)
if ((int) vals[cnt] < currentMin)
currentMin = (int) vals[cnt];

return currentMin;
}

public static int Min2(ArrayList vals)
{
int currentMin = (int) vals[0];

foreach (int curVal in vals)
if (curVal < currentMin)
currentMin = curVal;

return currentMin;
}
}
}
The right way to do something is the most readable way - look how much
more readable it is to use foreach than to use the for loop. Very, very
few applications have bottlenecks due to this kind of usage rather than
algorithmic or IO-based bottlenecks.

*shrug*. When profiling with NProf, I notice a lot of time is spent going to
collection indexers for MoveNext(), when I could just be incrementing a
counter and going to the next index.

I agree -- I like the way that foreach looks also, but when I notice a
significant performance hit when I use foreach, I need to optimize. I wrote
everything with foreach when I was starting, and then moved away from it
after everything was working and the boss was complaining about speed issues.

It's frustrating to me that not more information is readily available about
such optimization issues.

BTW, for those who don't want to run the program, here were my results
(repeatable and irrespective of which search went first):

Min value is: 1669
Min value is: 1669
First search took: 00:00:01.2323001
Second search took: 00:00:03.4364141


Respectfully,
clint
 
Clint Herron said:
Hrm. You know, I can't reproduce this right now. That's odd. I'm sure I saw
performance hits on my code, and noticed a significant improvement in my
profiler runs when I changed this before.

It might have had something to do with inherited classes, but I dunno'.
Okay, well you got me on this one, I can't show it right now. Hrm. I'll keep
stewing on this one.

I'll believe it when I see it :)
On the order of 250% (my results right now are showing around 278%)? Would
you consider that significant?

Only if i do virtually nothing in loops with 100,000,000 iterations
(effectively) like you are in your test case. I very rarely have a loop
where the iteration is actually the bottleneck.

Note that using an array rather than an array list would take the time
down by a factor of 10, and then foreach is faster...
For instance, I have an application where I'm calculating high resolution
phase and magnitude response curves in real-time, so I've got thousands of
data points that I'm looping through at 15-20 fps -- so spending a lot of
unnecessary time on MoveNext is a big deal to me.

You'd be better off using an array then, at which point the more
readable solution is roughly the same speed as the solution using a for
loop, and much faster than using an ArrayList.

For most apps, however, loops aren't as trivial as the one you posted,
and the bottleneck isn't in iteration.

Even if it *is* in an iteration, it tends to be localised - it's worth
considering micro-optimisations *only* when you've found the
bottleneck.
*shrug*. When profiling with NProf, I notice a lot of time is spent going to
collection indexers for MoveNext(), when I could just be incrementing a
counter and going to the next index.

I agree -- I like the way that foreach looks also, but when I notice a
significant performance hit when I use foreach, I need to optimize. I wrote
everything with foreach when I was starting, and then moved away from it
after everything was working and the boss was complaining about speed issues.

And that's the time to find the bottleneck, and optimise *just* there.
Everywhere other than the bottleneck should use the most readable code.
It's frustrating to me that not more information is readily available about
such optimization issues.

BTW, for those who don't want to run the program, here were my results
(repeatable and irrespective of which search went first):

Min value is: 1669
Min value is: 1669
First search took: 00:00:01.2323001
Second search took: 00:00:03.4364141

My results weren't quite as bad as those - about the same time for the
first search, but always under 3 for the second.
 
Hey Jon!
I'll believe it when I see it :)

hehe, okay. :)
You'd be better off using an array then, at which point the more
readable solution is roughly the same speed as the solution using a for
loop, and much faster than using an ArrayList.

Touche', I'm actually using an array. Sry.

However, I will note that MoveNext() takes up a fair chunk of time in my
profile, just from everywhere else I'm using ArrayLists. Granted, it's called
millions of times, but still, it's time.
And that's the time to find the bottleneck, and optimise *just* there.
Everywhere other than the bottleneck should use the most readable code.

Good call, and yeah, I'll agree with you there.

"Premature optimization is the root of many evils."

However, sometimes this can go overboard. One good example is hashtables,
vs. and array with an enumerated indexer.

Question:

is

MyCollection["Name"]

more readable than

MyCollection[DataFields.Name]

?

Answer:

Slightly, yes.

But hashtables take an enormous amount of overhead, and using an enumeration
isn't that much more confusing (plus it can help prevent typos regarding
mispelling the index).

Overall though, I think you're right on everything that you've been saying.
But when I am coding for speed, I would like to know some of the little
things that I can do to speed stuff and generate cleaner code.
My results weren't quite as bad as those - about the same time for the
first search, but always under 3 for the second.

Heh, you should have seen it before I tweaked the numbers -- I was getting
*long* run times, and almost crashed my computer. After I finally regained my
desktop (thankfully didn't have to restart -- yay for managed code!), task
manager said that my memory usage peaked out at one point at 1.5 gigs --
phew! It was chuggin'. :)

Thanks again! It's been very nice chatting with you, Jon.

Respectfully,
clint
 
For instance, in profiling that I've done for my components, I've noticed
a
*severe* performance hit when I use "this.method()" instead of just
"method()". You wouldn't think it would be significantly slower, but it
really seems to be from my profiling tests.

I have tested this, and there is absolutelly no difference in IL generated
for this.Method() calls and for Method() calls.

in first case, I have used

this.InitializeComponent();

and in the other, I have used just

InitializeComponent();

the generated IL looks absolutely the same in both cases.


..method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 20 (0x14)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: stfld class [System]System.ComponentModel.Container
ThisDotTest.Form1::components
IL_0007: ldarg.0
IL_0008: call instance void
[System.Windows.Forms]System.Windows.Forms.Form::.ctor()
IL_000d: ldarg.0
IL_000e: call instance void
ThisDotTest.Form1::InitializeComponent()
IL_0013: ret
} // end of method Form1::.ctor

there is also no way to indicate this.Method() against Method() when writing
IL code, of course ;-)
Where is your *severe* performance hit?

Sincerely,
Lebesgue
 
Back
Top