使用 Saxon-JS 识别 XSLT 转换的性能瓶颈
Identify performance bottlenecks for XSLT transform using Saxon-JS
任何人都可以提供一些关于查明转换瓶颈的指导吗?
这是 Saxon-JS 的 node.js 实现。我正在尝试提高转换某些 XML 文档的速度,以便我可以提供同步 API,理想情况下响应时间低于 60 秒(230 秒是应用程序网关的硬性限制)。我还需要能够处理最大 50MB 大小的 XML 文件。
我有 运行 节点的内置分析器 (https://nodejs.org/en/docs/guides/simple-profiling/)。但很难理解结果,因为免费版本的 Saxon-JS 的源代码并不是真正可读的。
我的代码
const path = require('path');
const SaxonJS = require('saxon-js');
const { loadCodelistsInMem } = require('../standards_cache/codelists');
const { writeFile } = require('../config/fileSystem');
const config = require('../config/config');
const { getStartTime, getElapsedTime } = require('../config/appInsights');
// Used for easy debugging the xslt stylesheet
// Runs iati.xslt transform on the supplied XML
const runTransform = async (sourceFile) => {
try {
const fileName = path.basename(sourceFile);
const codelists = await loadCodelistsInMem();
// this pulls the right array of SaxonJS resources from the resources object
const collectionFinder = (url) => {
if (url.includes('codelist')) {
// get the right filepath (remove file:// and after the ?
const versionPath = url.split('schemata/')[1].split('?')[0];
if (codelists[versionPath]) return codelists[versionPath];
}
return [];
};
const start = getStartTime();
const result = await SaxonJS.transform(
{
sourceFileName: sourceFile,
stylesheetFileName: `${config.TMP_BASE_DIR}/data-quality/rules/iati.sef.json`,
destination: 'serialized',
collectionFinder,
logLevel: 10,
},
'async'
);
console.log(`${getElapsedTime(start)} (s)`);
await writeFile(`performance_tests/output/${fileName}`, result.principalResult);
} catch (e) {
console.log(e);
}
};
runTransform('performance_tests/test_files/test8meg.xml');
示例控制台输出:
❯ node --prof utils/runTransform.js
SEF generated by Saxon-JS 2.0 at 2021-01-27T17:10:38.029Z with -target:JS -relocate:true
79.938 (s)
❯ node --prof-process isolate-0x102d7b000-19859-v8.log > v8_log.txt
文件:
- stylesheet
- 示例XML:是test8meg.xml
- 节点分析日志v8_log.txt
最大性能违规者的 V8 日志片段:
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 1.0% are not shown.
ticks parent name
33729 52.5% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
6901 20.5% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
3500 50.7% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
3197 91.3% LazyCompile: *k /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:287:264
3182 99.5% LazyCompile: *<anonymous> /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:682:218
2880 90.5% LazyCompile: *d /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:734:184
非常感谢。没有大量的资源可以让我自己完成。我也已经试过了:
- 使用预解析的 stylesheetInternal 参数 JSON(差别不大)
- 将文档拆分为仅包含一个活动的单独文档
<iati-activity>
根元素内的子元素 <iati-activities>
根元素,分别转换每个元素,然后将其放回原处,这最终花费了 2 倍的时间.
最佳,
尼克
你在https://saxonica.plan.io/boards/5/topics/8105?r=8106问了同样的问题,我已经在那里回复了。我知道 Whosebug 不喜欢 link-only 答案,但我更喜欢通过我们自己的支持渠道而不是尽可能通过 Whosebug 来支持用户。
任何人都可以提供一些关于查明转换瓶颈的指导吗?
这是 Saxon-JS 的 node.js 实现。我正在尝试提高转换某些 XML 文档的速度,以便我可以提供同步 API,理想情况下响应时间低于 60 秒(230 秒是应用程序网关的硬性限制)。我还需要能够处理最大 50MB 大小的 XML 文件。
我有 运行 节点的内置分析器 (https://nodejs.org/en/docs/guides/simple-profiling/)。但很难理解结果,因为免费版本的 Saxon-JS 的源代码并不是真正可读的。
我的代码
const path = require('path');
const SaxonJS = require('saxon-js');
const { loadCodelistsInMem } = require('../standards_cache/codelists');
const { writeFile } = require('../config/fileSystem');
const config = require('../config/config');
const { getStartTime, getElapsedTime } = require('../config/appInsights');
// Used for easy debugging the xslt stylesheet
// Runs iati.xslt transform on the supplied XML
const runTransform = async (sourceFile) => {
try {
const fileName = path.basename(sourceFile);
const codelists = await loadCodelistsInMem();
// this pulls the right array of SaxonJS resources from the resources object
const collectionFinder = (url) => {
if (url.includes('codelist')) {
// get the right filepath (remove file:// and after the ?
const versionPath = url.split('schemata/')[1].split('?')[0];
if (codelists[versionPath]) return codelists[versionPath];
}
return [];
};
const start = getStartTime();
const result = await SaxonJS.transform(
{
sourceFileName: sourceFile,
stylesheetFileName: `${config.TMP_BASE_DIR}/data-quality/rules/iati.sef.json`,
destination: 'serialized',
collectionFinder,
logLevel: 10,
},
'async'
);
console.log(`${getElapsedTime(start)} (s)`);
await writeFile(`performance_tests/output/${fileName}`, result.principalResult);
} catch (e) {
console.log(e);
}
};
runTransform('performance_tests/test_files/test8meg.xml');
示例控制台输出:
❯ node --prof utils/runTransform.js
SEF generated by Saxon-JS 2.0 at 2021-01-27T17:10:38.029Z with -target:JS -relocate:true
79.938 (s)
❯ node --prof-process isolate-0x102d7b000-19859-v8.log > v8_log.txt
文件:
- stylesheet
- 示例XML:是test8meg.xml
- 节点分析日志v8_log.txt
最大性能违规者的 V8 日志片段:
[Bottom up (heavy) profile]:
Note: percentage shows a share of a particular caller in the total
amount of its parent calls.
Callers occupying less than 1.0% are not shown.
ticks parent name
33729 52.5% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
6901 20.5% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
3500 50.7% T __ZN2v88internal20Builtin_ConsoleClearEiPmPNS0_7IsolateE
3197 91.3% LazyCompile: *k /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:287:264
3182 99.5% LazyCompile: *<anonymous> /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:682:218
2880 90.5% LazyCompile: *d /Users/nosvalds/Projects/validator-api/node_modules/saxon-js/SaxonJS2N.js:734:184
非常感谢。没有大量的资源可以让我自己完成。我也已经试过了:
- 使用预解析的 stylesheetInternal 参数 JSON(差别不大)
- 将文档拆分为仅包含一个活动的单独文档
<iati-activity>
根元素内的子元素<iati-activities>
根元素,分别转换每个元素,然后将其放回原处,这最终花费了 2 倍的时间.
最佳,
尼克
你在https://saxonica.plan.io/boards/5/topics/8105?r=8106问了同样的问题,我已经在那里回复了。我知道 Whosebug 不喜欢 link-only 答案,但我更喜欢通过我们自己的支持渠道而不是尽可能通过 Whosebug 来支持用户。