如何在Raku中高效实现wc -l功能?附Perl 5实现对比
要在Raku里高效实现类似wc -l的行数统计,核心要避免把整个大文件加载到内存里——你之前遇到的慢和高内存占用问题,就是因为lines.elems在旧版本Raku里会把所有行都读进内存才计数。下面就来拆解问题,给出高效的解决方案:
为什么你的初始写法慢?
"test.txt".IO.open.lines.elems之所以性能拉胯,是因为你使用的Rakudo 2019.11版本中,调用.elems会强制遍历所有行并把它们全部存在内存里,这对1.8G的大文件来说简直是灾难。而Perl 5的-ln模式是逐行处理,只维护一个计数器,完全不存储文件内容,所以效率高得多。
高效的Raku实现方式
1. 用-ne参数+END块(你试过的最优原生写法)
这是最接近Perl 5写法的方式,逐行处理仅计数,结束时输出结果:
raku -ne '++$; END { say $ }' test.txt
或者更简洁的版本:
raku -ne '++$ andthen END .say' test.txt
从你的测试结果看,这个方法耗时1分44秒,比初始写法快了近一半,而且完全没用到交换分区,内存占用极低——因为它只在内存里保存一个计数器变量,不会留存任何文件内容。
2. 显式逐行遍历的单行命令
如果不想依赖-ne参数,也可以直接写遍历逻辑,效果一致:
raku -e 'my $count = 0; $count++ for "test.txt".IO.lines; say $count'
这个写法同样是逐行处理,不保留任何行数据,内存开销可以忽略。
3. 新版本Raku的优化写法
如果你升级到2023年及以后的Raku版本,lines.elems的性能已经大幅提升,甚至可以直接用:
raku -e 'say "test.txt".IO.lines.elems'
新版本里.lines是完全惰性的,.elems会高效遍历计数而不加载整个文件,性能会接近Perl 5的水平。
性能对比(基于你的测试环境)
整理你给出的测试数据,直观对比各方法表现:
| 命令 | 耗时 | 内存/交换分区使用 |
|---|---|---|
wc -l test.txt | 0m0.350s | 几乎无额外占用 |
perl -lnE 'END { say $. }' test.txt | 0m1.981s | 低内存占用 |
raku -ne '++$ andthen END .say' test.txt | 1m44.906s | 无交换分区使用 |
raku -e 'say "test.txt".IO.open.lines.elems' | 2m51.852s | 使用779M交换分区 |
小建议
升级你的Raku版本吧!Rakudo近几年在文件IO和性能优化上做了超多改进,新版本处理大文件的速度会比2019.11快很多,能进一步缩小和Perl 5的差距。
内容的提问来源于stack exchange,提问作者TheAthlete

