10

オブジェクトのループを使用し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;
    }
4

4 に答える 4

3

問題が実際にPropertyInfo.GetValueメソッド呼び出しにある場合は、プロパティ ゲッター キャッシュを構築するアプローチを使用できます (たとえば、コンパイルされた式を介して)。次のサンプルは、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) (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

したがって、キャッシング アプローチが最適です。

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[] { }) });
}
于 2013-07-15T19:46:03.767 に答える
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);
于 2013-07-17T14:10:23.270 に答える