繁体   English   中英

使用Perl或Linux内置命令行工具如何快速将一个整数映射到另一个整数?

[英]Using Perl or Linux built-in command-line tools how quickly map one integer to another?

我有一个两个整数的文本文件映射,用逗号分隔:

123,456
789,555
...

它是120Megs ...所以这是一个非常长的文件。

我继续搜索第一列并返回第二列,例如,查找789 --returns - > 555,我需要使用常规的Linux内置函数快速完成。

我现在正在这样做,每次查找需要几秒钟。

如果我有一个数据库,我可以索引它。 我想我需要一个索引文本文件!

这是我现在正在做的事情:

my $lineFound=`awk -F, '/$COLUMN1/ { print $2 }' ../MyBigMappingFile.csv`;

是否有任何简单的方法可以提高性能?

120兆不是那么大。 假设你有至少512MB的内存,你可以轻松地将整个文件读入一个哈希,然后针对它进行所有查找。

哈希建议是经验丰富的Perler执行此操作的自然方式,但在这种情况下可能不是最理想的。 它扫描整个文件,并在线性时间内构建大型扁平数据结构。 较粗糙的方法可能在最坏的情况下线性时间短路,通常在实践中较少。

我首先制作了一个大的映射文件:

my $LEN = shift;
for (1 .. $LEN) {
    my $rnd = int rand( 999 );
    print "$_,$rnd\n";
}

在命令行上传递$LEN为10000000,文件出现了113MB。 然后我对三个实现进行了基准测试。 第一种是哈希查找方法。 第二个文件啜饮文件并用正则表达式进行扫描。 第三个逐行读取并在匹配时停止。 完成实施:

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw{timethese};

my $FILE  = shift;
my $COUNT = 100;
my $ENTRY = 40;


slurp(); # Initial file slurp, to get it into the hard drive cache

timethese( $COUNT, {
    'hash'       => sub { hash_lookup( $ENTRY ) },
    'scalar'     => sub { scalar_lookup( $ENTRY ) },
    'linebyline' => sub { line_lookup( $ENTRY ) },
});


sub slurp
{
    open( my $fh, '<', $FILE ) or die "Can't open $FILE: $!\n";
    undef $/;
    my $s = <$fh>;
    close $fh;
    return $s;
}

sub hash_lookup
{
    my ($entry) = @_;
    my %data;

    open( my $fh, '<', $FILE ) or die "Can't open $FILE: $!\n";
    while( <$fh> ) {
        my ($name, $val) = split /,/;
        $data{$name} = $val;
    }
    close $fh;

    return $data{$entry};
}

sub scalar_lookup
{
    my ($entry) = @_;
    my $data = slurp();
    my ($val) = $data =~ /\A $entry , (\d+) \z/x;
    return $val;
}

sub line_lookup
{
    my ($entry) = @_;
    my $found;

    open( my $fh, '<', $FILE ) or die "Can't open $FILE: $!\n";
    while( <$fh> ) {
        my ($name, $val) = split /,/;
        if( $name == $entry ) {
            $found = $val;
            last;
        }
    }
    close $fh;

    return $found;
}

我的系统上的结果:

Benchmark: timing 100 iterations of hash, linebyline, scalar...
      hash: 47 wallclock secs (18.86 usr + 27.88 sys = 46.74 CPU) @  2.14/s (n=100)
linebyline: 47 wallclock secs (18.86 usr + 27.80 sys = 46.66 CPU) @  2.14/s (n=100)
    scalar: 42 wallclock secs (16.80 usr + 24.37 sys = 41.17 CPU) @  2.43/s (n=100)

(注意我在SSD上运行它,因此I / O非常快,并且可能使得初始的slurp()不必要.YMMV。)

有趣的是, hash实现与linebyline一样快,这不是我的预期。 通过使用slurping, scalar可能最终在传统硬盘驱动器上更快。

但是,到目前为止最快的是对 grep的简单调用:

 
 
 
 
  
  
  $ time grep '^40,' int_map.txt 40,795 real 0m0.508s user 0m0.374s sys 0m0.046
 
 
  

Perl可以很容易地读取输出并在几乎任何时间分开逗号。

编辑:没关系grep。 我误读了数字。

采用:

sed -n "/^$COLUMN1/{s/.*,//p;q}" file

这样可以通过三种方式优化您的代码:1)无需在“,”中将每行分成两行。 2)在第一次点击后停止处理文件。 3)sed比awk快。

这应该是你搜索时间的一半以上。

HTH Chris

这一切都取决于数据更改的频率以及在单个脚本调用过程中需要查看的频率。

如果在每个脚本调用期间有很多查找,我建议将文件解析为散列(或者如果键的范围足够窄则将数组解析)。

如果文件每天都在变化,那么创建一个新的SQLite数据库可能也可能不值得花时间。

如果每个脚本调用只需要查找一个键,并且数据文件经常更改,则可以通过将整个文件拖入标量(最小化内存开销,并在其上进行模式匹配(而不是解析每个)来获得改进线)。

#!/usr/bin/env perl

use warnings; use strict;

die "Need key\n" unless @ARGV;

my $lookup_file = 'lookup.txt';
my ($key) = @ARGV;

my $re = qr/^$key,([0-9]+)$/m;

open my $input, '<', $lookup_file
    or die "Cannot open '$lookup_file': $!";

my $buffer = do { local $/; <$input> };

close $input;

if (my ($val) = ($buffer =~ $re)) {
    print "$key => $val\n";
}
else {
    print "$key not found\n";
}

在我旧的慢速笔记本电脑上,在文件末尾有一个键:

C:\Temp> dir lookup.txt
...
2011/10/14  10:05 AM       135,436,073 lookup.txt

C:\Temp> tail lookup.txt
4522701,5840
5439981,16075
7367284,649
8417130,14090
438297,20820
3567548,23410
2014461,10795
9640262,21171
5345399,31041

C:\Temp> timethis lookup.pl 5345399

5345399 => 31041

TimeThis :  Elapsed Time :  00:00:03.343

此示例将文件加载到哈希(在我的系统上120M需要大约20秒)。 随后的查找几乎是即时的。 这假设左列中的每个数字都是唯一的。 如果情况并非如此,则需要将右侧的数字与左侧相同的数字推送到数组或其他内容。

use strict;
use warnings;

my ($csv) = @ARGV;
my $start=time;
open(my $fh, $csv) or die("$csv: $!");
$|=1;
print("loading $csv... ");
my %numHash;
my $p=0;
while(<$fh>) { $p+=length; my($k,$v)=split(/,/); $numHash{$k}=$v }
print("\nprocessed $p bytes in ",time()-$start, " seconds\n");
while(1) { print("\nEnter number: "); chomp(my $i=<STDIN>); print($numHash{$i}) }

用法和输出示例:

$ ./lookup.pl MyBigMappingFile.csv 
loading MyBigMappingFile.csv... 
processed 125829128 bytes in 19 seconds

Enter number: 123
322

Enter number: 456
93

Enter number: 

如果你将文件cp到/dev/shm ,并使用/ awk / sed / perl / grep / ack /无论查询映射,它会有帮助吗?

不要告诉我你正在使用128MB的ram机器。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM