(注意:这篇文章主要通过回复建议进行了编辑)
我目前在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>
以下是我的代码:
Saxon 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();
}
Saxon 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>
答案 0 :(得分:2)
Saxon的.NET版本通常比Java版本慢3-5倍,尽管付出了艰苦的努力,但我们实际上并不知道为什么。有一段时间它只慢了25%。
我怀疑你在这里看到的重大差异主要是初始化成本。虽然你的计时器只测量转换时间(包括源文件解析和序列化但不包括样式表编译),但你只运行转换一次,如果你运行20次,看看数字是否不同会很有趣。取平均值。
你还没有说过源文件大小是什么,但除非是数百兆字节,否则2.2s的时间似乎相当过分,绝对值得寻找解释。
我注意到<xsl:for-each select="1 to 10">
循环的主体不依赖于上下文项,因此这里有一个“循环提升”优化的范围,它只执行一次主体然后复制结果。 Saxon-HE并没有尝试这样的优化,但Saxon-EE确实如此,微软处理器也很有可能。但这还不足以说明这种差异。
所以,我建议以下实验来收集更多见解:
(a)从C#级别重复运行变换以获得平均时间。
(b)通过在循环体中做某事来消除循环提升优化的可能性,这意味着每次结果都不同
(c)了解转换时间如何随源文档大小而变化
(d)进行一些剖析以查看热点的位置。
答案 1 :(得分:1)
我试图重现你说的极端差异,但我无法做到。
由于我无法理解所提供的样式表应该做什么,特别是xsl:sort
data-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>
开头(即字母和不是数字)似乎毫无意义,我首先写了一些样式表来创建一些要排序的样本数据。
然后我创建了一个样式表来对生成的样本数据进行排序:
-t -repeat
我首先使用Saxon 9.8.0.7 HE Java和.NET以及 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));
}
}
}
选项从命令行运行该样式表,以了解是否确实需要几秒钟。但是,Java版本已报告
过去11次运行的平均执行时间:48.161523ms
和.NET版
过去11次运行的平均执行时间:79.13861ms
所以.NET版本(已知)比Java版本慢得多,但它至少在我的机器上需要毫秒而不是秒。
此外,我在Visual Studio 2017中编写了一些C#控制台应用程序代码,使用类似
之类的代码对同一样本执行相同的样式表ApplyTemplates()
并使用Profiler in VS来分析该代码,结果显示大量的CPU使用量甚至不会来自Compile
调用,而是主要用途是{{1} }方法(接近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);
}
但是我没有像你似乎经历的那样发生如此极端的差异。
以下是生成样本数据的样式表(由于random-number-generator()
使用而需要Saxon PE或EE):
<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。
答案 2 :(得分:0)
我还尝试运行您提供的代码段。
以下是我的结果:
Saxon .Net版本:59 ms(编译时间28 ms,运行时间31 ms)
.Net版本:3毫秒
我们知道Saxon在.NET上速度较慢,但我没有看到报告的2-3秒速度。