【发布时间】:2015-03-27 17:55:41
【问题描述】:
我正在编写一个使用 DBI 将数据从数据库表中卸载为特定格式的 perl 脚本。我有一些工作,但性能......缺乏。
这是代码的性能关键部分:
while (my $row = $query->fetchrow_arrayref()) {
# Sanitize the columns to make sure certain characters are escaped with a backslash.
# The escaping is required as some binary data may be included in some columns.
# This must occur *before* the join() as $COLUMN_DELIM_STR may contain one of the special characters.
for $col (@$row) { $col =~ s/(?=[\x5C\x00-\x1F])/\\/g; }
# Output the sanitized row
print join($COLUMN_DELIM_STR, @$row) . $RECORD_DELIM_STR;
}
我有一个包含 5 列和 1000 万行的测试表。总卸载时间为 90 秒(输出重定向到 /dev/null,因此磁盘写入不会干扰基准测试)。
在尝试删除代码块以了解它们如何影响性能后,我意识到清理循环在时间上占了大量的处理时间,大约 30 秒(大约是总执行时间的 1/3时间)。设置DBI_PROFILE=4 显示提取本身需要大约 45 秒。
关键在于:删除实际替换步骤 ($col =~ s/(?=[\x5C\x00-\x1F])/\\/g;) 仅节省了大约 12 秒的处理时间。这意味着无操作 for 循环 (for $col (@$row) { ; }) 会产生 18 秒的开销,比替换本身还多。 (这已通过完全删除循环来验证。)
总结:
- 清理循环大约需要总执行时间的 1/3,我的测试数据大约需要 30 秒。根据源数据中的列数,它会相应地花费更多时间。
- 清理循环 (
$col =~ s/...//g;) 的替换部分需要 12 秒来处理我的测试数据。 - 剩下的 18 秒是 for 循环结构本身。
问题:
如何提高消毒步骤的性能?
奖励:为什么 for 循环开销很高?
注意事项:
清理本身只是在任何特殊字符之前放置一个反斜杠。
需要进行清理,并且必须在
join发生之前对每一列进行清理。这是一个技术限制,因为$COLUMN_DELIM_STR可能包含特殊字符,我们需要它们不被转义。此外,$COLUMN_DELIM_STR的长度和值可能因脚本运行而异。可以提前确定列数,但不能确定列名或数据类型。该脚本不知道哪些列可能包含或可能不包含需要转义的特殊字符。
如果有更好的清理列数据的方法,请随时提出建议。我愿意接受其他想法。
【问题讨论】:
-
数据库在哪里 - 跨网络?什么是数据库?
-
您可以使用
qr编译正则表达式以获得更高的性能。 (但由于这不是最大的问题,所以我没有发布作为答案) -
@MarkSetchell - 数据库在本地机器上,但这不是重点。我更关心的是消毒程序会浪费 1/3 的总处理时间。随着表中列数的增加,清理数据所花费的总时间百分比显着增加。
-
尝试使用Devel::NYTProf 以获得更细粒度的配置文件。
-
@Sobrique,
qr//、m//和s///中的常量正则表达式模式在编译时编译。perl -Mre=debug -c -e'qr/qr/; m/m/; s/s//;' 2>&1 | grep Compiling
标签: performance perl loops dbi