我有一个方法,它使用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);
}
}
我相信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;
}
答案 0 :(得分:12)
对于你的价值类,你可以创建直接的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<T>(propertyInfo));
循环使用
var myTarget = new MyType();
setter(myTarget, aNewValue)
帮助检索快速Setter和Getter
public static Action<T, object> BuildUntypedSetter<T>(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<Action<T, object>>(exBody, exTarget, exValue);
var action = lambda.Compile();
return action;
}
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;
}
=============性能分析已添加===================
5个Mio对象,20个属性
诀窍是为每个类生成一次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);
}
}
答案 1 :(得分:3)
如果问题确实存在于PropertyInfo.GetValue
方法调用中,您可以使用该方法构建property-getters缓存(例如通过编译表达式)。
下面的示例演示了这种方法在具有14个属性(带有热缓存)的8000个对象上比原始方法快30-40%:
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());
}
这是最初的实现(为了测试目的而减少):
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);
}
}
以下是优化的实施:
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();
}
测试类:
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<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();
}
控制台结果(版本,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<Obj>(...)
如果您正在使用编译时未知类型的对象列表,则可以使用以下方法在运行时调用InitializeData2<>
泛型方法:
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[] { }) });
}
答案 2 :(得分:0)
我做了一个简单的测试,我用一个执行简单赋值的函数替换了有问题的.GetValue
(“如果属性的名称是blabla,值是Object.blabla”)。测试只包含函数/变量/属性的简单版本以及允许完全控制迭代次数的循环。结果肯定令人惊讶:新方法快10倍!请记住,在我的原始测试(50000次迭代)中,时间是2276(旧)与234(新)。这种差异对于不同的情景保持不变;例如,对于8000次迭代,它提供358ms而不是36ms。我在一台非常强大的计算机和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();
}
最后注意:我希望人们能够理解如此令人印象深刻的结果,而不仅仅是应用于.GetValue
。如今计算机可以处理很多事情,而你真的不需要最大化每一位的性能,这是事实。另一方面,如果您遇到性能问题并且需要以更相关的方式“节省资源”,那么您应该将改进重点放在“更简单,更快”的想法上。我使用相关数量的Lists
和Dictionaries
完成了代码中的性能改进,即使在每次更改(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
数组中的相应位置也被填充时)并且由对象/属性位置(第一对象,第三属性等)引用。从逻辑上讲,这有对象数量的限制(增长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
我在这里停止了我的测试。我想这足以证明我的观点。
不同的方法在不同条件下提供不同的性能,因此了解情况的理想配置的最佳方式是实际测试它。依靠最终的事实很少是问题的最佳解决方案(尽管我可能错了......仍在等待DmitryG的答复,以便在不同条件下测试他的解决方案)。因此,在测试条件下,似乎情况是原始的简化方法对于属性数量相对较低(即低于20)的情况是可接受的;在此之上,所需的硬编码工作似乎并不值得,并且依赖于不同的替代方案(如我提出的2D阵列)更好。无论如何,GetValue
显然表现不佳,可能会以多种不同的方式改进。
我希望我不需要再次更新此答案:)
答案 3 :(得分:0)
接上来的帖子: 您的代码构建属性名称和(格式化)值的字典。 所以我们只需要一个List作为输入。从T我们可以得出所有信息。
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; ;
}
方法BuildUntypedGetter()就像
// 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;
}
无需在通话中指定类型。它通过类型推断来检测。
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);