【发布时间】:2013-06-25 20:53:27
【问题描述】:
我写了两个等价的方法:
static bool F<T>(T a, T b) where T : class
{
return a == b;
}
static bool F2(A a, A b)
{
return a == b;
}
时差:
00:00:00.0380022
00:00:00.0170009
测试代码:
var a = new A();
for (int i = 0; i < 100000000; i++)
F<A>(a, a);
Console.WriteLine(DateTime.Now - dt);
dt = DateTime.Now;
for (int i = 0; i < 100000000; i++)
F2(a, a);
Console.WriteLine(DateTime.Now - dt);
有人知道为什么吗?
在下面的评论中,dtb* 显示CIL:
IL for F2: ldarg.0, ldarg.1, ceq, ret. IL for F<T>: ldarg.0, box !!T, ldarg.1, box !!T, ceq, ret.
我认为这是我问题的答案,但我可以使用什么魔法来拒绝拳击?
接下来我使用 Psilon 的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApplication58
{
internal class Program
{
private class A
{
}
private static bool F<T>(T a, T b) where T : class
{
return a == b;
}
private static bool F2(A a, A b)
{
return a == b;
}
private static void Main()
{
const int rounds = 100, n = 10000000;
var a = new A();
var fList = new List<TimeSpan>();
var f2List = new List<TimeSpan>();
for (int i = 0; i < rounds; i++)
{
// Test generic
GCClear();
bool res;
var sw = new Stopwatch();
sw.Start();
for (int j = 0; j < n; j++)
{
res = F(a, a);
}
sw.Stop();
fList.Add(sw.Elapsed);
// Test not-generic
GCClear();
bool res2;
var sw2 = new Stopwatch();
sw2.Start();
for (int j = 0; j < n; j++)
{
res2 = F2(a, a);
}
sw2.Stop();
f2List.Add(sw2.Elapsed);
}
double f1AverageTicks = fList.Average(ts => ts.Ticks);
Console.WriteLine("Elapsed for F = {0} \t ticks = {1}", fList.Average(ts => ts.TotalMilliseconds),
f1AverageTicks);
double f2AverageTicks = f2List.Average(ts => ts.Ticks);
Console.WriteLine("Elapsed for F2 = {0} \t ticks = {1}", f2List.Average(ts => ts.TotalMilliseconds),
f2AverageTicks);
Console.WriteLine("Not-generic method is {0} times faster, or on {1}%", f1AverageTicks/f2AverageTicks,
(f1AverageTicks/f2AverageTicks - 1)*100);
Console.ReadKey();
}
private static void GCClear()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
}
Windows 7、.NET 4.5、Visual Studio 2012,发布,优化,无需附加。
x64
Elapsed for F = 23.68157 ticks = 236815.7
Elapsed for F2 = 1.701638 ticks = 17016.38
Not-generic method is 13.916925926666 times faster, or on 1291.6925926666%
x86
Elapsed for F = 6.713223 ticks = 67132.23
Elapsed for F2 = 6.729897 ticks = 67298.97
Not-generic method is 0.997522398931217 times faster, or on -0.247760106878314%
而且我有了新的魔力:x64 速度快了三倍...
PS:我的目标平台是 x64。
【问题讨论】:
-
我得到 00:00:00.2080244 和 00:00:00.0071957 在没有调试器的发布版本中使用秒表。
-
你检查过 IL 吗?第二个示例可能由编译器内联。
-
我用过.net 4.5,它内联了这两种方法; dtb,我想如果你添加 [MethodImpl(MethodImplOptions.AggressiveInlining)] - 你会得到相同的结果
-
F2 的 IL:
ldarg.0, ldarg.1, ceq, ret。 F的 IL: ldarg.0, box !!T, ldarg.1, box !!T, ceq, ret。所以答案是因为参数被装箱了。但是为什么它们会被装箱呢? -
@dtb:你在看 IL,这没关系。您需要查看 JIT 生成的本机代码。
标签: c# performance templates generics