.net - 为什么我发现 WCF 的性能比远程处理的性能慢?

标签 .net wcf performance remoting

据我所知,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/

相关文章:

.net - 使用 PropertyChanged.Fody 构建应用程序时出错

.net - 我是否需要编写自己的非托管 IL 库来使用 CLR Profiling API 重写 IL?

c# - 将 .NET 类库(主要定义 CRUD 操作)公开为服务

javascript - 删除属性或构造仅具有所需属性的新对象更快吗?

c# - '一个:Action' must be understood but cannot be handled and RequestSecurityToken problem on an ONVIF Camera

c# - TimeSpan.ParseExact(毫秒)

wcf - ApplyClientBehavior 没有被调用

wpf - 有没有一种方法可以在客户端的一个地方处理 WCF 服务器故障?

java - 使用 MySQL 和 Java 优化排序功能

MYSQL 查询性能 - 按距离搜索