为什么我从WCF看到的性能比Remoting慢?

时间:2009-08-18 17:27:37

标签: .net wcf performance remoting

我被告知的一切都说WCF应该至少和远程处理一样快。我有一个特定的场景,然而,它甚至没有接近,我想知道是否有人可以发现一些明显的我做错了。我正在研究用wcf替换远程处理以进行进程内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)
    {
        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

更新:事实证明,这只是初始设置对于WCF来说是如此昂贵(谢谢,Zach!)。因为我在每次测试中都在重新创建AppDomains,所以我一次又一次地支付这个价格。这是更新后的代码:

[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

2 个答案:

答案 0 :(得分:19)

笔记

在大多数情况下,花费时间来设置和拆除WCF频道。其他狮子分享的时间似乎是通过在调试器中运行来消耗的。请记住,这是非正式的类型测试.. :)

我能够重现你的数字,所以我从那里开始。这表明WCF数字<远>大于远程数字的3倍。

通过缓存频道工厂(以及相应的远程处理对象),数字会下降,以便WCF 仅比远程数字大2倍

在尝试了十几个其他不同的调整后,唯一一个似乎削减数字的任何实际可测量时间的是清除所有调试服务行为(默认添加) - 这导致大约100ms。还不足以让人感到兴奋。

然后,在一时兴起,我在发布配置中运行调试器外部的代码。数字下降到大致相当于远程数字,如果不是更好。额头砸了桌子并称它完成了。

简而言之,您应该看到大致相同,使用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",
        };
    }

}

答案 1 :(得分:1)

我发现在调试模式下使用visual studio时,使用netnamedpipes的wcf比远程调用慢。如果将其更改为发布模式,无论是否附加调试器,wcf都与远程处理一样快。