Delphiで利用可能な最速のXMLパーサーは何ですか?
現在、MSXML2を使用して解析しているかなり大きなXML文字列があります。
速度の向上を期待してMSXML6を使用したところ、何も得られませんでした。
現在、多くのDOMドキュメントを作成していますが、MSXML2/6 dllと常に対話する際にオーバーヘッドが発生する可能性があります。
Delphiのより良い/より速いXMLコンポーネントを知っている人はいますか?
誰かが代替案を提案でき、それがより速い場合は、それを統合することを検討しますが、それは多くの作業になるため、MSXMLで使用されているものと構造があまり変わらないことを願っています
Delphi 2010を使用しています
ポール
最近、同様の問題がありましたが、MSXML DOMパーサーを使用すると、指定されたタスクには遅すぎることが判明しました。 1 MBを超えるかなり大きなドキュメントを解析する必要があり、DOMパーサーのメモリ消費量は非常に大きかった。私の解決策は、DOMパーサーをまったく使用せず、イベント駆動型のMSXML SAXパーサーを使用することでした。これははるかに高速であることが証明されました。残念ながら、プログラミングモデルは完全に異なりますが、タスクによって異なりますが、価値があるかもしれません。 Craig Murphyが、delphiでMSXML SAXパーサーを使用する方法に関する優れた記事を公開しました。 SAX、DelphiおよびEx Em El
少し前に、record
をXML形式にシリアル化する必要がありました。例:
TTest = record
a : integer;
b : real;
end;
に
<データ> <a type="tkInteger">value</a> <b type = "tkFloat" >value</ b> </ Data>
RTTIを使用して、レコードフィールドを再帰的に移動し、値をXMLに格納しました。私はいくつかのXMLパーサーを試しました。私はxmlを作成するためにDOMモデルを必要としませんでしたが、それをロードするために必要でした。
XMLには約310kノード(10-15Mバイト)が含まれていました。下の表に示されている結果では、秒単位の時間が6列あります。
1-ノードを作成して値を書き込む時間
2-SaveToFile();
3 = 1 + 2
4-LoadFromFile();
5-ノード間を移動して値を読み取ります
6 = 4 + 5
MSXML/Xerces/ADOM
-TXMLDocument
(DOMVendor
)の異なるベンダーですJanXML
はユニコードでは機能しません。いくつかのエラーを修正してXMLを保存しましたが、ロードするとAV(またはスタックオーバーフロー、覚えていません)が発生します。manual
-TStringStream
を使用して手動でXMLを記述することを意味します。
Delphi2010、Win7x32、Q8200 CPU/2.3GHz、4GbのRAMを使用しました。
更新:このテストのソースコード(RTTIを使用したXMLへのシリアル化の記録)は、こちらからダウンロードできます http://blog.karelia.pro/teran/files/2012/03/XMLTest.Zip すべてのパーサー( Omni、Native、Jan)が含まれています(現在、XMLのノード数は約270kです)。コードにコメントはありません。
私はそれが古い質問であることを知っていますが、人々はそれを興味深いと思うかもしれません:
Delphi(OXml)用の新しいXMLライブラリを作成しました: http://www.kluug.net/oxml.php
直接XML処理(読み取り+書き込み)、SAXパーサー、DOM、および順次DOMパーサーを備えています。利点の1つは、OXmlがすべてのプラットフォーム(Win、MacOSX、Linux、iOS、Android)でDelphi 6-Delphi XE5、FPC/Lazarus、およびC++ Builderをサポートすることです。
OXml DOMはレコード/ポインターベースであり、他のどのXMLライブラリよりも優れたパフォーマンスを提供します。
読み取りテストは、パーサーがファイル(列「load」)からカスタムXML DOMを読み取り、ノード値を定数に書き込む必要がある時間を返します。ダミー関数(列「ナビゲート」)。ファイルはUTF-8でエンコードされており、サイズは約5.6 MBです。
書き込みテストは、パーサーがDOMを作成する必要がある時間(列「作成」)を返し、このDOMをファイルに書き込む(列「保存」) 。ファイルはUTF-8でエンコードされており、サイズは約11 MBです。
+ OmniXML(オリジナル)の書き込みパフォーマンスが低いのは、OmniXMLが書き込みにバッファリングを使用しなかったためです。したがって、TFileStreamへの書き込みは非常に低速でした。 OmniXMLを更新し、バッファリングのサポートを追加しました。 SVNから最新のOmniXMLコードを取得できます。
いつか私は非常にシンプルなXMLテストスイートを書きました。 MSXML(D7 MSXML3?)、Omni XML(少し古い)、Jedi XML(最新の安定版)を提供します。
1,52 MBファイルのテスト結果:
XMLファイルの読み込み時間MSXML:240,20 [ms]
XMLノードの選択MSXML:1,09 [s]
XMLファイルの読み込み時間OmniXML:2,25 [s]
XMLノードの選択OmniXML:1,22 [s]
XMLファイルの読み込み時間JclSimpleXML:2,11 [s]
jclSimpleXMLノード選択のアクセス違反:|
残念ながら私は実際にはAVの上を修正する時間があまりありませんが、魔術は下に含まれています...
fmuMain.pas
program XmlEngines;
uses
FastMM4,
Forms,
fmuMain in 'fmuMain.pas' {fmMain},
uXmlEngines in 'uXmlEngines.pas',
ifcXmlEngine in 'ifcXmlEngine.pas';
{$R *.res}
begin
Application.Initialize;
Application.Title := 'XML Engine Tester';
Application.CreateForm(TfmMain, fmMain);
Application.Run;
end.
fmuMain.pas
unit fmuMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, xmldom, XMLIntf, msxmldom, XMLDoc,
//
ifcXmlEngine, StdCtrls;
type
TfmMain = class(TForm)
mmoDebug: TMemo;
dlgOpen: TOpenDialog;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure mmoDebugClick(Sender: TObject);
private
fXmlEngines: TInterfaceList;
function Get_Engine(const aIx: Integer): IXmlEngine;
protected
property XmlEngine[const aIx: Integer]: IXmlEngine read Get_Engine;
procedure Debug(const aInfo: string); // inline
public
procedure RegisterXmlEngine(const aEngine: IXmlEngine);
end;
var
fmMain: TfmMain;
implementation
{$R *.dfm}
uses
uXmlEngines, TZTools;
{ TForm1 }
function TfmMain.Get_Engine(const aIx: Integer): IXmlEngine;
begin
Result:= nil;
Supports(fXmlEngines[aIx], IXmlEngine, Result)
end;
procedure TfmMain.RegisterXmlEngine(const aEngine: IXmlEngine);
var
Ix: Integer;
begin
if aEngine = nil then
Exit; // WARRNING: program flow disorder
for Ix:= 0 to Pred(fXmlEngines.Count) do
if XmlEngine[Ix] = aEngine then
Exit; // WARRNING: program flow disorder
fXmlEngines.Add(aEngine)
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
fXmlEngines:= TInterfaceList.Create();
dlgOpen.InitialDir:= ExtractFileDir(ParamStr(0));
RegisterXmlEngine(TMsxmlEngine.Create(Self));
RegisterXmlEngine(TOmniXmlEngine.Create());
RegisterXmlEngine(TJediXmlEngine.Create());
end;
procedure TfmMain.mmoDebugClick(Sender: TObject);
procedure TestEngines(const aFilename: TFileName);
procedure TestEngine(const aEngine: IXmlEngine);
var
PerfCheck: TPerfCheck;
Ix: Integer;
begin
PerfCheck := TPerfCheck.Create();
try
PerfCheck.Init(True);
PerfCheck.Start();
aEngine.Load(aFilename);
PerfCheck.Pause();
Debug(Format(
'XML file loading time %s: %s',
[aEngine.Get_ID(), PerfCheck.TimeStr()]));
if aEngine.Get_ValidNode() then
begin
PerfCheck.Start();
for Ix:= 0 to 999999 do
if aEngine.Get_ChildsCount() > 0 then
begin
aEngine.SelectChild(Ix mod aEngine.Get_ChildsCount());
end
else
aEngine.SelectRootNode();
PerfCheck.Pause();
Debug(Format(
'XML nodes selections %s: %s',
[aEngine.Get_ID(), PerfCheck.TimeStr()]));
end
finally
PerfCheck.Free();
end
end;
var
Ix: Integer;
begin
Debug(aFilename);
for Ix:= 0 to Pred(fXmlEngines.Count) do
TestEngine(XmlEngine[Ix])
end;
var
CursorBckp: TCursor;
begin
if dlgOpen.Execute() then
begin
CursorBckp:= Cursor;
Self.Cursor:= crHourGlass;
mmoDebug.Cursor:= crHourGlass;
try
TestEngines(dlgOpen.FileName)
finally
Self.Cursor:= CursorBckp;
mmoDebug.Cursor:= CursorBckp;
end
end
end;
procedure TfmMain.Debug(const aInfo: string);
begin
mmoDebug.Lines.Add(aInfo)
end;
procedure TfmMain.FormDestroy(Sender: TObject);
begin
fXmlEngines.Free()
end;
end.
ifcXmlEngine.pas
unit ifcXmlEngine;
interface
uses
SysUtils;
type
TFileName = SysUtils.TFileName;
IXmlEngine = interface
['{AF77333B-9873-4FDE-A3B1-260C7A4D3357}']
procedure Load(const aFilename: TFileName);
procedure SelectRootNode();
procedure SelectChild(const aIndex: Integer);
procedure SelectParent();
//
function Get_ID(): string;
function Get_ValidNode(): Boolean;
function Get_ChildsCount(): Integer;
function Get_HaveParent(): Boolean;
//function Get_NodeName(): Boolean;
end;
implementation
end.
uXmlEngines.pas
unit uXmlEngines;
interface
uses
Classes,
//
XMLDoc, XMLIntf, OmniXml, JclSimpleXml,
//
ifcXmlEngine;
type
TMsxmlEngine = class(TInterfacedObject, IXmlEngine)
private
fXmlDoc: XMLDoc.TXMLDocument;
fNode: XMLIntf.IXMLNode;
protected
public
constructor Create(const aOwner: TComponent);
destructor Destroy; override;
procedure Load(const aFilename: TFileName);
procedure SelectRootNode();
procedure SelectChild(const aIndex: Integer);
procedure SelectParent();
//
function Get_ID(): string;
function Get_ValidNode(): Boolean;
function Get_ChildsCount(): Integer;
function Get_HaveParent(): Boolean;
//function Get_NodeName(): Boolean;
end;
TOmniXmlEngine = class(TInterfacedObject, IXmlEngine)
private
fXmlDoc: OmniXml.IXmlDocument;
fNode: OmniXml.IXMLNode;
protected
public
constructor Create;
destructor Destroy; override;
procedure Load(const aFilename: TFileName);
procedure SelectRootNode();
procedure SelectChild(const aIndex: Integer);
procedure SelectParent();
//
function Get_ID(): string;
function Get_ValidNode(): Boolean;
function Get_ChildsCount(): Integer;
function Get_HaveParent(): Boolean;
//function Get_NodeName(): Boolean;
end;
TJediXmlEngine = class(TInterfacedObject, IXmlEngine)
private
fXmlDoc: TJclSimpleXML;
fNode: TJclSimpleXMLElem;
protected
public
constructor Create();
destructor Destroy(); override;
procedure Load(const aFilename: TFileName);
procedure SelectRootNode();
procedure SelectChild(const aIndex: Integer);
procedure SelectParent();
//
function Get_ID(): string;
function Get_ValidNode(): Boolean;
function Get_ChildsCount(): Integer;
function Get_HaveParent(): Boolean;
//function Get_NodeName(): Boolean;
end;
implementation
uses
SysUtils;
{ TMsxmlEngine }
constructor TMsxmlEngine.Create(const aOwner: TComponent);
begin
if aOwner = nil then
raise Exception.Create('TMsxmlEngine.Create() -> invalid owner');
inherited Create();
fXmlDoc:= XmlDoc.TXmlDocument.Create(aOwner);
fXmlDoc.ParseOptions:= [poPreserveWhiteSpace]
end;
destructor TMsxmlEngine.Destroy;
begin
fXmlDoc.Free();
inherited Destroy()
end;
function TMsxmlEngine.Get_ChildsCount: Integer;
begin
Result:= fNode.ChildNodes.Count
end;
function TMsxmlEngine.Get_HaveParent: Boolean;
begin
Result:= fNode.ParentNode <> nil
end;
function TMsxmlEngine.Get_ID: string;
begin
Result:= 'MSXML'
end;
//function TMsxmlEngine.Get_NodeName: Boolean;
//begin
// Result:= fNode.Text
//end;
function TMsxmlEngine.Get_ValidNode: Boolean;
begin
Result:= fNode <> nil
end;
procedure TMsxmlEngine.Load(const aFilename: TFileName);
begin
fXmlDoc.LoadFromFile(aFilename);
SelectRootNode()
end;
procedure TMsxmlEngine.SelectChild(const aIndex: Integer);
begin
fNode:= fNode.ChildNodes.Get(aIndex)
end;
procedure TMsxmlEngine.SelectParent;
begin
fNode:= fNode.ParentNode
end;
procedure TMsxmlEngine.SelectRootNode;
begin
fNode:= fXmlDoc.DocumentElement
end;
{ TOmniXmlEngine }
constructor TOmniXmlEngine.Create;
begin
inherited Create();
fXmlDoc:= OmniXml.TXMLDocument.Create();
fXmlDoc.PreserveWhiteSpace:= true
end;
destructor TOmniXmlEngine.Destroy;
begin
fXmlDoc:= nil;
inherited Destroy()
end;
function TOmniXmlEngine.Get_ChildsCount: Integer;
begin
Result:= fNode.ChildNodes.Length
end;
function TOmniXmlEngine.Get_HaveParent: Boolean;
begin
Result:= fNode.ParentNode <> nil
end;
function TOmniXmlEngine.Get_ID: string;
begin
Result:= 'OmniXML'
end;
//function TOmniXmlEngine.Get_NodeName: Boolean;
//begin
// Result:= fNode.NodeName
//end;
function TOmniXmlEngine.Get_ValidNode: Boolean;
begin
Result:= fNode <> nil
end;
procedure TOmniXmlEngine.Load(const aFilename: TFileName);
begin
fXmlDoc.Load(aFilename);
SelectRootNode()
end;
procedure TOmniXmlEngine.SelectChild(const aIndex: Integer);
begin
fNode:= fNode.ChildNodes.Item[aIndex]
end;
procedure TOmniXmlEngine.SelectParent;
begin
fNode:= fNode.ParentNode
end;
procedure TOmniXmlEngine.SelectRootNode;
begin
fNode:= fXmlDoc.DocumentElement
end;
{ TJediXmlEngine }
constructor TJediXmlEngine.Create;
begin
inherited Create();
fXmlDoc:= TJclSimpleXML.Create();
end;
destructor TJediXmlEngine.Destroy;
begin
fXmlDoc.Free();
inherited Destroy()
end;
function TJediXmlEngine.Get_ChildsCount: Integer;
begin
Result:= fNode.ChildsCount
end;
function TJediXmlEngine.Get_HaveParent: Boolean;
begin
Result:= fNode.Parent <> nil
end;
function TJediXmlEngine.Get_ID: string;
begin
Result:= 'JclSimpleXML';
end;
//function TJediXmlEngine.Get_NodeName: Boolean;
//begin
// Result:= fNode.Name
//end;
function TJediXmlEngine.Get_ValidNode: Boolean;
begin
Result:= fNode <> nil
end;
procedure TJediXmlEngine.Load(const aFilename: TFileName);
begin
fXmlDoc.LoadFromFile(aFilename);
SelectRootNode()
end;
procedure TJediXmlEngine.SelectChild(const aIndex: Integer);
begin
fNode:= fNode.Items[aIndex]
end;
procedure TJediXmlEngine.SelectParent;
begin
fNode:= fNode.Parent
end;
procedure TJediXmlEngine.SelectRootNode;
begin
fNode:= fXmlDoc.Root
end;
end.
MPL v1.1、GPL v3.0、またはLGPL v3.0ライセンス。
ダウンロードするには、 Delphi-Praxis (ドイツ語)の優れたDelphiサイトに登録する必要があります。
非常に印象的なパフォーマンスがあり、ディストリビューションにはそれを示すデモが含まれています。 Delphi 2007、Delphi 2010、Delphi XEで使用できました。