5

我们有一套成熟的代码,可以将文件中的数据加载到数据库中。有几种文件格式;它们都是固定宽度的字段。

部分代码使用 Perlunpack()函数将输入数据中的字段读取到包变量中。然后,业务逻辑能够以“人类可读”的方式引用这些字段。

文件读取代码在读取文件之前由格式描述生成一次。

在草图形式中,生成的代码如下所示:

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
4

6 回答 6

7

实际上,我已经一遍又一遍地处理这个问题。Unpack 比 substr 更好

就剥离空间而言,您几乎被搞砸了。那个正则表达式黑客是“官方”的方式来做到这一点。您可以通过改进您的解包语句来获得一些效率(假设没有数据长于 4 位,为什么解包字段的全部 12 位?),但除此之外,解析只是一个皮塔

祝你的平面数据好运。他妈的遗留垃圾,我多么讨厌它。

于 2009-10-19T14:13:29.970 回答
3

你确定你是这个任务的处理器吗?计算很简单,足以怀疑整个过程可能是 I/O 绑定的。在这种情况下,优化更快的解包不会为您赢得太多时间。

如果您实际上受处理器限制,那么所描述的问题似乎非常可并行化,但问题当然在于您的业务计算的细节。

于 2009-10-19T14:22:12.517 回答
1

是的。提取 usingsubstr可能是最快的方法。那是:

$FIELDS::transaction_date = substr $_, 0, 8;
$FIELDS::customer_id      = substr $_, 8, 20;

可能会更快。现在,如果我正在手写这段代码,我不会放弃unpack,但如果你正在生成代码,你不妨试一试。

另请参阅Perl 的 unpack() 是否比 substr() 更快?

至于剥离前导空格,s/^\s+//可能是最快的方法。

更新:如果不能运行基准测试,很难说任何确定的事情。但是,如何:

my  $x  = substr $_, 0, 8;

对于不需要任何修剪的字段和

my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/;

需要修剪吗?

于 2009-10-19T13:56:58.170 回答
1

这也可能适用于 XS - 这样您就可以使用 C 函数来更改数据。我可以想象这比其他任何东西都快得多,因为您可以手动控制何时真正复制数据。
由于您依赖于 C 编译器,因此构建过程将变得更加困难,并且需要额外的集成步骤。

于 2009-10-19T16:20:05.307 回答
1

只需让它并行运行。这很简单,而且在任何远程现代机器上都会更快。

于 2009-10-20T07:33:13.027 回答
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//;

随后像以前一样选择性地去除前导空格。

感谢到目前为止的所有答案。我会接受思南的,因为它对我们有用,尽管看起来“完全错误”。

于 2009-10-20T14:39:44.533 回答