Everyone seems to be missing the important point about ++i vs i++
optimization...
Namely it comes in play mostly on user defined types with an overload
operator ++.
You're just not going to see the difference using an int.
To understand this, imagine we have a class (MyClass), which has a
member function (AddOne), and we want to implement op++ and ++op. (For the
moment, I'm going to do this in C++)
class MyClass
{
public:
void AddOne() {.....}
// The Pre-fix (++x) operator is rather straight forward.
MyClass& operator++()
{
AddOne();
return *this;
}
// the post-fix (x++) is a bit trickier
MyClass operator++(int)
{
MyClass temp(*this);
AddOne();
return temp;
}
}
Note that for the post fix, we've got to make two copies of MyClass (one
in temp, and one for the return), which aren't needed in the pre-fix op++.
Also, note that I could have written the post-fix as:
MyClass operator++(int)
{
MyClass temp(*this);
this->operator++(); // or ++(*this);
return temp;
}
Now, let's write a similar class in C#:
class MyClass
{
public void AddOne() {.....}
public static MyClass operator++(MyClass a)
{
a.AddOne();
return a;
}
}
You'll note that this is the pre-fix operator++. So, where is the
post-fix? The C# compiler creates it for you automatically. And is it
written? Basically:
public static MyClass operator++(MyClass a) // post-fix
{
MyClass b = a.Clone();
++a;
return b;
}
So, like in the C++, the post-fix has to create a copy of the unaltered
object, so that it can be returned.
When an int is used, the operator method is inlined, so, when it's just
"x++;" on a line by itself, the compile can see that the duped value isn't
used, and it can remove the whole creation of it. When ++ is used on some
other type object, the oper++ is done out-of-line, and so, the compiler
cannot remove it.
So, now, let's try your test again, but this using a class:
using System;
public class LikeAnInt
{
int n =0;
public LikeAnInt(int i)
{
n = i;
}
public void AddOne()
{
++n;
}
static public LikeAnInt operator++(LikeAnInt a)
{
a.AddOne();
return a;
}
public int Val
{
get { return n; }
}
}
public class MyClass
{
public static void Main()
{
DateTime d;
TimeSpan t;
int iMax = 1000000000;
int j;
// Test 1 : i++
j = 0;
d = DateTime.Now;
for (LikeAnInt i = new LikeAnInt(0); i.Val < iMax; i++)
j += i.Val;
t = DateTime.Now - d;
Console.WriteLine("i++ : {0}", t.TotalMilliseconds.ToString());
// Test 2 : ++i
j = 0;
d = DateTime.Now;
for (LikeAnInt i = new LikeAnInt(0); i.Val < iMax; ++i)
j += i.Val;
t = DateTime.Now - d;
Console.WriteLine("++i : {0}", t.TotalMilliseconds.ToString());
// Test 3 : i.AddOne()
j = 0;
d = DateTime.Now;
for (LikeAnInt i = new LikeAnInt(0); i.Val < iMax; i.AddOne())
j += i.Val;
t = DateTime.Now - d;
Console.WriteLine("i.AddOne : {0}", t.TotalMilliseconds.ToString());
Console.Read();
}
}
The Results:
SnippetCompiler:
i++ : 44206.6704
++i : 42848.6265
Approx 3% faster
VC#-Debug:
i++ : 34887.6795
++i : 34731.5825
Approx 0.3% faster
VC#-Release:
i++ : 1623.4088
++i : 1607.7991
i.AddOne : 1607.7991
Approx 0.9% faster
A minmal difference, but it's beyond the "noise" level.
--
--
Truth,
James Curran
[erstwhile VC++ MVP]
Home:
www.noveltheory.com Work:
www.njtheater.com
Blog:
www.honestillusion.com Day Job:
www.partsearch.com