【问题标题】:XmlSerializer deserialization time fluctuates on WCF serviceXmlSerializer 反序列化时间在 WCF 服务上波动
【发布时间】:2013-08-23 14:11:08
【问题描述】:

更新

这个问题被确定不是 XmlSerializer 和反序列化的问题,而实际上是从远程源读取响应流的问题。感谢 Chris SinclairJesse C. Slicer 在确定我所看到的差异时提供的帮助和指导。


我一直在分析 XmlSerializer 将静态 XML 数据块反序列化到我的自定义 MyXmlObject 类所花费的时间,尽管数据恰好是一样。

我在将 XML 传递给 serializer.Deserialize() 方法的那一刻开始计时反序列化,并在完成后立即停止计时:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
//initialization stuff and writing to request stream.

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
    //If I run a stopwatch up to this point, I get the round trip time,
    //so I know that the stream has already been received at this point.

    using (Stream stream = response.GetResponseStream()) //In production this stream is received from a remote server.
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        MyXmlObject obj = (MyXmlObject)serializer.Deserialize(stream);

        stopwatch.Stop();
    }
}

我的XmlSerializer对象定义如下:

private static readonly XmlSerializer serializer = new XmlSerializer(typeof(MyXmlObject));

我的 WCF Web 服务是一个多线程的单例。

[ServiceBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple,
    InstanceContextMode = InstanceContextMode.Single)]

以下是我针对 15 个并发请求反序列化仅 177,792 字节的静态 XML 数据块的结果:

177,792 字节的静态 XML 文件

Request   Execution (ms)
1         300
2         302
3         303
4         303
5         368 *High
6         303
7         302
8         242 *Low
9         243
10        244
11        245
12        242
13        243
14        883 *Outlier
15        260

如您所见,它有些一致,但仍会波动大约 +/-100 毫秒

在这个相对较小的 XML 文件中,波动很小,但如果我提供一个更大的文件(我将在我的 WCF Web 服务中更频繁地接收它,它的波动会更大:

3,851,199 字节的静态 XML 文件

Request   Execution (ms)
1         1384
2         2402
3         1715
4         4000 *Outlier
5         1310
6         2132
7         1388
8         1654
9         1183
10        1464
11        2368
12        2752 *High
13        1094 *Low
14        1838
15        1940

因此,您可以看到反序列化 XML 文件所花费的时间比对较小文件的波动要大得多。

我希望它是相对相同的(只有 +/- 100 毫秒),但相反,我看到了 +/- 1200 毫秒或更大的差异。

此外,2.5 秒的时间比我希望放弃将 XML 反序列化为 C# POD 对象的时间还要多。有趣的是,当我将数据返回到服务器时,WCF 将 C# POD 对象序列化为 JSON 相对较快。

问题

  • 为什么会出现这些波动?
  • 如果有的话,我可以做些什么来尽量减少这些波动?
  • 是否有另一种类型的 Xml 反序列化可以更快地将这种大小的传入 Xml 转换为 C# POD 对象的层次结构?
    • 也许是DataContractSerializer
    • DataContractSerializer 不是在底层使用 XmlSerializer 吗?

如果我遗漏了任何对诊断此问题有用的信息,请告诉我。谢谢。

【问题讨论】:

  • 我可以想象可能还有其他力量在起作用。通过 WCF 服务,尤其是对于较大的文件,可能会由于 HTTP/网络传输而产生更大的波动。您还提到这些是“并发请求”;如果您的意思是它们同时发生,那么这也会影响时间,因为它们竞争资源或您的 WCF 服务将请求排队处理。
  • 我建议您尝试简单地测量时间以传输原始流数据/文本没有反序列化,看看您是否发现差异。根据我的经验,使用 XmlSerializer 进行反序列化非常快(大约为几分之一秒,远不及 2 到 4 秒),即使对于 ~4mb 文件也是如此。
  • 另外,我假设你没有实现IXmlSerializable;我曾经不小心搞砸了该实现,并引入了读取数据的重大瓶颈。 (一旦固定,阅读时间可以忽略不计)
  • @ChrisSinclair “由于 HTTP/网络传输”是说在(MyXmlObject)serializer.Deserialize(stream) 行中还没有从远程源接收到整个流?
  • @ChrisSinclair 我测量了传输 XML 的时间。但是,请注意我在调用 Deserialize 之前和之后如何启动和停止秒表。

标签: c# xml wcf xmlserializer


【解决方案1】:

根据 cmets,这里有一段代码纯粹用于测量反序列化时间,而不是通过网络接收响应流。请注意,它确实依赖于 .NET 4 的 CopyTo() 方法在 Stream 上工作。

var request = (HttpWebRequest)WebRequest.Create(uri);
// initialization stuff and writing to request stream.

using (var response = (HttpWebResponse)request.GetResponse())
using (var stream = response.GetResponseStream())
using (var memoryStream = new MemoryStream())
{
    // In production this stream is fully received from a remote server.
    stream.CopyTo(memoryStream);
    memoryStream.Seek(0, SeekOrigin.Begin);

    // If I run a stopwatch up to this point, I get the round trip time,
    // so I know that the stream has already been received at this point.
    var stopwatch = Stopwatch.StartNew();

    var obj = (MyXmlObject)serializer.Deserialize(memoryStream);

    stopwatch.Stop();
}

【讨论】:

  • 谢谢。我对其进行了一些不同的测量,但能够确定 ChrisSinclair 是正确的,并且反序列化并不是真正的罪魁祸首。我现在正在努力理解为什么阅读 Stream 需要这么长时间。例如,在 SoapUI 中,我可以在 7700 毫秒内获取整个原始 XML,然后转储到屏幕上。 request.GetResponse() 几乎需要这个确切的时间才能完成。但是,现在它正在添加另一个1-2s,以便在收到响应后从远程服务器检索Stream。我想做的是消除这个1-2s 的额外处理时间。
  • 这个问题和任何答案对您有帮助吗? stackoverflow.com/questions/901323/… 或者这篇文章? holyhoehle.wordpress.com/2010/01/12/webrequest-slow
  • 当我初始化我的HttpWebRequest 时,我正在设置request.Proxy = null;,因为我遇到了另一个提到类似内容的问题。我刚刚实现了链接的 SO 问题中提到的BufferedStream,但它似乎没有帮助。流的读取仍然需要大约 1-2 秒。我将发布一个新问题,重点是阅读 Stream 问题,因为这似乎是问题的真正根源。谢谢!
【解决方案2】:

鉴于您的 cmets 和您进行基准测试的方式,我认为速度缓慢或波动的根源不是 XML 反序列化。

根据我的经验,使用XmlSerializer 进行反序列化非常快,对于大约 3.5 兆字节的文件,肯定不会在 2-4 秒的范围内。我敢打赌,如果您要进行另一项测试,只需在没有任何网络的情况下反序列化静态 XML 字符串,您会发现它非常非常快(我打赌不到 50 毫秒;当然远不及 2000 毫秒)。

但是,对于您发布的代码,我相信存在一些网络问题或其他影响时间的因素。如果您更改基准测试以将流读取与 XML 反序列化分开,您会发现反序列化本身对总时间的贡献可能可以忽略不计。

我怀疑 WCF 服务还存在其他网络问题。我建议发布一个新问题,完全消除 XML 反序列化,并简单地测量读取原始数据流的速度。也许用户将能够为您提供有关如何优化/配置/改进该方面的更好想法,因为序列化不是这里的问题。

【讨论】:

  • 我希望我能为你们俩增加更多的声誉。我确信这是反序列化的问题,直到你向我指出我需要实现更细粒度的分析才能确定这一点,我会继续追求这种思路。我现在看到在解析HttpWebRequest 之后读取传入的数据流需要 1-2 秒。我将发布一个新问题。感谢您的帮助!
  • @crush:实际上,这是有道理的(至少在我看来)。如果 Jesse 指出存在网络协商问题(如代理检测),则有助于约 7.5 秒的初始请求+GetResponse(),1-2 秒即可下载/传输通过 HTTP 约 3.5 兆字节听起来相当似是而非。
  • 我要确定的是request.GetResponse() 是否会访问远程服务器,然后response.GetResponseStream() 是否会第二次访问远程服务器。我一直假设响应有效负载嵌入在 HTTP 响应中。我现在正在寻找那个答案。
  • @crush 你以前用过 Fiddler 吗?如果没有,我强烈推荐它:fiddler2.com/install。您可以查看所有连接、流量和时间。如果我不得不猜测 [是的,我只是在猜测 :)],GetResponse() 可能正在执行 HEAD 请求以获取响应的大小,然后 GetResponseStream() 执行 GET(或 POST,无论您指定什么)来根据标头中的信息获取剩余的有效载荷。
  • @JesseC.Slicer Wireshark 和/或 Chrome 开发人员工具一直是我的选择,但 WCF 服务在远程(我的网络内部)服务器上运行。我可以在该服务器上安装wireshark,但宁愿不。假设您对 HEAD+POST 的看法是正确的,我想知道是否有办法将这些组合成一次旅行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-04
相关资源
最近更新 更多