perl – 在BaseX中进行基准测试:如何设置

目前,我是一个研究小组的实习生,可以搜索大量的文本(语料库).不仅可以搜索文字字符串,更重要的是,还可以查找与给定输入类似的语法依赖结构,而无需精通任何编程语言或语料库注释样式.很明显,这个工具是针对语言学家的.

在项目开始时 – 在我参与项目之前 – 该工具仅限于相当小的语料库(最多900万字).目标是使大量文本也可搜索.我们正在谈论–5亿字.已经尝试过,理论上应该通过减少搜索空间来提高速度(见this paper),但尚未经过测试.此尝试的结果是一个新的文件结构.让我们称这个结构为B,与未处理的结构A相比.我们希望B在使用BaseX查询时提供更快的结果.

我的问题是:用Perl脚本测试和比较两种方法的最佳方法是什么?您可以在下面找到我当前在本地查询BaseX的脚本.它需要两个参数.存储不同文件的目录.这些文件分别存储XPath.那些XPath是我选择用于基准测试的那些.第二个参数是要返回的结果的限制.设置为零时,不设置限制.

因为数据集的某些部分非常庞大,我们将它们分成不同的,大小相同的文件,称为treebankparts.它们存储在< tb>中. treebankparts.lst内的标签.

#!/usr/bin/perl

use warnings;

$| = 1;    # flush every print

# Directory where XPaths are stored
my $directory = shift(@ARGV);

# Set limit. If set to zero all results will be returned
my $limit = shift(@ARGV);

# Create session, connect to BaseX
my $session = Session->new([INFORMATION WITHHELD]);

# List all files in directory
@xpathfiles = <$directory/*.txt>;

# Read lines of treebank parts into variable
open( my $tfh, "treebankparts.lst" ) or die "cannot open file treebankparts.lst";
chomp( my @tlines = <$tfh> );
close $tfh;

# Loop through all XPaths in $directory
foreach my $xpathfile (@xpathfiles) {
    open( my $xfh, $xpathfile ) or die "cannot open file $xpathfile";
    chomp( my @xlines = <$xfh> );
    close $xfh;

    print STDOUT "File = $xpathfile\n";

    # Loop through lines from XPath file (= XPath query)
    foreach my $xline (@xlines) {
        # Loop through the lines of treebank file
        foreach my $tline (@tlines) {
            my ($treebank) = $tline =~ /<tb>(.+)<\/tb>/;
            QuerySonar( $xline, $treebank );
        }
    }
}
$session->close();

sub QuerySonar {
    my ( $xpath, $db ) = @_;

    print STDOUT "Querying $db for $xpath\n";
    print STDOUT "Limit = $limit\n";
    my $x_limit;
    my $x_resultsofxp = 'declare variable $results := db:open("' . $db . '")/treebank/alpino_ds'
      . $xpath . ';';
    my $x_open       = '<results>';
    my $x_totalcount = '<total>{count($results)}</total>';
    my $x_loopinit   = '{for $node at $limitresults in $results';

    # Spaces are important!
    if ( $limit > 0 ) {
        $x_limit = ' where $limitresults <= ' . $limit . ' ';
    }
    # Comment needed to prevent `Incomplete FLWOR expression`
    else { $x_limit = '(: No limit set :)'; }

    my $x_sentenceinfo = 'let $sentid := ($node/ancestor::alpino_ds/@id)
        let $sentence := ($node/ancestor::alpino_ds/sentence)
        let $begin := ($node//@begin)
        let $idlist := ($node//@id)
        let $beginlist := (distinct-values($begin))';

    # Separate sentence info by tab
    my $x_loopexit = 'return <match>{data($sentid)}&#09;
        {string-join($idlist, "-")}&#09;
        {string-join($beginlist, "-")}&#09;
        {data($sentence)}</match>}';
    my $x_close = '</results>';

    # Concatenate all XQuery parts
    my $x_concatquery =
        $x_resultsofxp
      . $x_open
      . $x_totalcount
      . $x_loopinit
      . $x_limit
      . $x_sentenceinfo
      . $x_loopexit
      . $x_close;

    my $querysent = $session->query($x_concatquery);

    my $basexoutput = $querysent->execute();
    print $basexoutput. "\n\n";

    $querysent->close();
}

(请注意,这是一个精简版本,它可能无法正常工作.此代码段不使用结构B!)

会发生什么:遍历所有XPath文件,遍历XPath文件中的每一行,遍历所有treebankparts然后执行sub.然后子查询BaseX.这归结为向BaseX发送新的XQuery,并返回总命中和结果(可能受Perl脚本中的参数限制).所以我明白了,但现在的问题是:如何改进这个脚本,以便我可以从中获得一些基准测试结果.

首先,我开始在这个脚本中添加一个探查器.我猜这一点很明显.但是,我不确定应该如何开始将结构A与B进行比较.我会将两个查询(到不同的数据库)放在不同的脚本中,然后在两者上调用一个探查器,并多次运行这两个脚本并得到一个平均值并比较?或者我会在同一个脚本中同时运行两个数据库的每个查询吗?

考虑正在发生的缓存很重要.因此,我不完全确定这个巨大的数据库基准测试的构建是否合适.第一个脚本,然后是另一个.两者同时进行.两者之间的交替查询.等等.有这么多的可能性,但我想知道哪个会提供最好的结果.另外,我会重复这个过程几次.我会重复每个查询然后继续下一个,或者完成所有XPath文件,然后再重复整个过程吗?

(阅读基准标签的描述我相信这个 – 虽然精心制作 – 帖子适合SO.)

最佳答案 一种可能的改进:最小化将控制权从Perl传输到数据库的次数 – 就像您已将数据库连接数量最小化一样. (或者至少让自己测量转移控制的成本.)我怀疑如果你将循环转移到XQuery而不是在Perl中运行循环,你会得到明显更好的结果.

对数据库管理系统的单次调用要求它执行1000次搜索可能比每次请求单次搜索的DBMS的1000次调用快一些.第一个涉及两个上下文切换:一个从您的脚本或bash到dbms,一个返回;第二次涉及2000.上次我仔细测量这样的事情,每个上下文切换花费500毫秒;它安装得很快. (也就是说,这是很久以前,有一个不同的数据库.但是,令人惊讶的是[并且发人深省]学习而不是我试图比较的两个查询公式之间的差异与运行测试循环之间的差异相形见绌.脚本或dbms内部.)

第二个建议:根据您的说法,数据库的大小和结果集似乎可能确保运行之间的缓存不会对结果产生很大影响.但这似乎是一个可测试的断言,值得测试.因此,设置您的A和B脚本,然后进行试运行:对于1 2 3 4 5中的runco​​unt;做perl A.pl; perl B.pl;完成产生的结果可与1 2 3 4 5中的runco​​unt相媲美;做perl A.pl;完成; for runco​​unt in 1 2 3 4 5;做perl B.pl;做了什么?如果它们具有可比性,那么您有理由相信如果单独或交替运行A和B并不重要.如果它们不具有可比性,那么你知道它确实重要,这将是非常有价值的信息.在其他条件相同的情况下,我希望在继续执行下一个查询之前,在多次运行一个查询时会产生较低的缓存次数,如果只运行一次查询,则缓存未命中会产生更高的时间.可能值得一试.

本着同样的精神,我建议您使用Perl脚本中的循环和XQuery查询中的循环来运行测试.

第三个建议:在实践中,语料库查询界面的查询将涉及几个阶段,每个阶段都有可测量的时间:从用户的浏览器(假设它是Web界面)向服务器传输查询,将请求转换为表单适合传输到后端dbms(此处为BaseX),上下文切换到BaseX,在BaseX内处理,上下文切换回,由Web服务器处理,传输给用户.至少对每个步骤所涉及的时间进行粗略估计是有用的,或者至少是所有东西 – 但是BaseX所花费的时间.

因此,如果是我运行测试,我想我还准备了一组空的XQuery测试,就像我一样

2 + 3

要不就

42

将BaseX时间推至尽可能接近零;用户启动查询和显示响应之间的测量时间是每个查询的开销. (有趣的问题:是否应该使用许多不同的平凡表达式来防止缓存结果,或者应该反复使用相同的表达式,以鼓励缓存结果?我们如何尝试确保BaseX将缓存结果,但是Web服务器不会?…)

最后的建议:请记住,其他需要进行基准测试的人通常会遇到与您相同的问题.这意味着您可以重新形成“我应该做X还是Y?”形式的每个问题.形式为“X和Y之间的差异对基准测试的结果有什么可衡量的影响?”运行一些测试以尝试测量该效果,然后编写它们. (我总是发现如果我在制定问题之后但在测量差异之前强迫自己做出预测会使它更有趣.)

点赞