【问题标题】:(XSLT) why Saxon-HE 9.8 processor slower then dotNet processor in my case?(XSLT)在我的情况下,为什么 Saxon-HE 9.8 处理器比 dotNet 处理器慢?
【发布时间】:2018-01-19 08:05:01
【问题描述】:

(注意:这篇文章主要是通过回复建议编辑的) 我目前在 dotNet 处理器和 Saxon-HE 9.8 处理器中运行几乎相同的 xslt,但我发现 Saxon(2.2 秒)比 dotNet(0.03 秒)慢得多。那么我该如何解决呢?这是我的【简化】xml示例,只需复制<A>~<Z>并重复粘贴50次,我认为大小只有10kb左右:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="OutputFile.xslt"?>
  <Header>
      <A><![CDATA[NOTHING]]></A>
      <B><![CDATA[NOTHING]]></B>
      <C><![CDATA[NOTHING]]></C>
    <X>_R_testXR12</X>
    <Y>_R_testYR12</Y>
    <Z>_R_testZR12</Z>
  </Header>

这是我的代码:

撒克逊 C#

var processor = new Processor();
var compiler = processor.NewXsltCompiler();
var executable = compiler.Compile(new Uri(xslt.FullName));
var transformer = executable.Load30();
var serializer = new Serializer();

FileStream outStream = new FileStream(output.ToString(), FileMode.Create, FileAccess.Write);
serializer.SetOutputStream(outStream);

using (var inputStream = input.OpenRead())
{

    /*timer start*/
    var watch = Stopwatch.StartNew();
    transformer.ApplyTemplates(inputStream, serializer);
    /*timer end*/
    watch.Stop(); var elapsedMs = watch.ElapsedMilliseconds; Console.WriteLine(elapsedMs); Console.Read();
    outStream.Close();
}

撒克逊 XSLT

    <!--  Saxon  in xslt-->
<xsl:stylesheet version="3.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="yes"/>
  <xsl:template match="/">
    <xsl:variable name="NodesExtraCRI" select="/Header/*[( starts-with(text(), '_R_testZR'))]"></xsl:variable>
        <xsl:for-each select = "$NodesExtraCRI">
          <xsl:sort select = "text()" data-type = "number" order = "ascending"/>
          <xsl:value-of select="text()"/>
        </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

.NET C#

// Enable XSLT debugging.  
XslCompiledTransform xslt = new XslCompiledTransform(true);

// Compile the style sheet.  
xslt.Load(stylesheet);

// Execute the XSLT transform. 
/*timer start*/
var watch = System.Diagnostics.Stopwatch.StartNew();
FileStream outputStream = new FileStream(outputFile, FileMode.Append);
xslt.Transform(sourceFile, null, outputStream);
/*timer end*/
watch.Stop(); var elapsedMs = watch.ElapsedMilliseconds; Console.WriteLine(elapsedMs); Console.Read();

.NET XSLT

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" indent="yes"/>
  <xsl:template match="/">
    <xsl:variable name="NodesExtraCRI" select="/Header/*[( starts-with(text(), '_R_testZR'))]"></xsl:variable>
      <xsl:for-each select = "$NodesExtraCRI">
        <xsl:sort select = "text()" data-type = "number" order = "ascending"/>
        <xsl:value-of select="text()"/>
      </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

【问题讨论】:

  • 什么是“dotNet”处理器(XslCompiledTransform、XslTransform、XmlPrime)?你到底是怎么测量的?分享给出这些时间的代码或方法。
  • 谢谢,我编辑了它
  • 一般来说,你必须知道 Saxon 9 HE for .NET 和 Microsoft 的 XslCompiledTransform 有非常不同的架构,Saxon 是作为 Java 应用程序开发的,然后与 IKVM 交叉编译到 .NET,必须在 .NET 上携带各种提供基于 Java 的运行时结构的程序集,另一方面 XslCompiledTransform 是从 XSLT 1 到 .NET 的 CIL 的编译器,因此它在纯运行时性能方面 XslCompiledTransform 可能总是领先于 Saxon .NET 他。 Saxon EE 的性能可能比 HE 更好,因为它包含字节码生成。

标签: c# xml xslt saxon


【解决方案1】:

Saxon 的 .NET 版本通常比 Java 版本慢 3-5 倍,尽管付出了巨大的努力,但我们实际上并不知道为什么。有一次它只慢了 25%。

我怀疑您在这里看到的最大差异主要是初始化成本。尽管您的计时器仅测量转换时间(包括源文档解析和序列化,但不包括样式表编译),但您只运行一次转换,如果您运行 20 次,看看数字是否不同会很有趣取平均值。

你没有说源文档大小是多少,但是除非是几百兆,否则这里2.2s的时间似乎有点过分了,绝对值得寻找解释。

我注意到&lt;xsl:for-each select="1 to 10"&gt; 循环的主体不依赖于上下文项,因此这里有一个“循环提升”优化的范围,它只执行一次主体,然后复制结果。 Saxon-HE 没有尝试这样的优化,但 Saxon-EE 会,而且很可能 Microsoft 处理器也会这样做。但这还不足以解释这种差异。

因此,我建议进行以下实验以收集更多信息:

(a) 从 C# 级别重复运行转换以获得平均时间。

(b) 通过在循环体中做一些事情来消除循环提升优化的可能性,这意味着每次结果都不同

(c) 查看转换时间如何随源文档大小变化

(d) 进行一些分析以查看热点在哪里。

【讨论】:

  • 我根据您的建议进行了一些更改以测试并添加更多信息,我认为文档大小无关紧要,因为我测试了一个 10 kb 大小的 XML,您可以看看我的已编辑的帖子,我认为也值得解释一下,因为先生,您是开发人员,如果您尝试示例,那就太好了,谢谢
【解决方案2】:

我试图重现你所说的极端差异,但我无法做到。

由于我无法理解所提供的样式表应该做什么,特别是之前选择的元素上的 xsl:sortdata-type = "number"_R_testZR 开头(即字母而不是数字)似乎毫无意义,我首先编写了一些样式表来创建一些要排序的示例数据。

然后我创建了一个样式表来对生成的示例数据进行排序:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:output indent="yes"/>

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each select="*[starts-with(., 'test_Z')]">
                <xsl:sort select="@pos" data-type="number"/>
                <xsl:copy-of select="."/>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我首先使用 Saxon 9.8.0.7 HE Java 和 .NET 以及 -t -repeat 选项从命令行运行该样式表,以了解它是否真的需要几秒钟。但是,Java版本报告了

过去 11 次运行的平均执行时间:48.161523ms

和 .NET 版本

过去 11 次运行的平均执行时间:79.13861ms

所以 .NET 版本(众所周知)比 Java 版本慢得多,但它需要几毫秒而不是几秒,至少在我的机器上是这样。

然后,我在 Visual Studio 2017 中编写了一些 C# 控制台应用程序代码,以针对相同的示例执行相同的样式表,使用类似

的代码
    static void RunSaxon(string xmlUrl, string xslUrl, string outputFile)
    {
        Processor processor = new Processor();

        Xslt30Transformer xslt30Transformer = processor.NewXsltCompiler().Compile(new Uri(xslUrl)).Load30();

        using (Stream resultStream = File.OpenWrite(outputFile))
        {
            using (Stream inputStream = File.OpenRead(xmlUrl))
            {
                xslt30Transformer.ApplyTemplates(inputStream, processor.NewSerializer(resultStream));
            }
        }
    }

并使用Profiler in VS 来分析该代码,结果显示相当多的 CPU 使用量甚至不是来自ApplyTemplates() 调用,而是主要使用是由Compile 方法引起的(接近 64 % ) 后跟 new Processor() 构造函数(接近 28.5 %):

Function Name   Total CPU (ms)  Total CPU (%)   Self CPU (ms)   Self CPU (%)    Module
Saxon98HEPerfTest.exe (PID: 17768)  718 100,00 %    718 100,00 %    Saxon98HEPerfTest.exe
  Saxon98HEPerfTest.Program::Main   680 94,71 % 0   0,00 %  Saxon98HEPerfTest.exe
  Saxon98HEPerfTest.Program::RunSaxon   677 94,29 % 0   0,00 %  Saxon98HEPerfTest.exe
  [External Call] Saxon.Api.XsltCompiler::Compile   458 63,79 % 458 63,79 % Multiple modules
  [External Call] Saxon.Api.Processor::.ctor    204 28,41 % 204 28,41 % Multiple modules
  [External Call] Saxon.Api.Processor::NewXsltCompiler  5   0,70 %  5   0,70 %  Multiple modules

我在 RunSaxon 方法中设置断点得到的一些更详细的数据:

Function Name   Total CPU (ms)  Total CPU (%)   Self CPU (ms)   Self CPU (%)    Module
Saxon.Api.XsltCompiler::Compile 683 61,20 % 683 61,20 % Multiple modules
Saxon.Api.Processor::.ctor  214 19,18 % 214 19,18 % Multiple modules
Saxon.Api.Xslt30Transformer::ApplyTemplates 181 16,22 % 181 16,22 % Multiple modules
Saxon.Api.Processor::NewXsltCompiler    5   0,45 %  5   0,45 %  Multiple modules
Saxon.Api.XsltExecutable::Load30    3   0,27 %  3   0,27 %  Multiple modules
Saxon.Api.Processor::NewSerializer  1   0,09 %  1   0,09 %  Multiple modules
System.IO.File.OpenWrite(System.String)$##60017A3   1   0,09 %  1   0,09 %  Multiple modules

我还尝试使用秒表来测量ApplyTemplates() 呼叫,正如您尝试的那样:

    static void MeasureSaxon(string xmlUrl, string xslUrl, string outputFile)
    {
        Processor processor = new Processor();

        Xslt30Transformer xslt30Transformer = processor.NewXsltCompiler().Compile(new Uri(xslUrl)).Load30();

        Stopwatch watch = new Stopwatch();

        using (Stream resultStream = File.OpenWrite(outputFile))
        {
            using (Stream inputStream = File.OpenRead(xmlUrl))
            {            
                watch.Start();
                xslt30Transformer.ApplyTemplates(inputStream, processor.NewSerializer(resultStream));
                watch.Stop();
            }
        }
        Console.WriteLine("{0} ms", watch.ElapsedMilliseconds);
    }

我得到的结果大约是 165 毫秒。 XslCompiledTransform 确实更快,大约 32 毫秒,用

测量
    private static void RunXslCompiledTransform(string xmlUrl, string xslUrl, string outputFile)
    {
        XslCompiledTransform processor = new XslCompiledTransform();
        processor.Load(xslUrl);

        Stopwatch watch = new Stopwatch();
        watch.Start();
        processor.Transform(xmlUrl, outputFile);
        watch.Stop();
        Console.WriteLine("{0} ms", watch.ElapsedMilliseconds);
    }

但我并没有像你所经历的那样极端不同。

这是生成示例数据的样式表(需要 Saxon PE 或 EE,因为 random-number-generator() 使用):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:map="http://www.w3.org/2005/xpath-functions/map"
    xmlns:array="http://www.w3.org/2005/xpath-functions/array"
    exclude-result-prefixes="xs math map array"
    expand-text="yes"
    version="3.0">

    <xsl:output method="xml" indent="yes"/>

    <xsl:param name="seed" as="xs:dateTime" select="current-dateTime()"/>

    <xsl:param name="items" as="xs:integer" select="5000"/>

    <xsl:template match="/" name="xsl:initial-template">
        <root>
            <xsl:for-each select="random-number-generator($seed)?permute(1 to $items)">
                <A>...</A>
                <B>...</B>
                <C>...</C>
                <X>test_Y</X>
                <Y>test_Y</Y>
                <Z pos="{.}">test_Z_{format-number(., '0000')}</Z>
            </xsl:for-each>
        </root>
    </xsl:template>

</xsl:stylesheet>

由于生成示例数据的样式表需要 Saxon PE 或 EE,我已将生成的示例输入上传到 https://martin-honnen.github.io/xslt/2018/test2018012001Input5000.xml

【讨论】:

    【解决方案3】:

    我也试过运行你提供的代码sn-ps。

    这是我的结果:

    Saxon .Net 版本:59 毫秒(编译时间 28 毫秒,运行时间 31 毫秒)

    .Net 版本:3 毫秒

    我们知道 Saxon 在 .NET 上速度较慢,但​​我没有看到报告的 2-3 秒速度。

    【讨论】:

      猜你喜欢
      • 2011-10-09
      • 2018-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多