【发布时间】:2015-01-26 09:00:19
【问题描述】:
我遇到了一个我不太理解的性能问题。我知道如何解决它,但我不明白为什么会这样。这只是为了好玩!
让我们谈谈代码。我尽可能地简化了代码以重现该问题。
假设我们有一个泛型类。它内部有一个空列表,并在构造函数中使用T 执行某些操作。它具有Run 方法,该方法调用列表中的IEnumerable<T> 方法,例如Any().
public class BaseClass<T>
{
private List<T> _list = new List<T>();
public BaseClass()
{
Enumerable.Empty<T>();
// or Enumerable.Repeat(new T(), 10);
// or even new T();
// or foreach (var item in _list) {}
}
public void Run()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
// or if (_list.Count() > 0)
// or if (_list.FirstOrDefault() != null)
// or if (_list.SingleOrDefault() != null)
// or other IEnumerable<T> method
{
return;
}
}
}
}
然后我们有一个空的派生类:
public class DerivedClass : BaseClass<object>
{
}
让我们测量从两个类中运行ClassBase<T>.Run 方法的性能。从派生类型访问比从基类访问慢 4 倍。我不明白为什么会这样。在 Release 模式下编译,结果与 warm up 相同。它仅发生在 .NET 4.5 上。
public class Program
{
public static void Main()
{
Measure(new DerivedClass());
Measure(new BaseClass<object>());
}
private static void Measure(BaseClass<object> baseClass)
{
var sw = Stopwatch.StartNew();
baseClass.Run();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
完整列表on gist
【问题讨论】:
-
各位,你们试过运行代码吗?它处于释放模式,结果与热身相同。即使你互换线路。
-
如果你从构造函数中移除
Enumerable.Empty<T>,这两个类的表现是一样的。我不知道为什么,但是... -
@DavidLibido 你错了。我们在非常实际的应用中注意到了这一点。真正的代码要复杂得多,并且实现了 Aho Corasick 算法。而且我不是在问测试代码的商业价值。我在问为什么会这样?!
-
如果确实(正如下面的一些答案所暗示的那样)您在 JIT 中遇到了错误,那么 Microsoft Connect 就是打开错误的地方。您不会在 StackOverflow 上得到答案,告诉您为什么存在错误或错误的确切形状。如果您认为存在代码生成问题,请在 Connect 上打开一个错误。
-
向 Microsoft Connect connect.microsoft.com/VisualStudio/feedback/details/1041830/…提交了一个错误
标签: c# .net performance generics clr