With my Quickly Trim all your model's string properties post causing quite a stir (2 comments), I have decided to post a follow up and actually test the speed.
For the actual benchmarking I used a slightly modified version of this nice little class http://www.yoda.arachsys.com/csharp/Benchmark.cs, which I found from this page http://www.yoda.arachsys.com/csharp/benchmark.html
I tested 4 different methods that use reflection to trim all the string properties on an object and the 'long hand' method that does not use reflection at all.
Test Methods:
- Long Hand - No reflection.
- Initial code - with the GetIndexParameters call removed (not needed)
- Initial code with linq – Initial code but using linq to select the properties to process
- TypeDescriptor– used the TypeDescriptor class to get the property list
- FasterFlect m1 - Uses the FasterFlect library
- FasterFlect m2 – Uses the FasterFlect library’s delegates approach
Results
So here are the results (over 1 million object trims):Long Hand Trim | 00:00:01.4220000 |
Initial code | 00:00:07.7130000 |
Initial code with linq | 00:00:10.7270000 |
TypeDescriptor | 00:00:14.2340000 |
FasterFlect m1 | 00:00:06.1330000 |
FasterFlect m2 | 00:00:06.1830000 |
Surprised? I was. Firstly, the refection code varied from 7 - 14 times slower then the direct code, this wasn't a surprise more of a reminder that you really need to be careful when using reflection in your code base.
I knew my initial code would perform better then trying to filter for properties before doing the work. Filtering the properties first using linq or any other means, basically results in another loop over the properties on the object. So the more properties you have on your object the worst this method will perform.
But I was quite suprised with the TypeDescriptor. I had hopes that this would perform better then my initial code, as according to the documentation this method actually caches the meta data about objects. I am suspicious I haven’t used it to its full potential…
The other surprise was that the delegate method in fasterflect is actually slower then the normal method. Again, someone may well point out how to implement this better. The other note is that fasterflect was only able to achieve an 80% reduction. I was hoping for more.
Anyway interesting stuff, if someone has another fast method to achieve the same results let me know I’ll take it for a test drive.
4 comments:
Selecting the properties first with LINQ shouldn't mean another loop. The query will just return an Iterator that defers execution until the foreach loop.
Good point Chris. However it's still much slower then then the plain old for loop.
How about caching the property info for each type? Something like:
private readonly static Dictionary PropertyCache =
new Dictionary();
private readonly static object Mutex = new object();
public static void NullSafeTrimStrings(this T item)
{
var type = typeof(T);
PropertyInfo[] properties;
if (!PropertyCache.TryGetValue(type, out properties))
{
properties =
(from p in typeof (T).GetProperties()
where
p.CanRead && p.CanWrite &&
p.PropertyType == typeof (string)
select p).ToArray();
lock (Mutex)
{
if (!PropertyCache.ContainsKey(type))
PropertyCache.Add(type, properties);
}
}
foreach (var property in properties)
{
var currentValue = (string)property.GetValue(item, null);
if (currentValue != null)
property.SetValue(item, currentValue.Trim(), null);
}
}
@Chris, yep that's what I did in the next blog post (See the link at the top of this page)
Only thing is I didn't use a lock object, basically I figured if 1 or more threads attempt to set the dictionary value at the same time, then so be it. They will always set it to the same thing anyway.
Post a Comment