我们有一个成熟的代码体,可以将文件中的数据加载到数据库中。 有几种文件格式;它们都是固定宽度的字段。
部分代码使用Perl unpack()
函数将输入数据中的字段读入包变量。
然后,业务逻辑能够以“人类可读”的方式引用这些字段。
在读取文件之前,文件读取代码是从格式描述生成的。
在草图形式中,生成的代码如下所示:
while ( <> ) {
# Start of generated code.
# Here we unpack 2 fields, real code does around 200.
( $FIELDS::transaction_date, $FIELDS::customer_id ) = unpack q{A8 A20};
# Some fields have leading space removed
# Generated code has one line like this per affected field.
$FIELDS::customer_id =~ s/^\s+//;
# End of generated code.
# Then we apply business logic to the data ...
if ( $FIELDS::transaction_date eq $today ) {
push @fields, q{something or other};
}
# Write to standard format for bulk load to the database.
print $fh join( '|', @fields ) . q{\n} or die;
}
对代码进行分析后发现,大约35%的时间花费在解包和前导空间条带上。 剩余时间用于验证和转换数据,以及写入输出文件。
业务逻辑似乎没有一部分占用运行时间的1-2%。
问题是 - 我们能否以某种方式从拆包和空间剥离中获得更多的速度?最好不必重构所有引用FIELDS包变量的代码。
修改
如果它有所作为
$ perl -v
This is perl, v5.8.0 built for PA-RISC1.1
答案 0 :(得分:7)
我实际上一遍又一遍地处理这个问题。 Unpack is better than substr。
就剥离空间而言,你几乎搞砸了。正则表达式黑客攻击是“官方”的方式。你可以通过改进你的unpack语句来获得一些效率(假设没有数据超过4位数,为什么要解压缩字段的完整12位?),但除此之外,解析只是一个p.i.t.a。
祝你的平面数据好运。令人沮丧的传统垃圾,我多么讨厌它。
答案 1 :(得分:3)
你确定你是这个任务的处理器吗?计算很简单,可以怀疑整个过程可能是I / O绑定的。在这种情况下,优化以便更快地解压缩不会给你带来太多时间。
如果您实际上是处理器绑定的,那么所描述的问题似乎几乎可以并行化,但当然,恶魔就在您的业务计算的细节中。
答案 2 :(得分:1)
是。使用substr
进行提取可能是最快的方法。那就是:
$FIELDS::transaction_date = substr $_, 0, 8;
$FIELDS::customer_id = substr $_, 8, 20;
可能会更快。现在,如果我手写这段代码,我就不会放弃unpack
,但是如果你要生成代码,你也可以试一试。
另见Is Perl’s unpack() ever faster than substr()?
的答案至于剥离前导空格,s/^\s+//
可能是最快的方法。
更新:如果没有能够运行基准测试,很难说清楚。但是,怎么样:
my $x = substr $_, 0, 8;
表示不需要修剪和
的字段my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/;
是否需要修剪?
答案 3 :(得分:1)
这也可能适用于XS - 因此您可以使用C函数来更改数据。我可以想象这比其他任何东西都快得多,因为你可以手动控制何时真正复制数据 由于您对C编译器具有依赖性,因此构建过程将变得更加困难,并且需要额外的集成步骤。
答案 4 :(得分:1)
简单地让它并行。它是微不足道的,甚至在任何远程现代机器上都会更快。
答案 5 :(得分:0)
我们代码的基于substr的版本的基准测试表明它可能比我们现有的unpack快约50%。 比较实际应用中的代码,substr版本使运行时间缩短了16%。 这与我们希望的基于该问题中提到的基准和分析的结果非常接近。
此优化可能对我们有用。但是,我们正在迁移到新的操作系统,所以我们将等待,然后再继续查看代码在那里的执行情况。我们添加了一项测试,以密切关注比较基准。
我们现在的成语是:
$FIELDS::transaction_date = substr( $_, 0, 8 ) || '';
$FIELDS::transaction_date =~ s/\s+\z//;
$FIELDS::customer_id = substr( $_, 8, 20 ) || '';
$FIELDS::customer_id =~ s/\s+\z//;
随后像以前一样选择性地引导空间移除。
感谢目前为止的所有答案。 我会接受思南的,因为它对我们有用,尽管看起来“只是完全错误”。