Perl XML::LibXML 由于元素内的斜率,处理速度非常慢
Perl XML::LibXML very slow processing due to slope within elements
我写了一段代码将 xml 文件导入数据库,但遇到了巨大的性能问题。 xml 文件有 150 Mb,包含 170,000 个元素 (ART) 和相应的子元素 (REF_DATA, ...)。我能够找出问题的原因,但我不知道如何解决它。
每个元素ART都有子元素(见图)。如果我在 ART 中有几个子元素 ARTPRI,这些子元素与它们的子元素 PTYP 不同,就会出现问题。我想提取每个数据 ARTPRI/VDAT 和 ARTPRI/PRICE 并导入变量 $v_dat_pexf
、$v_dat_ppub
、$v_dat_zurr
等
下面是我的代码的最小 示例。此代码读取一个元素 ART 需要 30 秒。当我删除部件 (START node2 / END node2) 时,xml-文件的处理速度非常快 (< 1s/ART)。
有谁知道为什么这部分代码会减慢进程以及如何处理?
感谢您的帮助。
这是 xml 文件:
这是代码:
my $xml_article = "oddb_article.xml";
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs(sr => 'http://whatever');
my $doc = XML::LibXML->load_xml(location => $xml_article);
my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);
my $i = 0;
foreach my $node1 ( @node1_art ) {
$i++;
my $ref_data = $xpc->findvalue('./sr:REF_DATA',$node1);
my @node1_art_artpri = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);
my $v_dat_pexf;
# -- search through each ARTPRI within ART
# (This is the part which slows down processing)
# -------- START node2 -----------------------
foreach my $node2 ( @node1_art_artpri ) {
my $ctrl1 = $xpc->findvalue('./sr:PTYP',$node2);
if ( $ctrl1 eq 'PEXF' ) {
$v_dat_pexf = $xpc->findvalue('./sr:VDAT',$node2);
}
# -------- END node2 -----------------------
}
print "Row $i\n";
}
这里是复制粘贴版本,包含 3 个 ART 元素:
<?xml version="1.0" encoding="utf-8"?>
<ARTICLE xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://whatever" CREATION_DATETIME="2015-11-17T05:44:14+0100" PROD_DATE="2015-11-17T05:44:14+0100" VALID_DATE="2015-11-17T05:44:14+0100">
<ART DT="" SHA256="2744a856e9bdf226e68bd555f0695b37f6477c55fca3d9eec36a0740fe8146c2">
<REF_DATA>1</REF_DATA>
<PHAR>0000000</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>Epimineral Paste</DSCRD>
<DSCRF>Epimineral pâte</DSCRF>
<SORTD>EPIMINERAL PASTE</SORTD>
<SORTF>EPIMINERAL PâTE</SORTF>
<ARTCOMP>
<COMPNO>7601003300741</COMPNO>
</ARTCOMP>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
<ARTINS>
<NINCD>10</NINCD>
</ARTINS>
</ART>
<ART DT="" SHA256="ac0eb1ad7c81f5476541ead533c48690a2c9cf3b1dd0ba8ae295145b6bcb1b40">
<REF_DATA>0</REF_DATA>
<PHAR>0021976</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRD>
<DSCRF>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRF>
<SORTD>DIOPARINE GTT OPHT 7500 E 5 ML</SORTD>
<SORTF>DIOPARINE GTT OPHT 7500 E 5 ML</SORTF>
<ARTCOMP/>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
</ART>
<ART DT="" SHA256="ecc62600e79183822abddb3af0d2a1f9dfb9f2c343c51a2cf135a45354ba7de1">
<REF_DATA>0</REF_DATA>
<PHAR>0027447</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>ARTHROSENEX Salbe 100 g</DSCRD>
<DSCRF>ARTHROSENEX Salbe 100 g</DSCRF>
<SORTD>ARTHROSENEX SALBE 100 G</SORTD>
<SORTF>ARTHROSENEX SALBE 100 G</SORTF>
<ARTCOMP/>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
</ART>
<RESULT>
<OK_ERROR>OK</OK_ERROR>
<NBR_RECORD>170673</NBR_RECORD>
<ERROR_CODE/>
<MESSAGE/>
</RESULT>
</ARTICLE>
我认为您的问题的根源在于这两行的组合:
my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);
my @node1_art_artpri = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);
因为看起来您在整个文档中找到每个 ART
节点(扫描整个文档),然后对于每个这样的节点 - 您正在再次扫描整个文档 找到每个 ARTPRI
节点。
您随后迭代的对象:
foreach my $node2 ( @node1_art_artpri ) {
...但只捕获您找到的最后一个值。
这看起来可能是一个逻辑错误(很难确定)。
但是您是否尝试过在每次循环迭代时打印,看看它打印了多少次?
因为看起来的目的是只去几次。您的示例 ART
下面只有两个 ARTPRI
节点,但它会做得更多。
这应该可以通过以下方式解决:
my @node1_art_artpri = $xpc->findnodes("./sr:ARTPRI", $node1);
(或类似的东西 - 重要的部分是将上下文设置为节点,而不是文档)。
或者可能使用 xpath
而不是您的逻辑条件:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( sr => 'http://whatever' );
my $doc = XML::LibXML->load_xml( location => 'test4.xml' );
foreach my $node1 ( $xpc->findnodes( "/sr:ARTICLE/sr:ART", $doc ) ) {
my $ref_data = $xpc->findvalue( './sr:REF_DATA', $node1 );
my $v_dat_pexf =
$xpc->findvalue( './sr:ARTPRI/sr:PTYP[text()="PEXF"]/../sr:VDAT', $node1 );
print "$ref_data => $v_dat_pexf\n";
}
但实际上对于这类任务,我可能会考虑 XML::Twig
,它可以让您通过 twig_handlers
做事 - 从而减少内存占用。
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
sub extract_pexf {
my ( $twig, $ART ) = @_;
#or stuff it in an array, whatever.
print $ART -> first_child_text('REF_DATA'), " => ";
print $ART -> get_xpath('.//ARTPRI/PTYP[string()="PEXF"]/../VDAT',0) -> text,"\n";
$twig -> purge; #clear processed data from memory.
}
XML::Twig -> new ( twig_handlers => { 'ART' => \&extract_pexf } ) -> parsefile ( 'your_xml' );
我写了一段代码将 xml 文件导入数据库,但遇到了巨大的性能问题。 xml 文件有 150 Mb,包含 170,000 个元素 (ART) 和相应的子元素 (REF_DATA, ...)。我能够找出问题的原因,但我不知道如何解决它。
每个元素ART都有子元素(见图)。如果我在 ART 中有几个子元素 ARTPRI,这些子元素与它们的子元素 PTYP 不同,就会出现问题。我想提取每个数据 ARTPRI/VDAT 和 ARTPRI/PRICE 并导入变量 $v_dat_pexf
、$v_dat_ppub
、$v_dat_zurr
等
下面是我的代码的最小 示例。此代码读取一个元素 ART 需要 30 秒。当我删除部件 (START node2 / END node2) 时,xml-文件的处理速度非常快 (< 1s/ART)。
有谁知道为什么这部分代码会减慢进程以及如何处理? 感谢您的帮助。
这是 xml 文件:
这是代码:
my $xml_article = "oddb_article.xml";
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs(sr => 'http://whatever');
my $doc = XML::LibXML->load_xml(location => $xml_article);
my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);
my $i = 0;
foreach my $node1 ( @node1_art ) {
$i++;
my $ref_data = $xpc->findvalue('./sr:REF_DATA',$node1);
my @node1_art_artpri = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);
my $v_dat_pexf;
# -- search through each ARTPRI within ART
# (This is the part which slows down processing)
# -------- START node2 -----------------------
foreach my $node2 ( @node1_art_artpri ) {
my $ctrl1 = $xpc->findvalue('./sr:PTYP',$node2);
if ( $ctrl1 eq 'PEXF' ) {
$v_dat_pexf = $xpc->findvalue('./sr:VDAT',$node2);
}
# -------- END node2 -----------------------
}
print "Row $i\n";
}
这里是复制粘贴版本,包含 3 个 ART 元素:
<?xml version="1.0" encoding="utf-8"?>
<ARTICLE xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://whatever" CREATION_DATETIME="2015-11-17T05:44:14+0100" PROD_DATE="2015-11-17T05:44:14+0100" VALID_DATE="2015-11-17T05:44:14+0100">
<ART DT="" SHA256="2744a856e9bdf226e68bd555f0695b37f6477c55fca3d9eec36a0740fe8146c2">
<REF_DATA>1</REF_DATA>
<PHAR>0000000</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>Epimineral Paste</DSCRD>
<DSCRF>Epimineral pâte</DSCRF>
<SORTD>EPIMINERAL PASTE</SORTD>
<SORTF>EPIMINERAL PâTE</SORTF>
<ARTCOMP>
<COMPNO>7601003300741</COMPNO>
</ARTCOMP>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
<ARTINS>
<NINCD>10</NINCD>
</ARTINS>
</ART>
<ART DT="" SHA256="ac0eb1ad7c81f5476541ead533c48690a2c9cf3b1dd0ba8ae295145b6bcb1b40">
<REF_DATA>0</REF_DATA>
<PHAR>0021976</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRD>
<DSCRF>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRF>
<SORTD>DIOPARINE GTT OPHT 7500 E 5 ML</SORTD>
<SORTF>DIOPARINE GTT OPHT 7500 E 5 ML</SORTF>
<ARTCOMP/>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
</ART>
<ART DT="" SHA256="ecc62600e79183822abddb3af0d2a1f9dfb9f2c343c51a2cf135a45354ba7de1">
<REF_DATA>0</REF_DATA>
<PHAR>0027447</PHAR>
<SALECD>I</SALECD>
<CDBG>N</CDBG>
<BG>N</BG>
<DSCRD>ARTHROSENEX Salbe 100 g</DSCRD>
<DSCRF>ARTHROSENEX Salbe 100 g</DSCRF>
<SORTD>ARTHROSENEX SALBE 100 G</SORTD>
<SORTF>ARTHROSENEX SALBE 100 G</SORTF>
<ARTCOMP/>
<ARTBAR>
<CDTYP>E13</CDTYP>
<BC>0</BC>
<BCSTAT>A</BCSTAT>
</ARTBAR>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PEXF</PTYP>
<PRICE>305.83</PRICE>
</ARTPRI>
<ARTPRI>
<VDAT>01.10.2015</VDAT>
<PTYP>PPUB</PTYP>
<PRICE>367.5</PRICE>
</ARTPRI>
</ART>
<RESULT>
<OK_ERROR>OK</OK_ERROR>
<NBR_RECORD>170673</NBR_RECORD>
<ERROR_CODE/>
<MESSAGE/>
</RESULT>
</ARTICLE>
我认为您的问题的根源在于这两行的组合:
my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);
my @node1_art_artpri = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);
因为看起来您在整个文档中找到每个 ART
节点(扫描整个文档),然后对于每个这样的节点 - 您正在再次扫描整个文档 找到每个 ARTPRI
节点。
您随后迭代的对象:
foreach my $node2 ( @node1_art_artpri ) {
...但只捕获您找到的最后一个值。
这看起来可能是一个逻辑错误(很难确定)。
但是您是否尝试过在每次循环迭代时打印,看看它打印了多少次?
因为看起来的目的是只去几次。您的示例 ART
下面只有两个 ARTPRI
节点,但它会做得更多。
这应该可以通过以下方式解决:
my @node1_art_artpri = $xpc->findnodes("./sr:ARTPRI", $node1);
(或类似的东西 - 重要的部分是将上下文设置为节点,而不是文档)。
或者可能使用 xpath
而不是您的逻辑条件:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( sr => 'http://whatever' );
my $doc = XML::LibXML->load_xml( location => 'test4.xml' );
foreach my $node1 ( $xpc->findnodes( "/sr:ARTICLE/sr:ART", $doc ) ) {
my $ref_data = $xpc->findvalue( './sr:REF_DATA', $node1 );
my $v_dat_pexf =
$xpc->findvalue( './sr:ARTPRI/sr:PTYP[text()="PEXF"]/../sr:VDAT', $node1 );
print "$ref_data => $v_dat_pexf\n";
}
但实际上对于这类任务,我可能会考虑 XML::Twig
,它可以让您通过 twig_handlers
做事 - 从而减少内存占用。
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
sub extract_pexf {
my ( $twig, $ART ) = @_;
#or stuff it in an array, whatever.
print $ART -> first_child_text('REF_DATA'), " => ";
print $ART -> get_xpath('.//ARTPRI/PTYP[string()="PEXF"]/../VDAT',0) -> text,"\n";
$twig -> purge; #clear processed data from memory.
}
XML::Twig -> new ( twig_handlers => { 'ART' => \&extract_pexf } ) -> parsefile ( 'your_xml' );