【问题标题】:awk vs perl in reading a csv?awk vs perl 在读取 csv 中?
【发布时间】:2014-05-09 12:39:02
【问题描述】:

我在这里需要速度。我有一个 500mb 大小的 csv 文件(实际上有很多 csv 但我现在只考虑一个) 我需要阅读第 3 列并从中选择唯一的字符串。以下方法我测试过,我发现只有 awk 是最快的

使用 perl:

  • 使用传统方式读取文件打开文件并通过 while 循环的文件句柄
  • 取消设置 $/ 变量并将整个文件转换为字符串,然后 分割线并选择第三个字段并继续
  • 使用 File::Slurp 模块
  • 使用 Text::CSV / XS 读取 csv 并做必要的事情

在上述所有方式中,解析 csv 大约需要 500 秒。

但如果我尝试使用 awk 执行相同操作,它会在将近 10 秒内完成。

我仍在学习 perl 的步骤,最近在看到它的哈希功能后,我对它的热情越来越高。但是这个问题困扰了我 背部。这是 unix 工具占上风的 perl 的一些限制吗?

我知道 Text::CSV 是处理 csv 文件的最佳方式。但是速度是我关心的问题,我可以保证我的 csv 没有任何嵌入的逗号或其他只有 Text::CSV 处理的问题。

更新:发现我的问题

my %hash;
my $file = $ARGV[0] or die "Need input CSV $!\n";
open(my $fh,'<',$file) or die "Could not open the $file $!\n";
while(my $line = <$fh>)
{
chomp($line);
my $field2=(split /|/, $line)[2];  #I missed to quote the pipe delimiter
$hash{$field2}++;
}
print "$_\n" for keys %hash;

另一个更新:问题和修复

我的 csv 由“|”分隔我错过了引用它们。因此,执行时间显着减慢,而且它产生的输出是错误的,我没有注意到。引用分隔符后,脚本能够在大约 18 秒内完成,当我使用 @Borodin 限制字段拆分的逻辑时,执行时间进一步减少。我能够达到与 awk 相同的速度。

我仍然发现 Text::CSV 方法较慢,因为我的文件可以使用默认的拆分方法,所以我将使用它。

【问题讨论】:

  • 向我们展示代码和一些示例输入行。
  • 啜饮一个 500 MiB 的文件不是一个好主意。每行有多宽(500 MiB 文件中有多少行)?多少个领域?您如何收集第三列?一旦你收集了它,你会用它做什么?您是否在“awk”模式下尝试过 Perl (perl -n -a -F, -l -e 'print $F[2]')?
  • Text::CSV_XS 是纯C;它可能会加快你的代码速度。
  • cut -d '|' -f 3 | sort -u 是怎么做的,如果这就是你所需要的?
  • 是的,cut/awk + ​​sort 从一开始就很好。我只是想知道为什么 perl 很慢。现在我很高兴我找到了问题和解决方案

标签: perl awk


【解决方案1】:

我生成了一个包含 10,000,000 行的 350 MiB 文件,类似于:

part1,part2,data856801,part4,part5

(其中第三列中的数字是 100,000 到 999,999 之间的随机值)并使用自制的 Perl 5.18.1:

time perl -n -a -F, -l -e '$a{$F[2]}++;
           END { foreach $key (sort keys %a) {print "$key";} }' junk.data >junk.perl.output

这大约需要 34 秒。没有sort,大约需要 33 秒(我在时间上有一些差异)。与系统提供的 Perl 5.16.2 的时间基本相同。

为了比较,使用 BSD awk (20070501):

time awk -F, '{a[$3]++} END {for (key in a) print key}' junk.data > junk.awk.output

这大约需要 29 秒,以未排序的顺序生成数据。 GNU awk 3.1.7 大约需要 15 秒(快得多)。

只需对文件使用catcp 只需5 秒多一点。

所有过滤后的输出文件都有 899993 行;一致性好。

因此,对于这项工作,Perl 似乎比awk 稍慢,但在我的机器上却没有 50 倍。我不确定 Perl 脚本可以做多少优化;我写的很简单粗暴。

测试:

  • MacBook Pro(2011 年初),2.3 GHz Intel Core i7。
  • 16 GiB 1333 MHz DDR3 RAM。
  • 750 GiB 5400 rpm 磁盘。
  • 小牛队 10.9.2
  • Perl 5.18.1 (5.16.2)
  • BSDawk20070501
  • GNU awk 3.1.7

我让 iTunes 在后台播放音乐,并且在浏览器中输入,所以在运行测试时系统没有空闲。


将 Text::CSV 与 Text::CSV_XS 和以下脚本一起使用,耗时近 49 秒:

#!/usr/bin/env perl
use strict;
use warnings;

use Text::CSV;

my %a;
my $csv = Text::CSV->new ( { binary => 1 } )  # should set binary attribute.
                   or die "Cannot use CSV: ".Text::CSV->error_diag ();

open my $fh, "<:encoding(utf8)", "junk.data" or die "junk.data: $!";
while ( my $row = $csv->getline( $fh ) )
{
    $a{$row->[2]}++;
}
$csv->eof or $csv->error_diag();
close $fh;

print "$_\n" for keys %a;

Perl 运行速度很快

有趣的是,Borodinscript 耗时大约 17 秒,比 Perl-as-awk 模式操作快了不少。知道 Perl 是否设法优化拆分会很有趣,因为它知道它只需要第三个字段,而 awk 模式必须在每行中拆分五个字段(对于示例文件),即使只有第三个是用过。

这与 GNU awk 时间非常相似。

【讨论】:

  • Borodin 的脚本明确指出最多只能拆分 4 个字段 - perl 不会自动进行该优化(尽管它确实优化了 (undef,undef,$col3)=split...
  • 为了获得额外的速度,您可以尝试使用 Configure -Dusefaststdio 构建一个 perl(尽管现在有足够多的东西依赖于 PerlIO 层,这不再适合一般用途)
  • split(/|/,$text),我知道 perl 将管道视为正则表达式而不引用它,但为什么它会减慢速度
  • 因为它正在寻找空或空作为分隔符,然后为每一行中的每个字符创建一列输出。慢得多!请注意,展示您正在尝试的内容非常重要,因为我们很可能很快就发现了问题。
  • 嘿乔纳森,只是好奇,你是如何生成指定限制和数据的垃圾 csv 的。只需在 while 循环中使用 print 语句和 rand 函数,直到文件达到指定大小?
【解决方案2】:

如果您展示了您的 Perl 代码,这将有助于发现问题。您描述的文件不需要Text::CSV 的开销,其中数据从不包含逗号(并且可能没有引号?)

你的程序应该是这样的

use strict;
use warnings;
use autodie;

open my $fh, '<', 'myfile';

my %data;

while (<$fh>) {
  my $col3 = (split /,/, $_, 4)[2];
  ++$data{$col3};
}

print "$_\n" for keys %data;

【讨论】:

  • 谢谢,这对我很有帮助。我在我的问题中更新了我所做的导致程序变慢的错误
猜你喜欢
  • 2011-08-04
  • 1970-01-01
  • 2012-01-24
  • 2022-01-28
  • 2021-09-23
  • 2021-01-02
  • 2012-03-06
  • 2014-07-25
  • 2020-01-25
相关资源
最近更新 更多