8

友人と私は、代わりにコンパイルされた式をオブジェクト作成に使用してテストしていましたがActivator.CreateInstance<T>、いくつかの興味深い結果に出くわしました。各マシンで同じコードを実行すると、まったく逆の結果が得られることがわかりました。彼は期待どおりの結果を得て、コンパイルされた式から大幅に優れたパフォーマンスを達成しましたが、私はActivator.CreateInstance<T>2 倍のパフォーマンスを見て驚きました。

両方のコンピューターが .NET 4.0 でコンパイルされて実行されました

コンピューター 1 には .NET 4.5 がインストールされています。コンピューター 2 にはありません。

100000 個を超えるオブジェクトのコンピューター 1:

45ms - Type<Test>.New()
19ms - System.Activator.CreateInstance<Test>();

コンピューター 2 100000 個を超えるオブジェクト:

13ms - Type<Test>.New()
86ms - System.Activator.CreateInstance<Test>();

コードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace NewNew
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch benchmark = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                var result = Type<Test>.New();
            }
            benchmark.Stop();
            Console.WriteLine(benchmark.ElapsedMilliseconds + " Type<Test>.New()");

            benchmark = Stopwatch.StartNew();
            for (int i = 0; i < 100000; i++)
            {
                System.Activator.CreateInstance<Test>();
            }
            benchmark.Stop();
            Console.WriteLine(benchmark.ElapsedMilliseconds + " System.Activator.CreateInstance<Test>();");
            Console.Read();
        }


        static T Create<T>(params object[] args)
        {
            var types = args.Select(p => p.GetType()).ToArray();
            var ctor = typeof(T).GetConstructor(types);

            var exnew = Expression.New(ctor);
            var lambda = Expression.Lambda<T>(exnew);
            var compiled = lambda.Compile();
            return compiled;
        }
    }

    public delegate object ObjectActivator(params object[] args);

    public static class TypeExtensions
    {
        public static object New(this Type input, params object[] args)
        {
            if (TypeCache.Cache.ContainsKey(input))
                return TypeCache.Cache[input](args);

            var types = args.Select(p => p.GetType());
            var constructor = input.GetConstructor(types.ToArray());

            var paraminfo = constructor.GetParameters();

            var paramex = Expression.Parameter(typeof(object[]), "args");

            var argex = new Expression[paraminfo.Length];
            for (int i = 0; i < paraminfo.Length; i++)
            {
                var index = Expression.Constant(i);
                var paramType = paraminfo[i].ParameterType;
                var accessor = Expression.ArrayIndex(paramex, index);
                var cast = Expression.Convert(accessor, paramType);
                argex[i] = cast;
            }

            var newex = Expression.New(constructor, argex);
            var lambda = Expression.Lambda(typeof(ObjectActivator), newex, paramex);
            var result = (ObjectActivator)lambda.Compile();
            TypeCache.Cache.Add(input, result);
            return result(args);
        }
    }

    public class TypeCache
    {
        internal static IDictionary<Type, ObjectActivator> Cache;

        static TypeCache()
        {
            Cache = new Dictionary<Type, ObjectActivator>();
        }
    }

    public class Type<T>
    {
        public static T New(params object[] args)
        {
            return (T)typeof(T).New(args);
        }
    }

    public class Test
    {
        public Test()
        {

        }

        public Test(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }
}
4

1 に答える 1

8

これには少なくとも 2 つの原因があります。

  • Type<Test>.New()またはSystem.Activator.CreateInstance<Test>()を初めて呼び出すときのオーバーヘッドは比較的大きくなります。このため、100000 を 10000000 に変更しました。
  • アプリケーションをリリース モードでビルドし、デバッガーなしで実行します。

これら 2 つの変更により、2 つの方法の所要時間はほぼ同じになります。私のシステムでは、両方の方法で 1100 から 1200 の間になりました。

あなたはいくつかの引数を受け入れActivator.CreateInstance<T>()ますが、デフォルトのコンストラクターしか呼び出すことができないことに注意してください。あまり強力でなく、常にデフォルトのコンストラクターも使用すると、私のシステムよりもわずかに高速になりNew()ます。New()Activator.CreateInstance<T>()

渡された引数に応じて、同じ型の 2 つの異なるコンストラクターを使用する必要がある場合、パラメーターを使用したコンストラクターの処理は実際には機能しないことにも注意してください。プログラム全体の残りの部分で使用するコンストラクターを 1 回選択します。

于 2012-09-06T20:37:35.363 に答える