【发布时间】:2010-11-20 16:31:48
【问题描述】:
有人告诉我,WCF 至少应该和远程处理一样快。然而,我在这里有一个特定的场景,它甚至没有接近,我想知道是否有人能发现我做错的明显事情。我正在研究用 wcf 替换远程处理的可能性,以进行进程内应用程序域内通信的繁重工作。代码如下:
[ServiceContract]
interface IWorkerObject
{
[OperationContract] Outcome DoWork(Input t);
}
[DataContract]
[Serializable]
class Input
{
[DataMember] public int TaskId { get; set; }
[DataMember] public int ParentTaskId { get; set; }
[DataMember] public DateTime DateCreated { get; set; }
[DataMember] public string TextData { get; set; }
[DataMember] public byte[] BinaryData { get; set; }
}
[DataContract]
[Serializable]
class Outcome
{
[DataMember] public string Result { get; set; }
[DataMember] public string TextData { get; set; }
[DataMember] public byte[] BinaryData { get; set; }
}
class Program
{
static void Main(string[] args)
{
run_rem_test();
run_wcf_test();
run_rem_test();
run_wcf_test();
}
static void run_rem_test()
{
var dom = AppDomain.CreateDomain("remoting domain", null);
var obj = dom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as IWorkerObject;
RunTest("remoting", obj);
AppDomain.Unload(dom);
}
static void run_wcf_test()
{
var dom = AppDomain.CreateDomain("wcf domain", null);
var dcnt = dom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as WorkerObject;
var fact = new ChannelFactory<IWorkerObject>(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
var chan = fact.CreateChannel();
dcnt.OpenChannel();
RunTest("wcf", chan);
fact.Close();
dcnt.CloseChannel();
AppDomain.Unload(dom);
}
static void RunTest(string test, IWorkerObject dom)
{
var t = new Input()
{
TextData = new string('a', 8192),
BinaryData = null,
DateCreated = DateTime.Now,
TaskId = 12345,
ParentTaskId = 12344,
};
var sw = System.Diagnostics.Stopwatch.StartNew();
for( var i = 0; i < 1000; i++ )
dom.DoWork(t);
sw.Stop();
Console.WriteLine("{1} test run in {0}ms", sw.ElapsedMilliseconds, test);
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class WorkerObject : MarshalByRefObject, IWorkerObject
{
ServiceHost m_host;
public void OpenChannel()
{
m_host = new ServiceHost(this);
m_host.AddServiceEndpoint(typeof(IWorkerObject), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
m_host.Open();
}
public void CloseChannel()
{
m_host.Close();
}
public Outcome DoWork(Input t)
{
return new Outcome()
{
TextData = new string('b', 8192),
BinaryData = new byte[1024],
Result = "the result",
};
}
}
当我运行这段代码时,我会得到如下所示的数字:
远程测试在 386 毫秒内运行 wcf 测试运行时间为 3467 毫秒 远程测试在 499 毫秒内运行 wcf 测试运行时间为 1840 毫秒更新:事实证明,对于 WCF 而言,这只是初始设置的成本很高(谢谢,Zach!)。因为我在每次测试中都在重新创建 AppDomain,所以我一遍又一遍地付出这个代价。这是更新的代码:
[ServiceContract]
interface IWorkerObject
{
[OperationContract] Outcome DoWork(Input t);
}
[DataContract]
[Serializable]
class Input
{
[DataMember] public int TaskId { get; set; }
[DataMember] public int ParentTaskId { get; set; }
[DataMember] public DateTime DateCreated { get; set; }
[DataMember] public string TextData { get; set; }
[DataMember] public byte[] BinaryData { get; set; }
}
[DataContract]
[Serializable]
class Outcome
{
[DataMember] public string Result { get; set; }
[DataMember] public string TextData { get; set; }
[DataMember] public byte[] BinaryData { get; set; }
}
class Program
{
static void Main(string[] args)
{
var rem_dom = AppDomain.CreateDomain("remoting domain", null);
var rem_obj = rem_dom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as IWorkerObject;
var wcf_dom = AppDomain.CreateDomain("wcf domain", null);
var mgr_obj = wcf_dom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as WorkerObject;
var fact = new ChannelFactory<IWorkerObject>(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
var wcf_obj = fact.CreateChannel();
var rem_tot = 0L;
var wcf_tot = 0L;
mgr_obj.OpenChannel();
for( var i = 0; i < 10; i++ )
{
rem_tot += RunTest("remoting", i, rem_obj);
wcf_tot += RunTest("wcf", i, wcf_obj);
}
fact.Close();
mgr_obj.CloseChannel();
AppDomain.Unload(rem_dom);
AppDomain.Unload(wcf_dom);
Console.WriteLine();
Console.WriteLine("remoting total: {0}", rem_tot);
Console.WriteLine("wcf total: {0}", wcf_tot);
}
static long RunTest(string test, int iter, IWorkerObject dom)
{
var t = new Input()
{
TextData = new string('a', 8192),
BinaryData = null,
DateCreated = DateTime.Now,
TaskId = 12345,
ParentTaskId = 12344,
};
var sw = System.Diagnostics.Stopwatch.StartNew();
for( var i = 0; i < 1000; i++ )
dom.DoWork(t);
sw.Stop();
Console.WriteLine("{1,-8} {2,2} test run in {0}ms", sw.ElapsedMilliseconds, test, iter);
return sw.ElapsedMilliseconds;
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class WorkerObject : MarshalByRefObject, IWorkerObject
{
ServiceHost m_host;
public void OpenChannel()
{
m_host = new ServiceHost(typeof(WorkerObject));
m_host.AddServiceEndpoint(typeof(IWorkerObject), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
m_host.Open();
}
public void CloseChannel()
{
m_host.Close();
}
public Outcome DoWork(Input t)
{
return new Outcome()
{
TextData = new string('b', 8192),
BinaryData = new byte[1024],
Result = "the result",
};
}
}
这段代码给出这样的数字:
远程 0 测试在 377 毫秒内运行 wcf 0 测试在 2255 毫秒内运行 远程 1 次测试在 488 毫秒内运行 wcf 1 测试在 353 毫秒内运行 远程处理 2 次测试在 507 毫秒内运行 wcf 2 测试在 355 毫秒内运行 远程处理 3 次测试在 495 毫秒内运行 wcf 3 测试在 351 毫秒内运行 远程 4 测试运行在 484 毫秒 wcf 4 测试在 344 毫秒内运行 远程 5 次测试在 484 毫秒内运行 wcf 5 测试在 354 毫秒内运行 远程 6 次测试在 483 毫秒内运行 wcf 6 测试在 346 毫秒内运行 远程 7 测试在 491 毫秒内运行 wcf 7 测试在 347 毫秒内运行 远程 8 次测试在 485 毫秒内运行 wcf 8 测试在 358 毫秒内运行 远程 9 次测试在 494 毫秒内运行 wcf 9 测试在 338 毫秒内运行 远程总:4788 wcf 总计:5401【问题讨论】:
-
这可能取决于您的配置,您不觉得吗?使用的绑定可能很重要,甚至可能是某些设置。
-
当然可以。这正是我在这里寻找的。我正在使用 NetNamedPipes,我知道它应该是最快的,并且我已经关闭了安全性,但除此之外,我不知所措。
-
对于应用内域工作,我建议使用某种形式的接口解析器来滚动您自己的解决方案。这就是我为这类问题所做的。使用 ReaderWriterLockSlim 进行锁定(或者您可以使用 Unity)正确滚动大约需要 2-3 个小时,并且由于没有序列化并且解决方案远没有那么复杂,因此会比 WCF 或远程处理带来巨大的性能提升。我了解 WCF 团队正在提出一个进程内频道,但它不会进入 C# 4.0。
-
这些是您在使用远程处理或 WCF 时必须处理的延迟吗?我实现了本教程...bloggingabout.net/blogs/dennis/archive/2007/04/20/… 并且在创建代理和调用远程方法时得到了约 500 毫秒的延迟。对于我想要使用它的东西来说,这延迟太大了。我应该走套接字路线,还是应该放弃使用远程过程调用来实现我的项目的希望?
-
史蒂夫,您能详细说明一下您提出的解决方案吗? ReaderWriterLockSlim 处理锁定方案,但是如何在没有 WCF 或 Remoting 的情况下跨 AppDomain 边界传递方法调用?
标签: .net wcf performance remoting