【发布时间】:2025-12-14 07:25:01
【问题描述】:
在工作中,我们使用的是 XML 日志文件。每个日志消息都是一个带有<date> 和<time> 子节点的<message> 块,有<submessage> 块、<table> 构造等等,并且可以使用一些 Delphi 处理和 XSLT 将日志文件转换为本地化的 HTML稍后。
对于中等大小的日志文件(大约 2 MB),我们遇到了性能问题(加载 XML 和进行一些基本操作最多需要一分钟),我可以将它们简化为这样的测试项目(编辑:更新代码并添加测量值):
procedure TForm1.PrepareTest(MessageCount : integer);
var
XML : IXMLDocument;
i : integer;
begin
XML := NewXMLDocument;
XML.DocumentElement := XML.CreateNode('root');
for i := 1 to MessageCount do
begin
XML.DocumentElement.AddChild('message').Text := 'Test Text';
end;
XML.SaveToFile(XML_NAME);
end;
procedure TForm1.XMLTest;
var
StartTime : Cardinal;
XML : IXMLDocument;
begin
StartTime := GetTickCount();
XML := NewXMLDocument;
XML.LoadFromFile(XML_NAME);
Memo1.Lines.Add('Node count: ' + IntToStr(XML.DocumentElement.ChildNodes.Count));
Memo1.Lines.Add('Time: ' + FloatToStr((GetTickCount() - StartTime) / 1000) + ' seconds');
end;
这会产生以下时间测量结果(节点计数每列增加 25%,所有时间都以毫秒为单位):
Node count 8000 10000 12500 15625 19531 24413 30516 38145 47681
Base test time 484 781 1140 1875 2890 4421 6734 10672 16812
Variation 1 32 47 62 78 78 141
Variation 2 2656 3157 3906 5015 6532 8922 12140 17391 24985
(delta Base) 2172 2376 2766 3140 3642 4501 5406 6719 8173
注意这两种变体,第一种是 LoadFromFile,第二种是像 PrepareTest 那样在 XML 的开头(!)另外添加 10000 个节点,这是最坏的情况,但查看增量基础测试,即使这也没有显示二次效应。另请注意,计算节点可以用任何其他操作替换,因此看起来涉及的 XML 文件的初始化/验证存在一些延迟,这会导致问题,并且之后的任何操作都会显示预期的行为。
内存使用不高,最后一个测试用例(47681 个节点)的内存使用峰值为 39 MB,其 XML 文件大小为 1.3 MB。
加载 XML 后做的第一件事(例如读取或写入一些节点或访问节点计数)很慢,它显示 二次运行时行为,因此任何超过 10 MB 的日志文件都无法使用.
我们已经通过解析 100 条消息的小块解决了性能问题以及其他一些问题,而且我知道 Delphi XML 例程不适合/过度杀伤这种用例 - 使用不同的 XML 库很可能停止性能问题。所以我不是在寻求解决问题的方法(尽管如果不使用不同的 XML 库就可以解决问题会很有趣)。
我的问题是:Delphi XML 例程和 MSXML 的二次运行时行为的原因是什么?我无法想象在 XML 加载/解析/验证中会导致这种情况的事情,除了真正“愚蠢”的事情,比如管理链表而不是树中的节点,但我可能忽略了一些东西,也许是 DOM 相关的。
【问题讨论】:
-
这并不是真正的 Delphi 问题。有问题的 XML 例程只是 Windows 标准
MSXML库的接口。 -
XML 不适合记录。您最好的选择是打开文件,找到最后一个标签(希望通过已知大小自动附加您的新节点,然后再次附加最后一个标签。否则您将失败。
-
@mj2008:按照您的描述添加新消息,日志文件仅在转换为 HTML 时作为一个整体加载,因此该问题不会影响主日志记录过程。尽管如此,如前所述,XML 在此处具有优势,例如XSLT 非常有用,工作流中使用的所有内容都“准备好 UTF”。
-
@teran:几个月前我已经完成了测量,明天我可能会在查看它们后编辑问题,但是在没有进一步 XML 修改的情况下,单个
LoadFromFile()调用非常快,而且只会变慢添加修改时(并且只有第一次修改很慢),所以它似乎使用延迟初始化/验证。 -
@schnaader 基于 DOM 的 API 总是很容易使用,但速度很慢。另一方面,基于 SAX 的 API 使用起来相当棘手,但速度很快。
标签: xml performance delphi