bjq*_*inn 4 c# dynamic expandoobject dynamicobject
我正在使用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();
Run Code Online (Sandbox Code Playgroud)
当我在DynamicObject的字典直接设定值(iemdictionary ["键名"] = 500),那么我看到类似ExpandoObject,其是亚毫秒时间设置在字典中的密钥的值的性能.当我使用TrySetMember覆盖(iemkeyname = 500)时,我看到性能下降到30ms - 每个键值设置50ms.写入大量密钥时,这显然会成为一个问题.即使我一遍又一遍地写同一个密钥,通过TrySetMember访问它也需要相同的时间.
我的真实性能问题似乎与我使用动态的事实无关,因为它会影响TrySetMember.对于踢,我甚至评论了
dictionary[binder.Name] = value;
Run Code Online (Sandbox Code Playgroud)
在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;
}
Run Code Online (Sandbox Code Playgroud)
通过TryGetMember访问(读取)变量的性能问题是相同的,而直接读取字典提供了合理的性能.
有任何想法吗?
-BJ奎因
编辑:这是完整的示例代码.只需创建一个表单并在其上放置一个运行go_Click事件的按钮,并将您的项目设置为控制台应用程序.对我来说,在ExpandoObject中设置所有50个键需要大约30ms,而SuperExpando至少需要750ms.
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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
首先,你不应该像这样衡量时间.DateTime.Now不精确到毫秒.你应该用Stopwatch它.
其次,在.Net中,dynamic特别是在处理时,订单很重要.这是因为CLR和DLR第一次必须计算几件事,但可以第二次从缓存中检索.
第三,在我的测试中,我当然没有看到750毫秒,或者接近那个.
如果我先跑SuperExpando,并做两次测试,我会得到这样的时间:
SuperExpando: 50,7736 ms
EpandoObject: 27,786 ms
SuperExpando: 0,0285 ms
EpandoObject: 0,0373 ms
Run Code Online (Sandbox Code Playgroud)
所以,SuperExpando速度较慢,差异可能很大,但这只是第一次.当您再次使用相同类型运行相同的代码时,速度会快得多.
当我们撤销订单时会发生什么?
EpandoObject: 33,3107 ms
SuperExpando: 43,7383 ms
EpandoObject: 0,0348 ms
SuperExpando: 0,0186 ms
Run Code Online (Sandbox Code Playgroud)
SuperExpando仍然较慢,但差异现在更小.而且,两者的第二次运行速度提高了几个数量级.