I have an application that we upgraded form .NET 3.5 to .NET 4.7.2. The only problem really is performance from part of our code that uses reflection. Entire situation is best explained in a simple sample I uploaded to Gist: https://gist.github.com/vkocjancic/3e8a6b3496c412a75b1c85a1d2ba1111
Basically, we have a POCO class, which property methods throw exceptions, if value is not set for properties of non-nullable types.
[EDIT]:
Yes, I know that is not correct or a good pattern to use, but, the application started in .NET 1.1.
Yes, it should have been fixed long ago. It wasn't.
A reflection is then used to get property names and values of instances of POCO class and popluate it to DataTable.
The sample and real life project source code is exactly the same. The only difference is that in one case it is compiled using .NET 3.5 and in second using .NET 4.7.2.
This is the average time elapsed in milliseconds for 10 invocations:
.NET 3.5 -> 231.1 ms
.NET 4.7.2 -> 713.5 ms
.NET Core 2.2 -> 1013.2 ms
Can anyone elaborate why reflection is about 3 times slower in .NET 4.7.2 than in .NET 3.5 and how to fix that. The lag only happens, when properties are not set and throw exceptions. If you populate property values, there is no difference in performance.
If I run sample in debugger, .NET 3.5 build never triggers MissingFieldException. Build using .NET 4.7.2 triggers every exception.
[EDIT]
As @bevan mentioned, the slowdown actually occurs when switching to .NET 4.0. > Also the problem boils down to exception being thrown and handled 3 times faster in .NET 3.5 than it is in .NET 4.0
As i mentioned in the comments, if you call the IsPROP_NUMERIC_ANull
function before you access the properties, it wont throw any more exceptions and is super fast.
foreach (var myObject in objects)
{
var row = table.NewRow();
// TODO implement some caching: dont call GetProperties(), GetMethod(), for every object
var type = myObject.GetType();
var properties = type.GetProperties();
foreach (var info in properties)
{
try
{
// call the IsNullMethod for the property, eg "IsPROP_NUMERIC_ANull"
var isNullMethodName = $"Is{info.Name}Null";
var isNullMethod = type.GetMethod(isNullMethodName, BindingFlags.Instance | BindingFlags.Public);
if (isNullMethod != null)
{
var fldIsNull = (bool)isNullMethod.Invoke(myObject, new object[] { });
if (!fldIsNull)
row[info.Name] = info.GetValue(myObject, null);
} else
{
// isNullMethod doesn't exist
row[info.Name] = info.GetValue(myObject, null);
}
}
catch
{
// do nothing
}
}
table.Rows.Add(row);
}
Total runtime is 3 ms
vs 19500 ms you had before.
If you can redesign this i would use Cached Delegates like Marc Gravell proposed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With