* Felix Palmen said:
That's what I'd assume, too, but my tests showed consistently 10%
difference (windows xp, .net 4). Maybe the difference is caused by the
underlying OS. Maybe, the article I quoted was right for CLR 2.0?
For the sake of completeness, these tests were done with a debugging
build running inside VS 2010. Release builds behave quite differently. I
compiled two release executables, one against .NET 2.0, one against 4.0,
both using VS 2010's compiler -- these are the results on windows xp:
#v+
X:\>casttest-2.exe
Running plain tests ...
trial# | direct cast | as cast
-------+--------------+--------------
1 | 3436ms | 4810ms
2 | 3436ms | 4810ms
3 | 3435ms | 4808ms
4 | 3436ms | 4810ms
5 | 3435ms | 4812ms
Running safe tests ...
trial# | direct cast | as cast
-------+--------------+--------------
1 | 6020ms | 5495ms
2 | 6022ms | 5497ms
3 | 6022ms | 5496ms
4 | 6028ms | 5496ms
5 | 6022ms | 5496ms
X:\>casttest-4.exe
Running plain tests ...
trial# | direct cast | as cast
-------+--------------+--------------
1 | 3513ms | 4465ms
2 | 3513ms | 4466ms
3 | 3512ms | 4465ms
4 | 3513ms | 4466ms
5 | 3513ms | 4464ms
Running safe tests ...
trial# | direct cast | as cast
-------+--------------+--------------
1 | 5727ms | 5156ms
2 | 5754ms | 5155ms
3 | 5726ms | 5154ms
4 | 5739ms | 5155ms
5 | 5716ms | 5152ms
#v-
It's interesting to see that even without any exception thrown, just
checking for one adds cost so that the direct cast is slower than the as
cast. This was different in the debugging build. Also, the difference is
bigger in release builds.
I didn't do a test with CLR 1 -- maybe that's where the author of the
article I found got his data (or he just made some mistake...)
So to conclude, in case of casts following the language semantics (using
direct casts when a wrong type is an error condition, as casts
otherwise) also gives the best performance.
Regards,
Felix
PS -- Testcode:
#v+
using System;
using System.Diagnostics;
namespace CastTest
{
class Test
{
private int _count;
public static void UpdateDirect(object instance)
{
++((Test)instance)._count;
}
public static void UpdateAs(object instance)
{
++(instance as Test)._count;
}
public static bool SafeUpdateDirect(object instance)
{
bool result = true;
try
{
++((Test)instance)._count;
}
catch (InvalidCastException)
{
result = false;
}
return result;
}
public static bool SafeUpdateAs(object instance)
{
Test testObject = instance as Test;
if (testObject == null) return false;
++testObject._count;
return true;
}
}
static class Program
{
private static readonly int warmupIterations = 1024;
private static readonly int iterations = 1000000000;
private static readonly int trials = 5;
private static object testDirect = new Test();
private static object testAs = new Test();
private static Stopwatch watchDirect = new Stopwatch();
private static Stopwatch watchAs = new Stopwatch();
static void Main(string[] args)
{
Console.WriteLine("Running plain tests ...");
Console.WriteLine();
Console.WriteLine("trial# | direct cast | as cast");
Console.WriteLine("-------+--------------+--------------");
for (int trial = 1; trial <= trials; ++trial)
{
watchDirect.Reset();
watchAs.Reset();
for (int i = 0; i < warmupIterations; ++i)
{
Test.UpdateDirect(testDirect);
}
watchDirect.Start();
for (int i = 0; i < iterations; ++i)
{
Test.UpdateDirect(testDirect);
}
watchDirect.Stop();
for (int i = 0; i < warmupIterations; ++i)
{
Test.UpdateAs(testAs);
}
watchAs.Start();
for (int i = 0; i < iterations; ++i)
{
Test.UpdateAs(testAs);
}
watchAs.Stop();
Console.WriteLine(string.Format(
"{0,6} | {1,10}ms | {2,10}ms", trial,
watchDirect.ElapsedMilliseconds,
watchAs.ElapsedMilliseconds));
}
Console.WriteLine();
Console.WriteLine("Running safe tests ...");
Console.WriteLine();
Console.WriteLine("trial# | direct cast | as cast");
Console.WriteLine("-------+--------------+--------------");
for (int trial = 1; trial <= trials; ++trial)
{
watchDirect.Reset();
watchAs.Reset();
for (int i = 0; i < warmupIterations; ++i)
{
Test.SafeUpdateDirect(testDirect);
}
watchDirect.Start();
for (int i = 0; i < iterations; ++i)
{
Test.SafeUpdateDirect(testDirect);
}
watchDirect.Stop();
for (int i = 0; i < warmupIterations; ++i)
{
Test.SafeUpdateAs(testAs);
}
watchAs.Start();
for (int i = 0; i < iterations; ++i)
{
Test.SafeUpdateAs(testAs);
}
watchAs.Stop();
Console.WriteLine(string.Format(
"{0,6} | {1,10}ms | {2,10}ms", trial,
watchDirect.ElapsedMilliseconds,
watchAs.ElapsedMilliseconds));
}
#if DEBUG
Console.ReadLine();
#endif
}
}
}
#v-