5

いくつかのパフォーマンスの問題が発生しているという事実を除けば、アプリケーションで完全に機能する DynamicObject のカスタム実装を使用しています。ダイナミクスではある程度のパフォーマンス オーバーヘッドが予想されますが、ExpandoObject を使用した場合でも、大幅な (桁違いの) パフォーマンスの低下が見られます。

ExpandoObject を使用できない理由は、その動作の一部をオーバーライドしたいからです。以下の非常に単純な例に問題を要約しました。

私のカスタム ExpandoObject コードは次のとおりです (問題を示すのに十分なコードに簡略化されています) --

public class SuperExpando : DynamicObject
{
    public Dictionary<string, object> dictionary = new Dictionary<string, object>();
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }
}

public dynamic m = new SuperExpando();

DynamicObject のディクショナリに値を直接設定すると (iemdictionary["keyname"] = 500)、ExpandoObject と同様のパフォーマンスが得られます。これは、ディクショナリにキーの値を設定するのにミリ秒未満の時間です。TrySetMember オーバーライド (iemkeyname = 500) を使用すると、キー値セットごとにパフォーマンスが 30 ミリ秒から 50 ミリ秒に低下します。多くのキーに書き込む場合、これは明らかに問題になります。同じキーに何度も書き込んでも、TrySetMember を介してアクセスすると同じ時間がかかります。

私の実際のパフォーマンスの問題は、TrySetMember オーバーライドと同様にダイナミクスを使用しているという事実とは関係がないようです。キックのために、私もコメントアウトしました

dictionary[binder.Name] = value;

TrySetMember メソッドで "return true;" だけを残し、パフォーマンスは同じでした。

次のようなものを SuperExpando クラスに追加すると --

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    if (dictionary.ContainsKey(binder.Name))
    {
        result = dictionary[binder.Name];
        return true;
    }
    return false; 
}

TryGetMember を介して変数にアクセスする (読み取る) パフォーマンスの問題は同じですが、ディクショナリを直接読み取ると妥当なパフォーマンスが得られます。

何か案は?

-BJクイン

編集: ここに完全なサンプル コードがあります。フォームを作成し、go_Click イベントを実行するボタンを配置し、プロジェクトをコンソール アプリケーションに設定するだけです。私の場合、ExpandoObject で 50 個のキーすべてを設定するのに約 30 ミリ秒かかりますが、SuperExpando では最小で約 750 ミリ秒かかります。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Dynamic;

namespace test
{
    public partial class ExpandoTest : Form
    {
        public ExpandoTest()
        {
            InitializeComponent();
        }

        public class SuperExpando : DynamicObject
        {
            public Dictionary<string, object> dictionary = new Dictionary<string, object>();

            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                //dictionary[binder.Name] = value;
                return true;
            }
        }

        DateTime lasttime = DateTime.Now;

        public void outputtime(string label = "")
        {
            TimeSpan elapsedtime = DateTime.Now - lasttime;
            Double elapsedms = elapsedtime.TotalMilliseconds;
            Console.WriteLine(label + " : " + elapsedms.ToString());
            lasttime = DateTime.Now;
        }

        private void go_Click(object sender, EventArgs e)
        {
            outputtime("Time spent waiting on user");
            dynamic se = new SuperExpando();
            outputtime("Declared SuperExpando");
            se.test120 = 5;
            se.test121 = 5;
            se.test122 = 5;
            se.test123 = 5;
            se.test124 = 5;
            se.test125 = 5;
            se.test126 = 5;
            se.test127 = 5;
            se.test128 = 5;
            se.test129 = 5;
            se.test130 = 5;
            se.test131 = 5;
            se.test132 = 5;
            se.test133 = 5;
            se.test134 = 5;
            se.test135 = 5;
            se.test136 = 5;
            se.test137 = 5;
            se.test138 = 5;
            se.test139 = 5;
            se.test140 = 5;
            se.test141 = 5;
            se.test142 = 5;
            se.test143 = 5;
            se.test144 = 5;
            se.test145 = 5;
            se.test146 = 5;
            se.test147 = 5;
            se.test148 = 5;
            se.test149 = 5;
            se.test150 = 5;
            se.test151 = 5;
            se.test152 = 5;
            se.test153 = 5;
            se.test154 = 5;
            se.test155 = 5;
            se.test156 = 5;
            se.test157 = 5;
            se.test158 = 5;
            se.test159 = 5;
            se.test160 = 5;
            se.test161 = 5;
            se.test162 = 5;
            se.test163 = 5;
            se.test164 = 5;
            se.test165 = 5;
            se.test166 = 5;
            se.test167 = 5;
            se.test168 = 5;
            se.test169 = 5;
            outputtime("Time to Run SuperExpando, set 50 test key/value pairs -- (not even setting values, just returning true from TrySetMember!)");

            dynamic eo = new ExpandoObject();
            outputtime("Declared ExpandoObject");
            eo.test120 = 5;
            eo.test121 = 5;
            eo.test122 = 5;
            eo.test123 = 5;
            eo.test124 = 5;
            eo.test125 = 5;
            eo.test126 = 5;
            eo.test127 = 5;
            eo.test128 = 5;
            eo.test129 = 5;
            eo.test130 = 5;
            eo.test131 = 5;
            eo.test132 = 5;
            eo.test133 = 5;
            eo.test134 = 5;
            eo.test135 = 5;
            eo.test136 = 5;
            eo.test137 = 5;
            eo.test138 = 5;
            eo.test139 = 5;
            eo.test140 = 5;
            eo.test141 = 5;
            eo.test142 = 5;
            eo.test143 = 5;
            eo.test144 = 5;
            eo.test145 = 5;
            eo.test146 = 5;
            eo.test147 = 5;
            eo.test148 = 5;
            eo.test149 = 5;
            eo.test150 = 5;
            eo.test151 = 5;
            eo.test152 = 5;
            eo.test153 = 5;
            eo.test154 = 5;
            eo.test155 = 5;
            eo.test156 = 5;
            eo.test157 = 5;
            eo.test158 = 5;
            eo.test159 = 5;
            eo.test160 = 5;
            eo.test161 = 5;
            eo.test162 = 5;
            eo.test163 = 5;
            eo.test164 = 5;
            eo.test165 = 5;
            eo.test166 = 5;
            eo.test167 = 5;
            eo.test168 = 5;
            eo.test169 = 5;
            outputtime("Time to Run ExpandoObject, set 50 test key/value pairs");
        }
    }
}
4

1 に答える 1

9

まず、このように時間を測定するべきではありません。DateTime.Nowミリ秒まで正確ではありません。これに使用する必要がありますStopwatch

第二に、一般的に.Netで、そして特に扱うときdynamic、順序は重要です。これは、CLRとDLRが最初に計算しなければならないことがいくつかあるが、2回目はキャッシュから取得できるためです。

第三に、私のテストでは、確かに750ミリ秒またはそれに近いものは見られませんでした。

最初に実行SuperExpandoし、両方のテストを2回実行すると、次のような時間が発生します。

SuperExpando: 50,7736 ms
EpandoObject: 27,786 ms
SuperExpando: 0,0285 ms
EpandoObject: 0,0373 ms

したがって、SuperExpando速度が遅くなり、違いが大きくなる可能性がありますが、これは初めてのことです。同じタイプで同じコードを再度実行すると、はるかに高速になります。

順序を逆にするとどうなりますか?

EpandoObject: 33,3107 ms
SuperExpando: 43,7383 ms
EpandoObject: 0,0348 ms
SuperExpando: 0,0186 ms

SuperExpandoまだ遅いですが、違いは小さくなっています。また、両方の2回目の実行は、数桁高速です。

于 2012-03-04T00:33:45.743 に答える