У нас есть достаточно большие строки XML, которые мы в настоящее время анализируем с помощью MSXML2
Я только что попытался использовать MSXML6, надеясь на улучшение скорости и ничего не получив!
В настоящее время мы создаем много документов DOM, и, я думаю, могут быть некоторые накладные расходы при постоянном взаимодействии с dll MSXML2/6
Кто-нибудь знает о лучшем/более быстром XML-компоненте для Delphi?
Если кто-то может предложить альтернативу, и это быстрее, мы бы хотели интегрировать его, но это было бы очень много, поэтому, надеюсь, структура не будет слишком отличаться от используемой MSXML
Мы используем Delphi 2010
Пол
Недавно у меня была аналогичная проблема, при которой использование синтаксического анализатора MSXML DOM оказалось слишком медленным для данной задачи. Мне пришлось разбирать довольно большие документы > 1 МБ, а потребление памяти анализатора DOM было непомерно высоким. Мое решение состояло в том, чтобы вообще не использовать парсер DOM, но идти с парсером MSXML SAX, управляемым событиями. Это оказалось намного быстрее. К сожалению, модель программирования совершенно другая, но в зависимости от задачи она может стоить того. Крейг Мерфи опубликовал замечательную статью о том, как использовать парсер MSXML SAX в delphi: SAX, Delphi и Ex Em El
некоторое время назад мне пришлось сериализовать record
в формате XML; для ex:
TTest = record
a : integer;
b : real;
end;
в
<Data> <a type="tkInteger">value</a> <b type="tkFloat">value</b> </Data>
Я использовал RTTI для рекурсивного перемещения по полям записи и хранения значений в XML. Я пробовал несколько XML-парсеров. Мне не нужна модель DOM для создания xml, но она нужна для ее загрузки.
XML содержит около 310k узлов (10-15MBytes);
результаты представлены в таблице ниже, имеется 6 столбцов со временем в секундах;
1 - время создания узлов и записи значений
2 - SaveToFile();
3 = 1 + 2
4 - LoadFromFile();
5 - перемещение по узлам и чтение значений
6 = 4 + 5
MSXML/Xerces/ADOM
- разные поставщики для TXMLDocument
(DOMVendor
) JanXML
не работает с unicode; Я исправил некоторые ошибки и сохранил XML, но загрузка вызывает AV (или переполнение стека, я не помню), manual
- означает вручную записывать XML с помощью TStringStream
.
Я использовал Delphi2010, Win7x32, Q8200 CPU/2.3GHz, 4Gb ОЗУ.
update: вы можете скачать исходный код для этого теста (записать сериализацию в XML с помощью RTTI) здесь http://blog.karelia.pro/teran/files/2012/03/XMLTest.zip Все парсеры (Omni, Native, Ян) включены (теперь количество узлов в XML составляет около 270 тыс.), Извините, что в коде нет комментариев.
Я знаю, что это старый вопрос, но людям может показаться интересным:
Я написал новую XML-библиотеку для Delphi (OXml): http://www.kluug.net/oxml.php
Он имеет прямую обработку XML (чтение + запись), парсер SAX, DOM и последовательный парсер DOM. Одним из преимуществ является то, что OXml поддерживает Delphi 6-Delphi XE5, FPC/Lazarus и С++ Builder на всех платформах (Win, MacOSX, Linux, iOS, Android).
OXml DOM основан на записи/указателях и обеспечивает лучшую производительность, чем любая другая библиотека XML:
Тест чтения возвращает время, в течение которого анализатор должен читать пользовательский XML DOM из файла (столбец "load" ) и записывать значения node в константу фиктивной функции (столбец "navigate" "). Файл закодирован в UTF-8 и размер составляет около 5,6 МБ.
Тест записи возвращает время, в течение которого парсеру необходимо создать DOM (столбец "create" ) и записать этот DOM в файл (столбец "save" ). Файл закодирован в UTF-8 и размер составляет около 11 МБ.
+ Плохая производительность записи OmniXML (оригинал) была результатом того, что OmniXML не использовал буферизацию для записи. Таким образом, запись в TFileStream была очень медленной. Я обновил OmniXML и добавил поддержку буферизации. Вы можете получить последний код OmniXML из SVN.
Когда-нибудь я написал очень простой набор тестов XML. Он обслуживает MSXML (D7 MSXML3?), Omni XML (бит старый) и Jedi XML (последняя стабильная версия).
Результаты теста для файла 1,52 МБ:
Время загрузки файла XML MSXML: 240,20 [мс]
XML node выбор MSXML: 1,09 [s]
Время загрузки файла XML OmniXML: 2,25 [s]
XML node выбор OmniXML: 1,22 [s]
Время загрузки файла XML JclSimpleXML: 2,11 [s]
и нарушение доступа для JclSimpleXML node вариантов: |
К сожалению, у меня на самом деле не так много времени, чтобы исправить выше AV, но sorces содержатся ниже...
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.