Ale*_*tin 63 .net c# generics clr performance
我遇到过一个我无法理解的性能问题.我知道如何解决它,但我不明白为什么会这样.这只是为了好玩!
我们来谈谈代码.我尽可能地简化了代码以重现问题.
假设我们有一个泛型类.它内部有一个空列表,并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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们有一个空的派生类:
public class DerivedClass : BaseClass<object>
{
}
Run Code Online (Sandbox Code Playgroud)
让我们ClassBase<T>.Run从两个类中测量运行方法的性能.从派生类型访问比从基类访问慢4倍.我无法理解为什么会这样.在发布模式下编译,结果与预热相同.它仅在.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);
}
}
Run Code Online (Sandbox Code Playgroud)
完整列出的要点
Sin*_*nix 31
更新:
CLR团队在Microsoft Connect上有一个答案
它与共享泛型代码中的字典查找有关.运行时和JIT中的启发式方法不适用于此特定测试.我们将看看它可以做些什么.
在此期间,您可以通过向BaseClass添加两个虚拟方法来解决它(甚至不需要调用).它将使启发式工作正如人们期望的那样工作.
原文:
这是JIT失败.
可以通过这个疯狂的事情来修复:
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())
{
return;
}
}
}
public void Run2()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
{
return;
}
}
}
public void Run3()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
{
return;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,不会从任何地方调用Run2()/ Run3().但是如果你注释掉Run2或Run3方法 - 你会像以前一样受到性能损失.
我想,有一些与堆栈对齐或方法表大小有关的东西.
PS你可以更换
Enumerable.Empty<T>();
// with
var x = new Func<IEnumerable<T>>(Enumerable.Empty<T>);
Run Code Online (Sandbox Code Playgroud)
还是一样的bug.
| 归档时间: |
|
| 查看次数: |
1875 次 |
| 最近记录: |