14

I have a method that uses loops through 7,753+ objects and Gets the value of each property for each object. Each object has 14 properties.

private void InitializeData(IList objects, PropertyInfo[] props, List<DPV> dataPs, List<Dictionary<string, object>> tod)
{
    foreach (var item in objects)
    {
        var kvp = new Dictionary<string, object>();
        foreach (var p in props)
        {
            var dataPs = dataPs.FirstOrDefault(x => x.Name == p.Name);
            object returnData;
            if (dataPoint != null)
            {
                int maxLength = (dataP.MaxLength == null) ? 0 : (int) dataP.MaxLength;
                returnData = p.GetValue(item, null);
                if (!string.IsNullOrEmpty(dataP.FormatString) && !string.IsNullOrEmpty(returnData.ToString()))
                {
                    returnData = FormatDataForDisplay(returnData, dataP, maxLength, "", 8);
                }
            }
            else
            {
                returnData = p.GetValue(item, null);
            }
            kvp.Add(p.Name, returnData);
        }
        tod.Add(kvp);
    }
}

I believe GetValue is what takes the majority of the time in this method, The method took around 900ms to run, but GetValue which is called 800,000+ times takes around 750ms (total, not per-call).

public List<Dictionary<string, object>> GetColumnOptions<T>(List<T> list)
    {

        var tod= new List<Dictionary<string, object>>();



        var objects = (IList)list[0];
        Type objType = objects[0].GetType();

        var props = objType.GetProperties(BindingFlags.DeclaredOnly |
                                                         BindingFlags.Public |
                                                         BindingFlags.Instance);


        var dPs= GetDPs();



        //Initialize aaData
        //I don't believe this is correct
        InitializeData2<T>(new List<T> { (T) objects}, props, dPs, tod);

        return tod;
    }
Xaisoft
  • 45,655
  • 87
  • 279
  • 432
  • So basically you want `PropertyInfo.GetValue()` being speeded up? – juergen d Jul 15 '13 at 17:28
  • How long does an equivalent version without the reflection calls take? That is the main overhead of `GetValue`, but I am curious how much overhead it adds compared to a direct property reference. (1,000 ns is non-trivial but not enormous) – Guvante Jul 15 '13 at 17:28
  • Are the object types known at compile-time? Can you write type-safe value getters/formatters for each known type and all you need to do is a type lookup/cast on each `item` which will produce the KeyValuePairs for you and avoid reflection altogether? EDIT: Or better yet, have your objects implement an interface with a single method who's job is to return a set of KeyValuePairs; avoid the type lookup/cast completely. – Chris Sinclair Jul 15 '13 at 17:49
  • @ChrisSinclair - The objects are known at runtime. They are entities from LINQ to Entities. – Xaisoft Jul 15 '13 at 17:59
  • @Xaisoft and they all are of the same types? – Simon Belanger Jul 15 '13 at 18:03
  • Yes, when looping through the objects, they are all the same type. – Xaisoft Jul 15 '13 at 18:07
  • 2
    I don't have the code for this, but when you use reflection over and over again to get/set the same property, you can improve that by compiling lambdas in advance using expression trees, and using those lambdas. It's a messy syntax, and takes lots of trial-and-error until you are comfortable with it, but basically you can compile property access once (by using reflection one time), then store the lambdas in a dictionary or other form, then call the lambdas safely without any more need for reflection. – Joe Enos Jul 15 '13 at 19:09
  • @Guvante my answer gives the (surprising) result: just using the GetValue line makes the code 10 times slower (with respect to a direct assignation). – varocarbas Jul 15 '13 at 20:54
  • @Xaisoft perhaps my first answer wasn't excessively clear and thus I have updated it. Let me know what you think. – varocarbas Jul 16 '13 at 07:36

4 Answers4

26

For your value class you can create direct setter and getter lambda.
The performance is nearly as fast as directly accessing the properies.

Get Setter from PropertyInfo

var propertyInfo = typeof(MyType).GetProperty("MyPropertValue");
var propertySetter = FastInvoke.BuildUntypedSetter<T>(propertyInfo));
var fieldInfo = typeof(MyType).GetField("MyFieldValue");
var fieldSetter = FastInvoke.BuildUntypedSetter<T>(fieldInfo));

Usage in a loop

var myTarget = new MyType();
setter(myTarget, aNewValue)

Helper to retrieving fast Setter an Getter

public static class FastInvoke {

    public static Func<T, object> BuildUntypedGetter<T>(MemberInfo memberInfo)
    {
        var targetType = memberInfo.DeclaringType;
        var exInstance = Expression.Parameter(targetType, "t");

        var exMemberAccess = Expression.MakeMemberAccess(exInstance, memberInfo);       // t.PropertyName
        var exConvertToObject = Expression.Convert(exMemberAccess, typeof(object));     // Convert(t.PropertyName, typeof(object))
        var lambda = Expression.Lambda<Func<T, object>>(exConvertToObject, exInstance);

        var action = lambda.Compile();
        return action;
    }

    public static Action<T, object> BuildUntypedSetter<T>(MemberInfo memberInfo)
    {
        var targetType = memberInfo.DeclaringType;
        var exInstance = Expression.Parameter(targetType, "t");

        var exMemberAccess = Expression.MakeMemberAccess(exInstance, memberInfo);

        // t.PropertValue(Convert(p))
        var exValue = Expression.Parameter(typeof(object), "p");
        var exConvertedValue = Expression.Convert(exValue, GetUnderlyingType(memberInfo));
        var exBody = Expression.Assign(exMemberAccess, exConvertedValue);

        var lambda = Expression.Lambda<Action<T, object>>(exBody, exInstance, exValue);
        var action = lambda.Compile();
        return action;
    }

    private static Type GetUnderlyingType(this MemberInfo member)
    {
        switch (member.MemberType)
        {
            case MemberTypes.Event:
                return ((EventInfo)member).EventHandlerType;
            case MemberTypes.Field:
                return ((FieldInfo)member).FieldType;
            case MemberTypes.Method:
                return ((MethodInfo)member).ReturnType;
            case MemberTypes.Property:
                return ((PropertyInfo)member).PropertyType;
            default:
                throw new ArgumentException
                (
                 "Input MemberInfo must be if type EventInfo, FieldInfo, MethodInfo, or PropertyInfo"
                );
        }
    }
}

============= Performance Analysis Added ===================

5 Mio Objects, 20 Properties

  • 3.4s direct Property access
  • 130.0s via PropertyInfo.SetValue
  • 4.0s via TypedSetter (code shown in article)
  • 9.8s via UnTypedSetter (code above)

The trick is to generate the property-setter and -getter once for each class an reuse them.

// Create an fill objects fast from DataReader
// http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html 
static List<T> CreateObjectFromReader<T>(IDataReader reader)
    where T : new()
{
  // Prepare
  List<string> fieldNames = GetFieldNames(reader);
  List<Action<T, object>> setterList = new List<Action<T, object>>();
 
  // Create Property-Setter and store it in an array 
  foreach (var field in fieldNames)
  {
    var propertyInfo = typeof(T).GetProperty(field);
    setterList.Add(FastInvoke.BuildUntypedSetter<T>(propertyInfo));
  }
  Action<T, object>[] setterArray = setterList.ToArray();
 
  // generate and fill objects
  while (reader.Read())
  {
    T xclass = new T();
    int fieldNumber = 0;
 
    for (int i = 0; i< setterArray.Length; i++) 
    {
        // call setter
        setterArray[i](xclass, reader.GetValue(i));
        fieldNumber++;
    } 
    result.Add(xclass);
  }
}

My original article (german text and older code) was https://web.archive.org/web/20141020092917/http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html

Fried
  • 1,323
  • 12
  • 21
  • 1
    Is it possible to do the same for fields, or 'SetValue = (o1, o2) => fieldInfo.SetValue(o1, o2);' is just as good? – Guedez Oct 20 '19 at 23:35
  • 1
    @Guedez: You can now use it for Properties and Fields. I changed the implementation by making usage of MakeMemberAccess (I did not know this function in 2016 ;). – Fried Oct 21 '19 at 12:02
  • Throws a conversion error when trying to set an integer field and using a value of "12" is it possible to add conversion to this? – Ryan Thomas Oct 31 '19 at 10:44
  • @RyanThomas: As I understand, you are trying to set a string-value to an int-property? Same as propertyInfo.SetValue(..), the FastInvoke will do an Convert(), which will throw an exception, too. So the behaviour is fine. Your request is about adding an additional int.Parse(string) resp. propertyType.Parse(sourceType) into the expression. I think it is not possible to implent that for all source and target types, so you might expand the expression to access string types and deliver base types. Not too easy ... – Fried Oct 31 '19 at 12:31
  • @Fried Yes you are correct. That's a shame. Think I'll just stick to FieldInfo.SetValue - from my testing of setting 1000 fields on an object using this way, it executes between 0 and 1ms, which is such a minute amount, it is not worth optimising. Thank you anyway. :) – Ryan Thomas Oct 31 '19 at 16:59
  • @RyanThomas You can always use your specific setter if you know your types. FastInvoke is about not knowing the types. Code: Action setDirect = (MyObject o, string sValue) => { o.IntPropertValue = int.Parse(sValue); }; setDirect(myObject, "100"); – Fried Nov 01 '19 at 10:03
  • @Fried, article link is dead. – Michael Silver Apr 20 '21 at 03:38
  • 2
    Cached copy of article: [archive.org link](https://web.archive.org/web/20141020092917/http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html) – Michael Silver Apr 20 '21 at 03:49
3

If the problem is really in PropertyInfo.GetValue method call you can use the approach with building property-getters cache (for example via compiled expressions). Here is the sample that demostrates that this approach is up to 30-40% faster than original method on 8000 objects with 14 properties (with hot cache):

static void Main(string[] args) {
    IList objects = new List<Obj>();
    for(int i = 0; i < 8000; i++)
        objects.Add(new Obj());
    var properties = typeof(Obj).GetProperties();


    var sw1 = System.Diagnostics.Stopwatch.StartNew();
    InitializeData1(objects, properties, new List<Dictionary<string, object>>());
    sw1.Stop();
    Console.WriteLine("Reflection PropertyInfo.GetValue: " + sw1.ElapsedTicks.ToString());

    // cold cache testing
    var sw2_coldCache = System.Diagnostics.Stopwatch.StartNew();
    InitializeData2<Obj>(objects, properties, new List<Dictionary<string, object>>(), new Dictionary<string, Func<Obj, object>>());
    sw2_coldCache.Stop();
    Console.WriteLine("Cached Getters (Cold cache): " + sw2_coldCache.ElapsedTicks.ToString());

    // cache initialization
    InitializeData2<Obj>(new List<Obj> { new Obj() }, properties, new List<Dictionary<string, object>>(), gettersCache);
    // hot cache testing
    var sw2_hotCache = System.Diagnostics.Stopwatch.StartNew();
    InitializeData2<Obj>(objects, properties, new List<Dictionary<string, object>>(), gettersCache);
    sw2_hotCache.Stop();
    Console.WriteLine("Cached Getters (Hot cache): " + sw2_hotCache.ElapsedTicks.ToString());

    var sw3 = System.Diagnostics.Stopwatch.StartNew();
    InitializeData3(objects, properties, new List<Dictionary<string, object>>());
    sw3.Stop();
    Console.WriteLine("returnProps special method: " + sw3.ElapsedTicks.ToString());

    var sw4 = System.Diagnostics.Stopwatch.StartNew();
    InitializeData2_NonGeneric(objects, properties, new List<Dictionary<string, object>>());
    sw4.Stop();
    Console.WriteLine("Cached Getters (runtime types resolving): " + sw4.ElapsedTicks.ToString());
}

Here is the original implementation (reduced for test purposes):

static void InitializeData1(IList objects, PropertyInfo[] props, List<Dictionary<string, object>> tod) {
    foreach(var item in objects) {
        var kvp = new Dictionary<string, object>();
        foreach(var p in props) {
            kvp.Add(p.Name, p.GetValue(item, null));
        }
        tod.Add(kvp);
    }
}

Here is the optimized implementation:

static IDictionary<string, Func<Obj, object>> gettersCache = new Dictionary<string, Func<Obj, object>>();
static void InitializeData2<T>(IList objects, PropertyInfo[] props, List<Dictionary<string, object>> tod, IDictionary<string, Func<T, object>> getters) {
    Func<T, object> getter;
    foreach(T item in objects) {
        var kvp = new Dictionary<string, object>();
        foreach(var p in props) {
            if(!getters.TryGetValue(p.Name, out getter)) {
                getter = GetValueGetter<T>(p);
                getters.Add(p.Name, getter);
            }
            kvp.Add(p.Name, getter(item));
        }
        tod.Add(kvp);
    }
}

static Func<T, object> GetValueGetter<T>(PropertyInfo propertyInfo) {
    var instance = System.Linq.Expressions.Expression.Parameter(propertyInfo.DeclaringType, "i");
    var property = System.Linq.Expressions.Expression.Property(instance, propertyInfo);
    var convert = System.Linq.Expressions.Expression.TypeAs(property, typeof(object));
    return (Func<T, object>)System.Linq.Expressions.Expression.Lambda(convert, instance).Compile();
}

Test class:

class Obj {
    public int p00 { set; get; }
    public string p01 { set; get; }
    public float p02 { set; get; }
    public double p03 { set; get; }
    public char p04 { set; get; }
    public byte p05 { set; get; }
    public long p06 { set; get; }
    public int p07 { set; get; }
    public string p08 { set; get; }
    public float p09 { set; get; }
    public double p10 { set; get; }
    public char p11 { set; get; }
    public byte p12 { set; get; }
    public long p13 { set; get; }
}

Update: Added solution from varocarbas into tests

static void InitializeData3(IList objects, PropertyInfo[] props, List<Dictionary<string, object>> tod) {
    foreach(Obj item in objects) {
        var kvp = new Dictionary<string, object>();
        foreach(var p in props) {
            kvp.Add(p.Name, returnProps(p.Name, item));
        }
        tod.Add(kvp);
    }
}
static object returnProps(string propName, Obj curObject) {
    if(propName == "p00") {
        return curObject.p00;
    }
    else if(propName == "p01") {
        return curObject.p01;
    }
    else if(propName == "p02") {
        return curObject.p02;
    }
    else if(propName == "p03") {
        return curObject.p03;
    }
    else if(propName == "p04") {
        return curObject.p04;
    }
    else if(propName == "p05") {
        return curObject.p05;
    }
    else if(propName == "p06") {
        return curObject.p06;
    }
    else if(propName == "p07") {
        return curObject.p07;
    }
    else if(propName == "p08") {
        return curObject.p08;
    }
    else if(propName == "p09") {
        return curObject.p09;
    }
    else if(propName == "p10") {
        return curObject.p10;
    }
    else if(propName == "p11") {
        return curObject.p11;
    }
    else if(propName == "p12") {
        return curObject.p12;
    }
    else if(propName == "p13") {
        return curObject.p13;
    }
    return new object();
}

Console Results: (Release, x64) (Core i5 M560 @2.67 GHz, 8GB RAM, Win7x64)

Reflection PropertyInfo.GetValue: 161288
Cached Getters (Cold cache): 153808
Cached Getters (Hot cache): 110837
returnProps special method: 128905

Thus, the caching approach is the best.

UPDATE2
The methods demonstrated in sample are intended to be used when the type of objects elements is known at compile time (generic way):

InitializeData2<Obj>(...)

If you are using the objects list which type is unknown at compile-time, you can use the following approach to invoke the InitializeData2<> generic method at run-time:

InitializeData2_NonGeneric(objects, properties, new List<Dictionary<string, object>>());
//...
static void InitializeData2_NonGeneric(IList objects, PropertyInfo[] props, List<Dictionary<string, object>> tod) {
    Type elementType = objects[0].GetType();
    var genericMethodInfo = typeof(Program).GetMethod("InitializeData2", BindingFlags.Static | BindingFlags.NonPublic);
    var genericMethod = genericMethodInfo.MakeGenericMethod(new Type[] { elementType });

    var genericGetterType = typeof(Func<,>).MakeGenericType(elementType,typeof(object));
    var genericCacheType = typeof(Dictionary<,>).MakeGenericType(typeof(string), genericGetterType);
    var genericCacheConstructor = genericCacheType.GetConstructor(new Type[] { });
    genericMethod.Invoke(null, new object[] { objects, props, tod, genericCacheConstructor.Invoke(new object[] { }) });
}
DmitryG
  • 17,677
  • 1
  • 30
  • 53
  • Hi, when I make the change, it is telling me this method: `InitializeData(objects, props, dataPs, tod); Type arguments can't be inferred from usage.` Is there something missing? – Xaisoft Jul 16 '13 at 14:51
  • My objects don't come in as List. They come in as `List list` and then to get the properties the type, I do `var objects = (IList)list[0]; Type objType = objects[0].GetType();` – Xaisoft Jul 16 '13 at 14:54
  • @Xaisoft 1st- You can specify it explicitly : `InitializeData2(objects, properties, tod);` 2nd - Anyway, i don't see any problem with any modifications of my example... And you can always clarify your question or create new one to demonstrate the exact issue – DmitryG Jul 16 '13 at 15:15
  • After making the change, I get the following error message: `Unable to cast object of type 'System.Func`2[<>f__AnonymousTypea`12[System.Int32,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String,System.String],System.Object]' to type 'System.Func`2[System.Object,System.Object]` on line: `return (Func)System.Linq.Expressions.Expression.Lambda(convert, instance).Compile();` – Xaisoft Jul 16 '13 at 15:42
  • @Xaisoft it is difficult to determine the cause of this issue due to a lack of information. Please update the question with the code that produce this issue. – DmitryG Jul 16 '13 at 15:48
  • @DmitryG thanks for including my approach. I am intending to run your code to do some tests but I am finding problems. I am using VS2010 and have to adapt various parts; also I am not too much into Tlist and stuff. The main problem I have is in "foreach(T item in objects) {" where I don't know how to make "items" in "objects" a "T" (actually, they are "Object"); if I let "items" as "Object", I cannot execute the part "kvp.Add(p.Name, getter(item));" (the most important one) as far as "getter" is expecting a "T". Could you please tell me what I am doing wrong? – varocarbas Jul 17 '13 at 08:01
  • @varocarbas I have updated my answer with information on non-generic usage of demonstated generic methods. I have also checked that all my code works fine in VS2010 (you need just copy my code into new console application) – DmitryG Jul 17 '13 at 16:02
  • @Xaisoft I have updated my answer with information on non-generic usage of demonstated generic methods. – DmitryG Jul 17 '13 at 16:03
  • @DmitryG previously I was pasting your code into a winforms application. But now I did it into a console and I am having the same problems. Firstly it forces me to update all the "IList objects" to "IList objects" and then I get an error in the foreach ("Cannot convert type ...Obj to T") which makes lots of sense to me ("item" is an "Obj" not a "T") and which I cannot fix. Are you using VS 2012? I can install (have to do it anyway) and give it a try with it. – varocarbas Jul 17 '13 at 16:20
  • @varocarbas I do not know why you are experiencing these issues with my code compilation, because all things operates correctly to me in any version of VS(I'm using VS2010Pro/VS2012Pro/VS2013.Preview). Here is zipped sample dl.dropboxusercontent.com/u/104783990/sample.zip - please check it again – DmitryG Jul 17 '13 at 16:35
  • @DmitryG you are right, these projects work fine (at least the 2010 one). It has to do with some project configuration (as said no idea on this front). I took snaphsots of your project and mine, as you can see, I get errors were you don't (http://www.mediafire.com/download/ovxch44qgarjcn4/pics.zip). Will take care of this "problem" at other point, now I will focus on your code. Write you back in a while. – varocarbas Jul 17 '13 at 16:55
  • @DmitryG I found the problem, my project didn't include using System.Collections;. I did some tests and your approaches behave pretty well. You can read my last update regarding the performance tests I did: GetValues behave better with an increasing number of properties and your approach follow a parallel behaviour. Thus certainly good. Although I did propose a new approach which most like is much better than your methods (not tested) for any number of properties, I have to recognise that it is a quite good solution for the problem; I keep your code and +1 your answer :) – varocarbas Jul 17 '13 at 17:15
1

I did a simple test where I replaced the problematic .GetValue with a function performing a simplistic assignation ("if the name of the property is blabla, the value is Object.blabla"). The test consists just in a simple version of your function/variable/properties and a loop allowing to have full control over the number of iterations. The results have been certainly surprising: the new approach is 10 times faster! Bear in mind that in my original tests (50000 iterations) the times were 2276 (old) vs. 234 (new). This difference remains constant for different scenarios; for example for 8000 iterations, it delivers 358ms vs. 36ms. I have done these tests on a pretty powerful computer and on C# winforms; @Xaisoft can take the code below, perform a test under his specific conditions and tell the results.

The code:

 private void Form1_Load(object sender, EventArgs e)
 {
     List<List> var = new List<List>();

     List var1 = new List();
     var1.var = 1;
     var1.var2 = 1;
     var1.var3 = 1;
     var1.var4 = 1;
     var1.var5 = 1;

     List var2 = new List();
     var2.var = 1;
     var2.var2 = 1;
     var2.var3 = 1;
     var2.var4 = 1;
     var2.var5 = 1;

     List var3 = new List();
     var3.var = 1;
     var3.var2 = 1;
     var3.var3 = 1;
     var3.var4 = 1;
     var3.var5 = 1;

     List var4 = new List();
     var4.var = 1;
     var4.var2 = 1;
     var4.var3 = 1;
     var4.var4 = 1;
     var4.var5 = 1;

     var.Add(var1);
     var.Add(var2);
     var.Add(var3);
     var.Add(var4);

     InitializeData(var, typeof(List).GetProperties());
 }

 private static void InitializeData(List<List> objects, PropertyInfo[] props)
 {
     DateTime start = DateTime.Now;

     int count = 0;
     do
     {
         count = count + 1;
         foreach (var item in objects)
         {

             foreach (var p in props)
             {
                 object returnData = p.GetValue(item, null); //returnProps(p.Name, item);
             }
         }

     } while (count < 50000);


     TimeSpan timer = new TimeSpan();
     timer = DateTime.Now.Subtract(start);
 }

 private class List
 {
     public int var { set; get; }
     public int var2 { set; get; }
     public int var3 { set; get; }
     public int var4 { set; get; }
     public int var5 { set; get; }
     public int var6 { set; get; }
     public int var7 { set; get; }
     public int var8 { set; get; }
     public int var9 { set; get; }
     public int var10 { set; get; }
     public int var11 { set; get; }
     public int var12 { set; get; }
     public int var13 { set; get; }
     public int var14 { set; get; }
 }
 private static object returnProps(string propName, List curObject)
 {
     if (propName == "var")
     {
         return curObject.var;
     }
     else if (propName == "var2")
     {
         return curObject.var2;
     }
     else if (propName == "var3")
     {
         return curObject.var3;
     }
     else if (propName == "var4")
     {
         return curObject.var4;
     }
     else if (propName == "var5")
     {
         return curObject.var5;
     }
     else if (propName == "var6")
     {
         return curObject.var6;
     }
     else if (propName == "var7")
     {
         return curObject.var7;
     }
     else if (propName == "var8")
     {
         return curObject.var8;
     }
     else if (propName == "var9")
     {
         return curObject.var9;
     }
     else if (propName == "var10")
     {
         return curObject.var10;
     }
     else if (propName == "var11")
     {
         return curObject.var11;
     }
     else if (propName == "var12")
     {
         return curObject.var12;
     }
     else if (propName == "var13")
     {
         return curObject.var13;
     }
     else if (propName == "var14")
     {
         return curObject.var14;
     }

     return new object();
 }

FINAL NOTE: I would like people to understand so impressive results more generically than just applied to .GetValue. Nowadays computers can deal with lots of things and you don't really need to maximise the performance of each single bit, this is true. On the other hand, if you have performance problems and you need to "save resources" in a more relevant way, you should focus your improvements on the idea "the simpler, the quicker". I have done myself performance improvements in codes using a relevant number of Lists and Dictionaries and the results are noticiable even after each single change (List into conventional Array). You don't need to be too alarmist on this front but, in case of being required, remember that the memory consumption/associated time requirements to a List with respect to an Array are higher (and both elements do basically the same). Same thing for multi-dimension arrays, long-sized arrays, etc.

------ MORE DETAILED PERFORMANCE ANALYSIS

Even though I have let my point very clear since the start (just an idea which has to be adapted to each situation), I do understand that my claim (10 times faster) do require a proper definition. I have been doing tests under different conditions and here come the results:

NOTE: the aforementioned results were output by a 32-bit executable; all the ones below come from a 64-bit one. I have observed an improvement on the .GetValue performance when moving from 32-bit to 64-bit. The updated 64-bit version of the results above are (ms):

                      GetValue       Direct Assignation     
50000 iterations ->    1197                 157
80000 iterations ->    1922                 253
100000 iterations ->   2354                 310

Thus, the ratio changes from 10 times to 7.5 times.

I started increasing the number of properties (every time on 64-bit) and GetValue became better and better. Results:

28 Properties
                          GetValue       Direct Assignation     
    50000 iterations ->    2386                552
    80000 iterations ->    3857                872

Aver. ratio = 4.37

50 Properties
                          GetValue       Direct Assignation     
    50000 iterations ->    4292                1707
    80000 iterations ->    6772                2711

Aver. ratio = 2.475

I am not sure if the improvement of GetValue will continue and will reach a point where will be better than the simplistic approach but who cares? At this point, it is clear that the increasing number of properties plays against the simplistic approach, so it is time to try a different (again pretty simplistic) alternative: global array storing all the properties.

  private static int[,] List0;

Being populated in parallel with the given property (i.e., when object.propX = any value the corresponding positions in the array are also populated) and referred by objects/properties positions (first object, third property, etc.). Logically, this has the limitation of the number of objects (growing the first dimension above 1000 does not sound recommendable), but you might rely on different arrays (one storing from the first object to the 1000th one, other from the 1001th to the 2000th, etc.); you can set a function taking as argument the object name and returning the corresponding array.

Modifications in the main loop:

int countObject = -1;
foreach (var item in objects)
{
    countObject = countObject + 1;
    int countProp = -1;
    foreach (var p in props)
    {
        countProp = countProp + 1;
        object returnData = List0[countObject, countProp];
    }
}

By running this new approach in the case above, I get:

50 Properties
                         GetValue           2D Array    
   80000 iterations ->    6772                155

Aver. ratio = 45.146

One more:

70 Properties
                          GetValue          2D Array     
    80000 iterations ->    10444               213

Aver. ratio = 49.06

And I stopped my tests here. I guess that it is more than enough to prove my point.

Different approaches deliver different performances under different conditions and thus the best way to know the ideal configuration for a situation is actually testing it. Relying on an ultimate truth is rarely the best solution for a problem (although I might be wrong... still waiting for the reply from DmitryG to test his solution under different conditions). Thus, UNDER THE TESTED CONDITIONS, it seems to be the case that the original simplistic approach is acceptable for cases where the number of properties is relatively low (i.e., below 20); above this, the required hardcoding effort does not seem to be worthy and relying on a different alternative (like the 2D array I proposed) is better. In any case, GetValue delivers clearly a bad performance, which might be improved in many different ways.

I hope that I will not need to update this answer again :)

varocarbas
  • 12,354
  • 4
  • 26
  • 37
  • What sucks for me is that one of my objects has 140+ fields :( – Xaisoft Jul 15 '13 at 19:38
  • This is certainly a problem. The whole point of my answer is just proving an idea. You have to come up with the most adequate configuration for each situation. For example, setting up a global array which might not be the best approach from the fancy-coding point of view would speed up certain situations a lot (like the 140-field one you are proposing). In any case, for the time being, my crappy correction does deliver a much faster performance for this function; you can rely on it for the time being, while thinking in a better way to speed up the whole algorithm in a better way. – varocarbas Jul 15 '13 at 19:44
  • @Xaisoft as far as everyone is coming here with new ideas and putting numbers on the table (and I am joking around, saying that this is crappy, etc.), I hav updated my answer (last paragraph) to highlight what is actually representing this approach: 90% IMPROVEMENT without doing anything else. I guess that you got that, but just in case... some times I kid around too much and the point is not properly transmitted. – varocarbas Jul 15 '13 at 19:56
  • Don't just recommend people give up `List` and `Dictionary`, the former has good performance if you don't grow it too much (you need to know the size of an array beforehand, so no benefit there). `Dictionary` is vastly superior when used correctly (in this case as a naive wrapper of two objects so not ideal, but not bad either). Never use temporary files, the file system is exceedingly slow. I would bet that he won't get as good performance, his example had 800k items to look at, yours has 20. Memory paging is really important at those sizes. – Guvante Jul 16 '13 at 01:45
  • @Guvante You are right on Lists and Dictionaries, I might have been a bit too alarmist, I will correct my answer on this front. Regarding temp files, I said that as a support of my idea "dictionaries and lists are bad", again too alarmist. The only thing I don't quite understand is your last statement: my code does a simplistic comparison (call this function X times and compare the time requirements against this other one) and the results are impressively different (10 times faster), there is no discussion on that. Also note that my test took 2276ms vs the original 900ms. – varocarbas Jul 16 '13 at 07:16
  • @Guvante I have updated my answer. I hope that the ideas are clearer now. – varocarbas Jul 16 '13 at 07:35
  • @varocarbas: You need to ensure that you are comparing apples to apples. The OP says he is making 800,000 calls *without artificial looping*. He actually has an object with 170 fields and approximately 2,000 of them. This means he is accessing at minimum 1.6 MB of RAM, probably significantly more. That involves paging in parts of RAM into memory, which is incredibly expensive in small scenarios like this. For instance it might add 100 ms, which would turn your 10x into a 7x difference. (Since it adds a static amount of time, regardless of method) – Guvante Jul 16 '13 at 19:17
  • @Guvante I have offered the OP to test my approach and tell the results. In any case I am pretty sure that the exact framework does not have any effect as far as the problem is located in a single line. In my test both approaches do exactly the same (for example, getting the properties via reflection) until this single line. Nothing done before or after this line affects what this line does (or this is my assumption). In any case, I guess that the best way is testing it under real conditions: implementing my approach takes just 1 min. – varocarbas Jul 16 '13 at 19:38
  • @varocarbas: Actually with 140 properties implementing your approach takes several hours, but you are correct that the best way to find out is try. – Guvante Jul 16 '13 at 22:17
  • @Guvante no doubt on that, testing has the last word. In any case, I am afraid that there is a misunderstanding here: I haven't done any test for more than 14 properties (neither for less); my point was based on the idea that the number of objects or of the times the function is called does not have any effect. The number of properties logically has some effect. Even though I don't understand your statement: several hours does not mean anything; how long it took the .GetValue alternative? could you please post the code? I will do some tests on the varying-props front and update my answer. – varocarbas Jul 17 '13 at 07:02
  • @Guvante change of plans; I have seen right now that DmitryG including some code comparing everything; I will firstly implement it and see the results and then look at the varying-props issue. – varocarbas Jul 17 '13 at 07:12
  • @Guvante finally I haven't been able to execute the DmitryG approach (still waiting for his help) and have carried out a detailed performance analysis and updated my answer accordingly. Summary: GetValue gets better with the increasing number of properties but you don't need to continue relying on what I proposed forever... enjoy the reading. – varocarbas Jul 17 '13 at 09:33
0

Continued from post above: Your code builds a dictionary of property name and (formatted) value. So we need just a List as input. From T we can derive all information.

    public Dictionary<string, object> ExtractParameterNameAndValue<T>(List<T> colleciton)
        where T : class
    {
        var result = new Dictionary<string, object>();

        // out of the loop - generate getters
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var getterList = new List<Func<T,object>>();
        foreach (var p in properties)
        {
            getterList.Add(MyStatic.BuildUntypedGetter<T>(p));
        }

        // Array of getters
        var getters = getterList.ToArray(); // improving performance (?) - never use Dictionary
        // Corresponding array of Names
        var names = properties.Select(p => p.Name).ToArray();

        // iterate all data
        int counter = 0;
        foreach (var item in colleciton)
        {
            for (int i = 0; i< getters.Length; i++)
            {
                var name = names[i]; // name from property
                var value = getters[i](item);  // value from getter-call
                result.Add(counter + " " + name, value); 
            }
            counter++;
        }

        return result; ;
    }

The method BuildUntypedGetter() is like

   // see http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html
   public static Func<T, object> BuildUntypedGetter<T>(PropertyInfo propertyInfo)
    {
        var targetType = propertyInfo.DeclaringType;
        var methodInfo = propertyInfo.GetGetMethod();
        var returnType = methodInfo.ReturnType;

        var exTarget = Expression.Parameter(targetType, "t");
        var exBody = Expression.Call(exTarget, methodInfo);
        var exBody2 = Expression.Convert(exBody, typeof(object));

        var lambda = Expression.Lambda<Func<T, object>>(exBody2, exTarget);

        var action = lambda.Compile();
        return action;
    }

There is no need to specify the type in the call. It is detected by type inference.

       var accountList = new List<Account>()
        {
            new Account { Name = "X1", Name2 ="X2"},
            new Account { Name = "X3", Name2 ="X4"},
            new Account { Name = "X5", Name2 ="X6"},
        };

        var result = ExtractParameterNameAndValue(accountList);
Fried
  • 1,323
  • 12
  • 21