如何避免Perl中自动换行处理导致的文本输出性能损耗?
Perl5自动换行输出的性能优化问题
核心问题摘要
Perl5的自动换行处理会降低输出性能,能不能在不放弃say或-l的便利性、不用手动添加换行的前提下,缓解这种性能损耗?
详细背景与测试
我一直纳闷,主观上觉得Perl5的字符串处理速度比Python快,但Benchmark Game的客观测试(比如fasta示例)里Perl反而更慢。后来发现多数开发者用say输出,而我一直坚持用print手动追加\n。
为此我搭了个极简性能测试:
- 在Perl里从1计数到指定数值
- 每一步输出一行数字
- 重复测试三次
- 把标准输出重定向到
/dev/null,取三次运行的最短时间
测试三种输出方案,每种方案都对比输出缓冲开启($|=0)和关闭($|=1)的状态:
- 用
print手动追加\n - 调用Perl时加
-l参数开启自动换行,搭配print - 使用Perl的
say特性自动换行
测试结果
测试结果很意外:禁用输出缓冲区对性能的影响远小于启用自动换行处理——后者让运行时间增加了一倍还多。
Records/sec n_total Time Command 7120052.46 10000000 1.40 perl -e '$|=0; for (my $i=1; $i<=10000000;$i++) {print $i . "\n";};' 4961279.63 10000000 2.02 perl -e '$|=1; for (my $i=1; $i<=10000000;$i++) {print $i . "\n";};' 2821234.16 10000000 3.54 perl -l -e '$|=0; for (my $i=1; $i<=10000000;$i++) {print $i;};' 2462226.44 10000000 4.06 perl -l -e '$|=1; for (my $i=1; $i<=10000000;$i++) {print $i;};' 2898328.96 10000000 3.45 perl -e 'use feature "say"; $|=0; for (my $i=1; $i<=10000000;$i++) {say $i;};' 2452486.23 10000000 4.08 perl -e 'use feature "say"; $|=1; for (my $i=1; $i<=10000000;$i++) {say $i;};'
正式问询
手动加换行能提升性能,但操作太麻烦。有没有什么遗漏的参数或开关,能在使用say或-l参数时避免这种性能下降?
额外跨工具对比
为了全面了解情况,我还对比了其他常用工具(gawk、jq、Python等)的输出性能,发现Perl禁用输出缓冲区对性能的影响比多数其他语言小。
Records/sec n_total Time Command 46772945.30 100000000 2.14 -- seq 100000000 7102358.15 10000000 1.41 -- perl -e '$|=0; for (my $i=1; $i<=10000000;$i++) {print $i . "\x0A";};' 4851147.42 10000000 2.06 -- php -r 'for ($i=1; $i<=10000000;$i++) {echo $i . PHP_EOL;};' 4524956.61 10000000 2.21 -- perl -e '$|=1; for (my $i=1; $i<=10000000;$i++) {print $i . "\x0A";};' 2643538.99 10000000 3.78 -- perl -l -e '$|=0; for (my $i=1; $i<=10000000;$i++) {print $i;};' 2542994.96 10000000 3.93 -- awk -e "BEGIN{for (i=1;i<=10000000;i++) print i}" 2537951.85 10000000 3.94 -- perl -e '$|=0; use feature "say"; for (my $i=1; $i<=10000000;$i++) {say $i;};' 2407226.07 10000000 4.15 -- luajit -e 'for i = 1,10000000 do print(i) end' 2347240.89 10000000 4.26 -- perl -e '$|=1; use feature "say"; for (my $i=1; $i<=10000000;$i++) {say $i;};' 2342141.92 10000000 4.27 -- perl -l -e '$|=1; for (my $i=1; $i<=10000000;$i++) {print $i;};' 1869504.49 10000000 5.35 -- python -c 'for i in range(1,round(10000000)+1): print(i)' 1641012.48 10000000 6.09 -- awk -e "BEGIN{for (i=1;i<=10000000;i++) {print i; fflush()}}" 1591311.72 10000000 6.28 -- jq -n 'range(1;10000000+1)' 1395488.79 10000000 7.17 -- lua -e 'for i = 1,10000000 do print(i) end' 905889.95 1000000 1.10 -- echo 'for {set i 1} {$i <= 1000000} {incr i} {puts $i}' | tclsh 516841.54 1000000 1.93 -- python -uc 'for i in range(1,round(1000000)+1): print(i)' 263998.59 1000000 3.79 -- clisp -x '(loop for x from 1 to 1000000 do (print x))' 139282.21 1000000 7.18 -- echo '(loop for x from 1 to 1000000 do (print x))' | sbcl
使用版本:Perl 5.36.3; Python 3.9.16; PHP 7.3.7; GAWK 5.3.0; Lua 5.3.6; Luajit 2.0.4; jq 1.7.1; clisp 2.49.92; sbcl 2.4.9; tclsh 8.6.12; seq from GNU core utils 9.0
内容的提问来源于stack exchange,提问作者jf1
相关产品推荐
相关产品推荐

