Коллеги наткнулись на интересное падение производительности. Подняли
тему на stackoverflow.
Наткнулись в реальном коде, им удалось воссоздать минимальный пример.
Есть generic класс
public class BaseClass<T>
{
private List<T> _list = new List<T>();
public BaseClass()
{
// если убрать эту строку из конструктора, то падения производительности в Run не происходит
Enumerable.Empty<T>();
// или Enumerable.Repeat(new T(), 10);
// или даже new T();
// или foreach (var item in _list) {}
// или что-либо, что ссылается на тип T
}
public void Run()
{
for (var i = 0; i < 8000000; i++)
{
_list.Any();
}
}
}
создается наследник
public class DerivedClass : BaseClass<object>
{
}
Далее делают замеры метода Run из обоих классов. Предвосхищая вопросы о методах замера, они пробовали и с разогревом и меняя местами строки Measure.
Компилировали в Release-режиме под .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 в наследованном классе DerivedClass в 4 раза медленнее, чем в BaseClass<object>.
Есть мысли, почему так может быть?