Performance Perl DBI-用于消除循环的性能?
我正在编写一个perl脚本,它使用DBI将数据从数据库表卸载到特定格式。我有点事要做,但演出。。。缺乏 以下是代码的性能关键部分:Performance Perl DBI-用于消除循环的性能?,performance,perl,loops,dbi,Performance,Perl,Loops,Dbi,我正在编写一个perl脚本,它使用DBI将数据从数据库表卸载到特定格式。我有点事要做,但演出。。。缺乏 以下是代码的性能关键部分: 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 binar
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)。SettingsDBI_PROFILE=4
显示获取本身大约需要45秒
关键在于:删除实际的替换步骤($col=~s/(?=[\x5C\x00-\x1F])/\\/g;
)只节省大约12秒的处理时间。这意味着对$col(@$row){;}不执行循环(为$col(@$row){;}
)会产生18秒的开销,比替换本身还要多。(通过完全移除回路验证了这一点。)
摘要:
- 消毒循环大约占总执行时间的1/3,对于我的测试数据大约30秒。根据源数据中的列数,按比例需要更多的时间
- 消毒循环的替换部分(
)需要12秒来获取测试数据$col=~s/..//g;
- 剩下的18秒是for循环结构本身
奖励:为什么for循环开销很高 注意事项:
- 消毒本身只是在任何特殊字符前加上反斜杠
- 需要进行消毒,并且必须在
连接之前对每一列进行消毒。这是一个技术限制,因为
可能包含特殊字符,我们需要它们不能转义。此外,$COLUMN\u DELIM\u STR
的长度和值在脚本运行期间可能会有所不同$COLUMN\u DELIM\u STR
- 可以预先确定列数,但不能确定列名或数据类型。脚本事先不知道哪些列可能包含或不包含需要转义的特殊字符
- 如果有更好的方法来清理列数据,请随时提出建议。我对其他想法持开放态度
- 测试线束加上替换每个元素需要3.57µs(对于七个字符串,其中一个字符需要转义)
- 测试线束加上回路每个元件需要0.960µs+0.141µs
- 因此,在5个元素上循环将变成1.66µs
(1000个元素):7065.42/s对于
(0个元素):1041030.65/s
:284348.25/ss//
- 测试线束加上替换每个元素需要3.57µs(对于七个字符串,其中一个字符需要转义)
- 测试线束加上回路每个元件需要0.960µs+0.141µs
- 因此,在5个元素上循环将变成1.66µs
(1000个元素):7065.42/s对于
(0个元素):1041030.65/s
:284348.25/ss//
- 对我来说
输出:
- 对我来说
输出:
另一个选项是在SELECT中进行转义。在Oracle中,您可以使用。这应该可以(我可能把反斜杠的细节搞错了) 现在的问题是对每一列都这样做。您不知道有多少列或它们的名称,但您可以使用
SELECT*fromtable LIMIT 1
和$sth->fetchrow\u hashref
或更直接地使用$dbh->column\u info
来查找。现在,您可以构造一个具有正确行数的SELECT,并将REGEXP_REPLACE应用于每个行。这可能更快。您甚至可以在选择中进行连接
您甚至可以编写一个PL/SQL函数来完成这一切。这可能是最有效的。这里有一个可以用来做转义的
至于为什么空循环很慢,您运行了5000万次,尽管18秒似乎很高。我的2011 Macbook Pro可以在大约6秒钟内运行它,让我们验证一下空循环是问题所在。这个代码需要多长时间
time perl -wle 'my $rows = [1..5]; for my $row (1..10_000_000) { for $col (@$rows) {} }'
简单地迭代5000万次(对于(1..50\u 000\u 000)
)需要三分之一的时间。所以也许有一种方法可以对内部循环进行微观优化。恕我直言,事实证明,在无块的void上下文中,映射速度要快得多
map s{(?=[\x5C\x00-\x1F])}{\\}g, @$rows;
为什么??用B::Terse转储字节码告诉我们Perl在映射中做的工作更少。下面是内部for循环的作用:
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
BINOP (0x1234567890ab) leaveloop
LOOP (0x1234567890ab) enteriter
OP (0x1234567890ab) null [3]
UNOP (0x1234567890ab) null [147]
OP (0x1234567890ab) pushmark
UNOP (0x1234567890ab) rv2av [7]
OP (0x1234567890ab) padsv [1]
PADOP (0x1234567890ab) gv GV (0x1234567890ab) *_
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
PMOP (0x1234567890ab) subst
SVOP (0x1234567890ab) const [12] PV (0x1234567890ab) "2"
OP (0x1234567890ab) unstack
OP (0x1234567890ab) unstack
这是地图
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
LOGOP (0x1234567890ab) mapwhile [8]
LISTOP (0x1234567890ab) mapstart
OP (0x1234567890ab) pushmark
UNOP (0x1234567890ab) null
PMOP (0x1234567890ab) subst
SVOP (0x1234567890ab) const [12] PV (0x1234567890ab) "2"
UNOP (0x1234567890ab) rv2av [7]
OP (0x1234567890ab) padsv [1]
OP (0x1234567890ab) unstack
基本上,for循环必须进行额外的工作,为每次迭代设置新的词汇上下文。地图没有,但你不能
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
BINOP (0x1234567890ab) leaveloop
LOOP (0x1234567890ab) enteriter
OP (0x1234567890ab) null [3]
UNOP (0x1234567890ab) null [147]
OP (0x1234567890ab) pushmark
UNOP (0x1234567890ab) rv2av [7]
OP (0x1234567890ab) padsv [1]
PADOP (0x1234567890ab) gv GV (0x1234567890ab) *_
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
PMOP (0x1234567890ab) subst
SVOP (0x1234567890ab) const [12] PV (0x1234567890ab) "2"
OP (0x1234567890ab) unstack
OP (0x1234567890ab) unstack
UNOP (0x1234567890ab) null
LOGOP (0x1234567890ab) and
OP (0x1234567890ab) iter
LISTOP (0x1234567890ab) lineseq
COP (0x1234567890ab) nextstate
LOGOP (0x1234567890ab) mapwhile [8]
LISTOP (0x1234567890ab) mapstart
OP (0x1234567890ab) pushmark
UNOP (0x1234567890ab) null
PMOP (0x1234567890ab) subst
SVOP (0x1234567890ab) const [12] PV (0x1234567890ab) "2"
UNOP (0x1234567890ab) rv2av [7]
OP (0x1234567890ab) padsv [1]
OP (0x1234567890ab) unstack