是否有可能加速这种方法?

Xai*_*oft 8 c# reflection asp.net-3.5

我有一个方法,通过7,753+对象使用循环并获取每个对象的每个属性的值.每个对象都有14属性.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我相信GetValue大部分时间都采用这种方法,该方法需要900ms运行,但这GetValue被称为800,000+时间需要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;
    }
Run Code Online (Sandbox Code Playgroud)

Fri*_*ied 13

对于您的值类,您可以创建直接的setter和getter lambda.
性能几乎与直接访问属性一样快.取自http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html(德语文本).

从PropertyInfo获取Setter

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

在循环中使用

var myTarget = new MyType();
setter(myTarget, aNewValue)
Run Code Online (Sandbox Code Playgroud)

帮助检索快速塞特的吸气剂

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"
                );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

=============性能分析已添加===================

5个Mio对象,20个属性

  • 3.4s直接财产访问
  • 通过PropertyInfo.SetValue获得 130.0s
  • 4.0s通过TypedSetter(文章中显示的代码)
  • 9.8s通过UnTypedSetter(上面的代码)

诀窍是为每个类生成一次property-setter和-getter并重用它们.

// 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 文章的缓存副本:[archive.org 链接](https://web.archive.org/web/20141020092917/http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der- befullungmapping.html) (2认同)

var*_*bas 1

.GetValue我做了一个简单的测试,用执行简单赋值的函数替换了有问题的问题(“如果属性的名称是 blabla,则值是 Object.blabla”)。该测试仅包含函数/变量/属性的简单版本和允许完全控制迭代次数的循环。结果确实令人惊讶:新方法速度快了 10 倍!请记住,在我最初的测试(50000 次迭代)中,时间为 2276(旧)与 234(新)。对于不同的场景,这种差异保持不变;例如,对于 8000 次迭代,它的速度为 358 毫秒,而 36 毫秒。我已经在一台功能强大的计算机和 C# winforms 上完成了这些测试;@Xaisoft可以拿下面的代码,在他的具体条件下进行测试并告诉结果。

代码:

 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();
 }
Run Code Online (Sandbox Code Playgroud)

最后说明:我希望人们能够更普遍地理解如此令人印象深刻的结果,而不仅仅是应用于.GetValue. 如今计算机可以处理很多事情,并且您实际上不需要最大化每个位的性能,这是事实。另一方面,如果您遇到性能问题并且需要以更相关的方式“节省资源”,那么您应该将改进重点放在“越简单、越快”的想法上。我自己使用相关数量的Lists和来改进代码的性能Dictionaries,即使在每次更改(List变为常规Array)之后,结果也是显而易见的。在这方面您不需要过于危言耸听,但是,如果需要的话,请记住 a 相对于 an 的内存消耗/相关时间要求List更高Array(并且两个元素的作用基本相同)。对于多维数组、长数组等也是如此。

------更详细的性能分析

尽管我从一开始就已经非常明确地表达了我的观点(只是一个必须适应每种情况的想法),但我确实明白我的主张(快 10 倍)确实需要一个正确的定义。我一直在不同条件下进行测试,结果如下:

注意:上述结果是由 32 位可执行文件输出的;以下所有内容均来自 64 位。我发现.GetValue从 32 位迁移到 64 位时性能有所改善。上述结果的更新后的 64 位版本为(毫秒):

                      GetValue       Direct Assignation     
50000 iterations ->    1197                 157
80000 iterations ->    1922                 253
100000 iterations ->   2354                 310
Run Code Online (Sandbox Code Playgroud)

因此,该比率从10倍变为7.5倍。

我开始增加属性的数量(每次都是在 64 位上)并且GetValue变得越来越好。结果:

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
Run Code Online (Sandbox Code Playgroud)

我不确定这种改进是否GetValue会继续下去,并且会达到比简单化方法更好的程度,但谁在乎呢?此时,很明显,属性数量的增加与简单化的方法相悖,因此是时候尝试一种不同的(同样非常简单的)替代方案:存储所有属性的全局数组。

  private static int[,] List0;
Run Code Online (Sandbox Code Playgroud)

与给定属性并行填充(即,当object.propX = any value数组中的相应位置也被填充时)并由对象/属性位置(第一个对象、第三个属性等)引用。从逻辑上讲,这有对象数量的限制(将第一个维度增长到 1000 以上听起来并不值得推荐),但您可能依赖于不同的数组(一个存储从第一个对象到第 1000 个对象,另一个存储从第 1001 个到第 2000 个对象) , ETC。); 您可以设置一个函数,以对象名称作为参数并返回相应的数组。

主循环中的修改:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

通过在上述案例中运行这种新方法,我得到:

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

Aver. ratio = 45.146
Run Code Online (Sandbox Code Playgroud)

多一个:

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

Aver. ratio = 49.06
Run Code Online (Sandbox Code Playgroud)

我在这里停止了测试。我想这足以证明我的观点了。

不同的方法在不同的条件下提供不同的性能,因此了解某种情况的理想配置的最佳方法是实际测试它。依赖最终的真理很少是问题的最佳解决方案(尽管我可能是错的......仍在等待 DmitryG 的回复以在不同条件下测试他的解决方案)。因此,在测试条件下,对于属性数量相对较少(即低于 20)的情况,原始的简单方法似乎是可以接受的;除此之外,所需的硬编码工作似乎并不值得,依赖不同的替代方案(如我提出的 2D 数组)会更好。无论如何,GetValue它的性能显然很差,可以通过许多不同的方式来改进。

我希望我不需要再次更新这个答案:)