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

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

 private void InitializeData(IList objects, PropertyInfo[] props, List dataPs, List<Dictionary> tod) { foreach (var item in objects) { var kvp = new Dictionary(); 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); } } 

我相信GetValue在这种方法中占用了大部分时间,该方法需要大约900ms才能运行,但是被称为800,000+次以上的GetValue需要大约750ms (total, not per-call)

 public List<Dictionary> GetColumnOptions(List list) { var tod= new List<Dictionary>(); 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(new List { (T) objects}, props, dPs, tod); return tod; } 

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

从PropertyInfo获取Setter

 var propertyInfo = typeof(MyType).GetProperty(MyFieldName); var setter = BuildUntypedSetter(propertyInfo)); 

在循环中使用

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

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

 public static Action BuildUntypedSetter(PropertyInfo propertyInfo) { var targetType = propertyInfo.DeclaringType; var methodInfo = propertyInfo.GetSetMethod(); var exTarget = Expression.Parameter(targetType, "t"); var exValue = Expression.Parameter(typeof(object), "p"); var exBody = Expression.Call(exTarget, methodInfo, Expression.Convert(exValue, propertyInfo.PropertyType)); var lambda = Expression.Lambda>(exBody, exTarget, exValue); var action = lambda.Compile(); return action; } public static Func BuildUntypedGetter(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>(exBody2, exTarget); var action = lambda.Compile(); return action; } 

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

5 Mio Objects,20个属性

  • 3.4s直接财产访问
  • 130.0s通过PropertyInfo.SetValue
  • 通过TypedSetter 4.0s (文章中显示的代码)
  • 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 CreateObjectFromReader(IDataReader reader) where T : new() { // Prepare List fieldNames = GetFieldNames(reader); List> setterList = new List>(); // Create Property-Setter and store it in an array foreach (var field in fieldNames) { var propertyInfo = typeof(T).GetProperty(field); setterList.Add(FastInvoke.BuildUntypedSetter(propertyInfo)); } Action[] 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); } } 

如果问题确实存在于PropertyInfo.GetValue方法调用中,则可以使用该方法构建property-getters缓存(例如通过编译表达式)。 下面的示例演示了这种方法在具有14个属性(具有热缓存)的8000个对象上比原始方法快30-40%:

 static void Main(string[] args) { IList objects = new List(); 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>()); sw1.Stop(); Console.WriteLine("Reflection PropertyInfo.GetValue: " + sw1.ElapsedTicks.ToString()); // cold cache testing var sw2_coldCache = System.Diagnostics.Stopwatch.StartNew(); InitializeData2(objects, properties, new List>(), new Dictionary>()); sw2_coldCache.Stop(); Console.WriteLine("Cached Getters (Cold cache): " + sw2_coldCache.ElapsedTicks.ToString()); // cache initialization InitializeData2(new List { new Obj() }, properties, new List>(), gettersCache); // hot cache testing var sw2_hotCache = System.Diagnostics.Stopwatch.StartNew(); InitializeData2(objects, properties, new List>(), 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>()); sw3.Stop(); Console.WriteLine("returnProps special method: " + sw3.ElapsedTicks.ToString()); var sw4 = System.Diagnostics.Stopwatch.StartNew(); InitializeData2_NonGeneric(objects, properties, new List>()); sw4.Stop(); Console.WriteLine("Cached Getters (runtime types resolving): " + sw4.ElapsedTicks.ToString()); } 

这是最初的实现(为了测试目的而减少):

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

这是优化的实现:

 static IDictionary> gettersCache = new Dictionary>(); static void InitializeData2(IList objects, PropertyInfo[] props, List> tod, IDictionary> getters) { Func getter; foreach(T item in objects) { var kvp = new Dictionary(); foreach(var p in props) { if(!getters.TryGetValue(p.Name, out getter)) { getter = GetValueGetter(p); getters.Add(p.Name, getter); } kvp.Add(p.Name, getter(item)); } tod.Add(kvp); } } static Func GetValueGetter(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)System.Linq.Expressions.Expression.Lambda(convert, instance).Compile(); } 

测试类:

 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; } } 

更新:将 varocarbas的解决方案添加到测试中

 static void InitializeData3(IList objects, PropertyInfo[] props, List> tod) { foreach(Obj item in objects) { var kvp = new Dictionary(); 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(); } 

控制台结果:(发布,x64)(酷睿i5 M560 @ 2.67 GHz,8GB内存,Win7x64)

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

因此,缓存方法是最好的。

UPDATE2
在编译时已知 objects元素的类型 (通用方式),将使用样本中演示的方法:

 InitializeData2(...) 

如果您正在使用编译时未知类型的对象列表,则可以使用以下方法在运行时调用InitializeData2<>generics方法:

 InitializeData2_NonGeneric(objects, properties, new List>()); //... static void InitializeData2_NonGeneric(IList objects, PropertyInfo[] props, List> 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[] { }) }); } 

我做了一个简单的测试,我用一个执行简单.GetValue的函数替换了有问题的.GetValue (“如果属性的名称是blabla,值是Object.blabla”)。 测试只包含函数/变量/属性的简单版本和循环,允许完全控制迭代次数。 结果肯定令人惊讶:新方法快了10倍! 请记住,在我的原始测试(50000次迭代)中,时间是2276(旧)与234(新)。 这种差异对于不同的情景保持不变; 例如,对于8000次迭代,它提供358ms而不是36ms。 我在一台非常强大的计算机和C#winforms上完成了这些测试。 @Xaisoft可以使用下面的代码,在他的特定条件下执行测试并告诉结果。

代码:

  private void Form1_Load(object sender, EventArgs e) { List var = new 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 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(); } 

最后注意:我希望人们能够理解如此令人印象深刻的结果,而不仅仅是应用于.GetValue 。 如今计算机可以处理很多事情,而你真的不需要最大化每一位的性能,这是事实。 另一方面,如果您遇到性能问题并且需要以更相关的方式“节省资源”,那么您应该将改进重点放在“更简单,更快”的想法上。 我使用相关数量的ListsDictionaries在代码中完成了性能改进,即使在每次更改( List常规Array )之后,结果也是可察觉的。 你不需要在这方面过于危言耸听,但是,如果需要,请记住List相对于Array的内存消耗/相关时间要求更高(两个元素基本相同)。 多维数组,长尺寸数组等也是如此。

------更详细的表现分析

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

注意:上述结果由32位可执行文件输出; 下面的所有内容都来自64位的。 我观察到从32位移动到64位时.GetValue性能的改进。 以上结果的更新64位版本是(ms):

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

因此,该比例从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 

我不确定GetValue的改进是否会继续,并且会达到比简单方法更好但又关心谁的程度? 在这一点上,很明显越来越多的属性与简单的方法相比,所以现在是时候尝试一种不同的(同样非常简单的)替代方案:存储所有属性的全局数组。

  private static int[,] List0; 

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

主循环中的修改:

 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]; } } 

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

 50 Properties GetValue 2D Array 80000 iterations -> 6772 155 Aver. ratio = 45.146 

多一个:

 70 Properties GetValue 2D Array 80000 iterations -> 10444 213 Aver. ratio = 49.06 

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

不同的方法在不同条件下提供不同的性能,因此了解情况的理想配置的最佳方式是实际测试它。 依靠最终的事实很少是问题的最佳解决方案(尽管我可能错了......仍在等待DmitryG的答复,以便在不同条件下测试他的解决方案)。 因此,在测试条件下,似乎情况是原始的简化方法对于属性数量相对较低(即低于20)的情况是可接受的; 在此之上,所需的硬编码工作似乎并不值得,并且依赖于不同的替代方案(如我提出的2Darrays)更好。 在任何情况下, GetValue提供明显糟糕的性能,可能会以多种不同的方式进行改进。

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

接上来的post:您的代码构建了属性名称和(格式化)值的字典。 所以我们只需要一个List作为输入。 从T我们可以得出所有信息。

  public Dictionary ExtractParameterNameAndValue(List colleciton) where T : class { var result = new Dictionary(); // out of the loop - generate getters var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); var getterList = new List>(); foreach (var p in properties) { getterList.Add(MyStatic.BuildUntypedGetter(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; ; } 

BuildUntypedGetter()方法就像

  // see http://flurfunk.sdx-ag.de/2012/05/c-performance-bei-der-befullungmapping.html public static Func BuildUntypedGetter(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>(exBody2, exTarget); var action = lambda.Compile(); return action; } 

无需在调用中指定类型。 它通过类型推断来检测。

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