据我所知,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",
};
}
}
当我运行此代码时,我得到如下所示的数字:
remoting test run in 386ms wcf test run in 3467ms remoting test run in 499ms wcf test run in 1840ms
UPDATE: So it turns out that it's just the initial setup that is so costly for WCF (Thanks, Zach!). Because I was recreating the AppDomains in each test, I was paying that price over and over. Here's the updated code:
[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",
};
}
}
此代码给出如下数字:
remoting 0 test run in 377ms wcf 0 test run in 2255ms remoting 1 test run in 488ms wcf 1 test run in 353ms remoting 2 test run in 507ms wcf 2 test run in 355ms remoting 3 test run in 495ms wcf 3 test run in 351ms remoting 4 test run in 484ms wcf 4 test run in 344ms remoting 5 test run in 484ms wcf 5 test run in 354ms remoting 6 test run in 483ms wcf 6 test run in 346ms remoting 7 test run in 491ms wcf 7 test run in 347ms remoting 8 test run in 485ms wcf 8 test run in 358ms remoting 9 test run in 494ms wcf 9 test run in 338ms remoting total: 4788 wcf total: 5401
最佳答案
注释
大部分时间都花在设置和拆除 WCF channel 上。其他大部分时间似乎都消耗在调试器中运行。请记住,这实际上是非正式的类型测试..:)
我能够重现你的数字,所以我从那里开始。这表明 WCF 数量大约比远程处理数量大 3 倍。
通过缓存 channel 工厂(以及相应的远程处理对象),数量会下降,因此 WCF 仅比远程处理数量大 2 倍。
在尝试了十几种其他不同的调整后,唯一能真正缩短时间的似乎是清除所有调试服务行为(默认添加) - 这导致大约 100 毫秒。还不足以让人真正兴奋。
然后,一时兴起,我在调试器之外的发布配置中运行了代码。这些数字下降到与远程处理数字大致相当,甚至更好。额头拍了一下 table ,大功告成。
简而言之,您应该看到大致相同的结果,但使用 WCF 可能会获得更好的性能,并获得更好的编程模型来启动。
示例运行
remoting 1 test run in 347ms
wcf 1 test run in 1544ms
remoting 2 test run in 493ms
wcf 2 test run in 324ms
remoting 3 test run in 497ms
wcf 3 test run in 336ms
remoting 4 test run in 449ms
wcf 4 test run in 289ms
remoting 5 test run in 448ms
wcf 5 test run in 284ms
remoting 6 test run in 447ms
wcf 6 test run in 282ms
remoting 7 test run in 439ms
wcf 7 test run in 281ms
remoting 8 test run in 441ms
wcf 8 test run in 278ms
remoting 9 test run in 441ms
wcf 9 test run in 278ms
remoting 10 test run in 438ms
wcf 10 test run in 286ms
代码
注意 - 这段代码已经完全被破坏了。我道歉。 :)
using System;
using System.ServiceModel;
using System.Runtime.Serialization;
[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 AppDomain dom = AppDomain.CreateDomain("wcf domain", null);
static WorkerObject dcnt = dom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as WorkerObject;
static ChannelFactory<IWorkerObject> fact = new ChannelFactory<IWorkerObject>(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
static IWorkerObject chan = fact.CreateChannel();
static AppDomain remdom = AppDomain.CreateDomain("remoting domain", null);
static IWorkerObject remoteObject;
static void Main(string[] args)
{
remoteObject = remdom.CreateInstanceFromAndUnwrap(System.Reflection.Assembly.GetExecutingAssembly().Location, typeof(WorkerObject).FullName) as IWorkerObject;
dcnt.OpenChannel();
int numberOfIterations = 10;
for (int i = 1; i <= numberOfIterations; i++)
{
run_rem_test(i);
run_wcf_test(i);
}
fact.Close();
dcnt.CloseChannel();
AppDomain.Unload(dom);
AppDomain.Unload(remdom);
}
static void run_rem_test(int iteration)
{
RunTest("remoting " + iteration, remoteObject);
}
static void run_wcf_test(int iteration)
{
RunTest("wcf " + iteration, chan);
}
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(typeof(WorkerObject));
m_host.AddServiceEndpoint(typeof(IWorkerObject), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "net.pipe://localhost/the_channel");
// cache our ServiceBehaviorAttribute, clear all other behaviors (mainly debug)
// and add our ServiceBehavior back
//
var b = m_host.Description.Behaviors[0] as ServiceBehaviorAttribute;
m_host.Description.Behaviors.Clear();
m_host.Description.Behaviors.Add(b);
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",
};
}
}
关于.net - 为什么我发现 WCF 的性能比远程处理的性能慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1295353/