为什么 Perl 的 tr/\n// 随着行长度的增加而变得越来越慢?

2024-02-07

In perlfaq5 http://faq.perl.org/perlfaq5.html,有一个答案如何计算文件中的行数? http://faq.perl.org/perlfaq5.html#How_do_I_count_the_n。目前的答案建议sysread and a tr/\n//。我想尝试一些其他的事情来看看速度有多快tr/\n//是的,并且还可以针对具有不同平均行长度的文件进行尝试。我创建了一个基准来尝试各种方法来做到这一点。我在 Mac OS X 10.5.8 和 MacBook Air 上的 Perl 5.10.1 上运行:

  • 炮轰到wc(除短线外最快)
  • tr/\n//(第二快,除了较长的平均线长度)
  • s/\n//g(通常很快)
  • while( <$fh> ) { $count++ }(几乎总是缓慢地戳,除非tr///陷入困境)
  • 1 while( <$fh> ); $.(非常快)

让我们忽略这一点wc,即使有所有 IPC 的东西,这确实会带来一些有吸引力的数字。

乍一看,它看起来像tr/\n//当行长度很小时(例如,100 个字符)时,它的性能非常好,但当行长度变大(一行 1,000 个字符)时,其性能就会下降。队伍越长,情况就越糟糕tr/\n//做。我的基准测试有问题吗,或者内部发生了其他事情,导致tr///降级?为什么不s///同样退化?

一、结果:

                         Rate very_long_lines-tr very_long_lines-$count very_long_lines-$. very_long_lines-s very_long_lines-wc
very_long_lines-tr     1.60/s                 --                   -10%               -12%              -39%               -72%
very_long_lines-$count 1.78/s                11%                     --                -2%              -32%               -69%
very_long_lines-$.     1.82/s                13%                     2%                 --              -31%               -68%
very_long_lines-s      2.64/s                64%                    48%                45%                --               -54%
very_long_lines-wc     5.67/s               253%                   218%               212%              115%                 --
                    Rate long_lines-tr long_lines-$count long_lines-$. long_lines-s long_lines-wc
long_lines-tr     9.56/s            --               -5%           -7%         -30%          -63%
long_lines-$count 10.0/s            5%                --           -2%         -27%          -61%
long_lines-$.     10.2/s            7%                2%            --         -25%          -60%
long_lines-s      13.6/s           43%               36%           33%           --          -47%
long_lines-wc     25.6/s          168%              156%          150%          88%            --
                     Rate short_lines-$count short_lines-s short_lines-$. short_lines-wc short_lines-tr
short_lines-$count 60.2/s                 --           -7%           -11%           -34%           -42%
short_lines-s      64.5/s                 7%            --            -5%           -30%           -38%
short_lines-$.     67.6/s                12%            5%             --           -26%           -35%
short_lines-wc     91.7/s                52%           42%            36%             --           -12%
short_lines-tr      104/s                73%           61%            54%            14%             --
                      Rate varied_lines-$count varied_lines-s varied_lines-$. varied_lines-tr varied_lines-wc
varied_lines-$count 48.8/s                  --            -6%             -8%            -29%            -36%
varied_lines-s      51.8/s                  6%             --             -2%            -24%            -32%
varied_lines-$.     52.9/s                  8%             2%              --            -23%            -30%
varied_lines-tr     68.5/s                 40%            32%             29%              --            -10%
varied_lines-wc     75.8/s                 55%            46%             43%             11%              --

这是基准。我确实有一个控制器,但速度太快了,我根本不在乎。第一次运行它时,基准测试会创建测试文件并打印一些有关其行长度的统计信息:

use Benchmark qw(cmpthese);
use Statistics::Descriptive;

my @files = create_files();

open my( $outfh ), '>', 'bench-out';

foreach my $file ( @files )
    {
    cmpthese(
        100, {
#               "$file-io-control" => sub { 
#                       open my( $fh ), '<', $file; 
#                   print "Control found 99999 lines\n";
#                       },
               "$file-\$count" => sub { 
                    open my( $fh ), '<', $file; 
                    my $count = 0;
                    while(<$fh>) { $count++ } 
                    print $outfh "\$count found $count lines\n";
                    },
               "$file-\$."     => sub { 
                    open my( $fh ), '<', $file; 
                    1 while(<$fh>); 
                    print $outfh "\$. found $. lines\n";
                    },
               "$file-tr"      => sub { 
                    open my( $fh ), '<', $file; 
                    my $lines = 0;
                    my $buffer;
                    while (sysread $fh, $buffer, 4096) {
                        $lines += ($buffer =~ tr/\n//);
                        }
                    print $outfh "tr found $lines lines \n";
                    },
               "$file-s"       => sub { 
                    open my( $fh ), '<', $file; 
                    my $lines = 0;
                    my $buffer;
                    while (sysread $fh, $buffer, 4096) {
                        $lines += ($buffer =~ s/\n//g);
                        }
                    print $outfh "s found $lines line\n";
                    },
               "$file-wc"       => sub { 
                    my $lines = `wc -l $file`;
                    chomp( $lines );
                    print $outfh "wc found $lines line\n";
                    },
                    }
           );   
     }

sub create_files
    {
            my @names;
    my @files = (
        [ qw( very_long_lines 10000  4000 5000 ) ],
        [ qw( long_lines   10000 700 800 ) ],
        [ qw( short_lines  10000  60  80 ) ],
        [ qw( varied_lines 10000  10 200 ) ],
        );

    foreach my $tuple ( @files )
        {
        push @names, $tuple->[0];
        next if -e $tuple->[0];
        my $stats = create_file( @$tuple );
        printf "%10s: %5.2f  %5.f \n", $tuple->[0], $stats->mean, sqrt( $stats->variance );
        }

    return @names;
    }


sub create_file
    {
    my( $name, $lines, $min, $max ) = @_;

    my $stats = Statistics::Descriptive::Full->new();

    open my( $fh ), '>', $name or die "Could not open $name: $!\n";

    foreach ( 1 .. $lines )
        {
        my $line_length = $min + int rand( $max - $min );
        $stats->add_data( $line_length );
        print $fh 'a' x $line_length, "\n";
        }

    return $stats;
    }

我想知道我们一直使用的基准测试是否有太多的移动部分:我们正在处理不同大小的数据文件,使用不同的行长度,并尝试衡量速度tr相对于其竞争对手——有一个基本的(但未经测试的)假设:tr是其性能随线路长度而变化的方法。

另外,正如布莱恩在一些评论中指出的那样,我们正在喂养tr数据缓冲区始终大小相同(4096 字节)。如果任何方法应该是不敏感的对于线条尺寸,应该是tr.

然后我突然想到:如果tr参考点是否稳定,其他方法是否随线尺寸变化?当你向宇宙飞船的窗户望去时,是你还是那只克林贡猛禽在移动?

因此,我开发了一个保持数据文件大小不变的基准:行长度变化,但字节总数保持不变。结果显示:

  • tr是最不敏感的方法 线长度的变化。自从 处理的总字节数为 所有三个线长度均为常数 经过测试(短、中、长),这个 意思是tr是相当有效的 编辑给定的字符串。甚至 虽然短行数据文件 需要更多的编辑,tr方法能够处理数据 文件几乎和处理的速度一样快 长行文件。
  • 依赖的方法<>速度 随着队伍变长, 尽管速度在递减。这 有道理:因为每次调用<>需要一些工作,应该是 处理给定的 N 个字节的速度较慢 使用较短的线路(至少超过 测试范围)。
  • The s///方法也很敏感 到线长。喜欢tr, 这 方法通过编辑字符串来工作 它是给定的。再次,较短的线路 长度意味着更多的编辑。显然, 的能力s///做出这样的 编辑效率比 的那个tr.

以下是使用 Perl 5.8.8 在 Solaris 上的结果:

#   ln = $.      <>, then check $.
#   nn = $n      <>, counting lines
#   tr = tr///   using sysread
#   ss = s///    using sysread

#   S = short lines  (50)
#   M = medium lines (500)
#   L = long lines   (5000)

       Rate nn-S
nn-S 1.66/s   --
ln-S 1.81/s   9%
ss-S 2.45/s  48%
nn-M 4.02/s 142%
ln-M 4.07/s 145%
ln-L 4.65/s 180%
nn-L 4.65/s 180%
ss-M 5.85/s 252%
ss-L 7.04/s 324%
tr-S 7.30/s 339%    # tr
tr-L 7.63/s 360%    # tr
tr-M 7.69/s 363%    # tr

Windows ActiveState 的 Perl 5.10.0 上的结果大致相当。

最后是代码:

use strict;
use warnings;
use Set::CrossProduct;
use Benchmark qw(cmpthese);

# Args: file size (in million bytes)
#       N of benchmark iterations
#       true/false (whether to regenerate files)
#
# My results were run with 50 10 1
main(@ARGV);

sub main {
    my ($file_size, $benchmark_n, $regenerate) = @_;
    $file_size *= 1000000;
    my @file_names = create_files($file_size, $regenerate);
    my %methods = (
        ln => \&method_ln,  # $.
        nn => \&method_nn,  # $n
        tr => \&method_tr,  # tr///
        ss => \&method_ss,  # s///
    );
    my $combo_iter = Set::CrossProduct->new([ [keys %methods], \@file_names ]);
    open my $log_fh, '>', 'log.txt';
    my %benchmark_args = map {
        my ($m, $f) = @$_;
        "$m-$f" => sub { $methods{$m}->($f, $log_fh) }
    } $combo_iter->combinations;
    cmpthese($benchmark_n, \%benchmark_args);
    close $log_fh;
}

sub create_files {
    my ($file_size, $regenerate) = @_;
    my %line_lengths = (
        S =>    50,
        M =>   500,
        L =>  5000,
    );
    for my $f (keys %line_lengths){
        next if -f $f and not $regenerate;
        create_file($f, $line_lengths{$f}, $file_size);
    }
    return keys %line_lengths;
}

sub create_file {
    my ($file_name, $line_length, $file_size) = @_;
    my $n_lines = int($file_size / $line_length);
    warn "Generating $file_name with $n_lines lines\n";
    my $line = 'a' x ($line_length - 1);
    chop $line if $^O eq 'MSWin32';
    open(my $fh, '>', $file_name) or die $!;
    print $fh $line, "\n" for 1 .. $n_lines;
    close $fh;
}

sub method_nn {
    my ($data_file, $log_fh) = @_;
    open my $data_fh, '<', $data_file;
    my $n = 0;
    $n ++ while <$data_fh>;
    print $log_fh "$data_file \$n $n\n";
    close $data_fh;
}

sub method_ln {
    my ($data_file, $log_fh) = @_;
    open my $data_fh, '<', $data_file;
    1 while <$data_fh>;
    print $log_fh "$data_file \$. $.\n";
    close $data_fh;
}

sub method_tr {
    my ($data_file, $log_fh) = @_;
    open my $data_fh, '<', $data_file;
    my $n = 0;
    my $buffer;
    while (sysread $data_fh, $buffer, 4096) {
        $n += ($buffer =~ tr/\n//);
    }
    print $log_fh "$data_file tr $n\n";
    close $data_fh;
}

sub method_ss {
    my ($data_file, $log_fh) = @_;
    open my $data_fh, '<', $data_file;
    my $n = 0;
    my $buffer;
    while (sysread $data_fh, $buffer, 4096) {
        $n += ($buffer =~ s/\n//g);
    }
    print $log_fh "$data_file s/ $n\n";
    close $data_fh;
}

更新以回应布拉德的评论。我尝试了所有三种变体,它们的行为大致如下s/\n//g-- 行数较短的数据文件速度较慢(附加条件是s/(\n)/$1/甚至比其他人还慢)。有趣的是m/\n/g速度基本相同s/\n//g,表明正则表达式方法的缓慢(两者s/// and m//)并不直接取决于以下问题editing字符串。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Perl 的 tr/\n// 随着行长度的增加而变得越来越慢? 的相关文章

  • 包含跨语言基准的资源?

    有哪些资源可以使用基准来比较编程语言 我对两者都感兴趣 给定语言的程序执行给定基准测试的速度有多快 给定语言需要多少行代码才能实现给定基准 有一个历史悠久的网站 https benchmarksgame team pages debian
  • Perl 中的 Fork 无法在从文件读取的 while 循环内工作

    我正在运行一个 while 循环来读取文件中的每一行 然后使用该行的数据将进程分叉给子进程 N 行之后 我想等待子进程结束并继续接下来的 N 行 等等 它看起来像这样 while w
  • 多维哈希排序 - Perl [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我真的需要一些帮助来理解这个哈希并对
  • 如何在没有循环的情况下初始化哈希中的值?

    我正在尝试找出一种无需经过循环即可初始化哈希的方法 我希望使用切片来实现这一点 但它似乎没有产生预期的结果 考虑以下代码 usr bin perl use Data Dumper my hash hash currency symbol B
  • 如何在正则表达式中匹配“/*”?

    stuff d learning perl tmp txt open STUFF stuff or die Cannot open stuff for read while
  • Perl 中的布尔函数应该返回什么值?

    简短的问题 在 Perl 代码库中一致表示真假的最佳方法是什么 1 0 1 Perl 原生布尔运算符的特殊空字符串 return undef 即空列表 问题背景 我们都知道 Perl 在布尔值方面非常灵活 就像大多数事情一样 例如 Perl
  • 如何使用 Net::SSH::Perl 和公钥?

    我正在尝试使用Net SSH Perl使用公钥与此代码进行连接 my ssh Net SSH Perl gt new host debug gt 1 die 我将密钥放在 root ssh id rsa 和 root ssh identit
  • SQL统计高于和低于平均分的学生人数

    我在下面有一个示例表 我试图获取高于平均分数的学生人数和低于平均分数的学生人数 name subject classroom classarm session first term score first term grade std1 m
  • 如何比较两个文件中的多列并在找​​到匹配时从另一列检索相应的值

    我有两个文件 File1 txt 和 File2 txt 我需要将 File1 中的三列 1 2 和 3 分别与 File2 的 4 5 和 6 进行比较 如果找到匹配项 我想从 File2 的第 2 列中检索相应的值并将其粘贴到输出中 T
  • 如何为不同的 Perl 应用程序安装专门的环境?

    就功能而言是否有与 Python 相同或接近的东西虚拟环境 http pypi python org pypi virtualenv what it does 但是对于 Perl 来说呢 我已经用 Python 进行了一些开发 并且将非系统
  • 计算每个唯一值出现的次数

    假设我有 v rep c 1 2 2 2 25 现在 我想计算每个唯一值出现的次数 unique v 返回唯一值是什么 但不返回它们的数量 gt unique v 1 1 2 我想要一些能给我的东西 length v v 1 1 25 le
  • Perl 中可以使用哪种语法糖来减少左/右值运算符与 if 语句的代码?

    那里有很多语句 因为 Perl 是一种非常甜蜜的语言 但任何语言中最常用的语句都是 if 语句和设置值的组合 我想我已经找到了很多 但仍然存在一些空白 最终 目标是不必多次编写变量名 这是我到目前为止所拥有的 r s r s unless
  • unix df 上的正则表达式帮助

    我需要一些帮助来调整我的代码以查找此 UNIX 中的另一个属性df output Ex Filesystem Size Used Avail Capacity Mounted on dev ad4s1e 61G 46G 9 7G 83 ho
  • 为什么Redis SET性能优于GET?

    根据Redis基准 http redis io topics benchmarkss Redis 可以执行 100 000 SET 操作 秒和 80 000 GET 操作 秒 Redis 是一种内存数据库 这似乎令人惊讶 因为通常人们会认为
  • 为什么我只得到第一个捕获组?

    https stackoverflow com a 2304626 6607497 https stackoverflow com a 2304626 6607497 and https stackoverflow com a 370042
  • perl-5.10 之前的高效版本相当于 pack("Q>")

    更新 萨尔瓦正确地指出我对 Q 包模板的介绍是错误的 这是 gt 修饰符 不会返回到 5 8 Perl 5 10 引入了 pack 修饰符 gt 对于我使用 Q 的用例 它将一个无符号四边形 64 位 值打包在大尾数法 现在 我正在寻找一个
  • 在perl中调用基本构造函数

    从 Perl 中的类构造函数调用基本构造函数的正确方法是什么 我见过这样的语法 my class shift my a shift my b shift my self class gt SUPER new a b return self
  • 使用包管理器时如何管理 Perl 模块?

    A 最近的问题 https stackoverflow com questions 397817 unable to find perl modules in intrepid ibex ubuntu这让我开始思考 在我尝试过的大多数 Li
  • Perl 三元条件运算符

    我正在尝试在脚本中编写更高效的代码 并且有时会实现三元条件运算符 我不明白为什么在循环中使用三元条件运算符时会得到额外的结果 usr bin perl use strict use warnings my array Serial 123
  • 在perl中更改多维哈希的第一个键

    我在 perl 中有一个多维哈希 我想更改所选值的第一个键 例如 我有哈希 my Hash1 Hash1 1 12 1 Hash1 1 10 1 Hash1 2 31 1 Hash1 3 52 1 Hash1 3 58 1 Hash1 4

随机推荐

  • Operator= 和 C++ 中未继承的函数?

    在我刚刚进行的测试之前 我认为在 C 中只有构造函数不被继承 但显然 任务operator 是不是太 这是什么原因呢 是否有任何解决方法来继承赋值运算符 是否也是如此operator operator 所有其他函数 除了构造函数 运算符 都
  • 暂停JW播放器?

    我有三个标签 每个选项卡的滑块中有两个视频 问题是当我切换任何选项卡时or单击任何单个视频 所有其他视频都应暂停 我可以收集所有 id 然后循环使用 stop 但是还有其他更干净 更简单的方法吗 jwplayer video pub sto
  • Universal Analytics - 第三方支付网关

    我们的网站目前正在通过跟踪代码管理器使用 Universal Analytics 进行跟踪 我们的结账流程包括在前往感谢页面之前重定向至第三方支付网关 所以 它看起来像这样 site com checkout gt site com pay
  • 通过保留顺序,根据 id 列将 Spark DataFrame 拆分为两个 DataFrame(70% 和 30%)

    我有一个 Spark 数据框 就像 id start time feature 1 01 01 2018 3 567 1 01 02 2018 4 454 1 01 03 2018 6 455 2 01 02 2018 343 4 2 01
  • 预先计算多维线性插值的权重

    我有一个沿 D 维度的非均匀矩形网格 网格上的逻辑值 V 矩阵和查询数据点 X 矩阵 网格点的数量在不同维度上有所不同 我对同一网格 G 和查询 X 多次运行插值 但对于不同的值 V 目标是预先计算插值的索引和权重并重用它们 因为它们始终相
  • Haystack more_like_this 返回全部

    我正在使用 Django haystack solr 进行搜索 我已经能够搜索 现在我想使用 more like this 查找类似的项目 当我尝试使用 more like this 功能时 我会返回属于该模型类型的所有对象 而不仅仅是与其
  • 以编程方式创建/运行命令文件

    我正在尝试创建一个 cmd 文件来安装 msi 然后通过 C 代码执行该 cmd 文件 如果我使用 Visual Studio 中的 f5 或 control f5 运行该代码 则该代码可以完美运行 但是 一旦我将代码打包在 msi 文件中
  • 如何在 ecmascript 6 中模拟导入的模块?

    我有一个带有 mocha babel 和 node 的测试设置 旨在测试 ecmascript 6 代码 有人对如何模拟被测模块中的导入有任何建议吗 ES2015 中的导入和导出是语言本身的一部分 并且被设计为可静态分析 因此 它们无法在运
  • 嵌套 CAST 不起作用

    为什么嵌套转换在 MySQL 中不起作用 它确实使用 SQL Server select cast cast myColumn as decimal 5 2 as int from myTable SQLFiddle示例 http sqlf
  • Codeigniter ajax CSRF问题

    我制作了一个简单的自动加载功能 当您在网站上向下滚动时加载内容 但是 当我在 Codeigniter 中启用 CSRF 保护时 似乎存在一些问题 我没有使用表单 所以当我在滚动时执行发布请求时 我不知道如何将令牌从 A 发送到 B 我的 J
  • 静态库的静态成员

    我有带有静态成员的静态库 该库静态链接到主应用程序及其插件之一 看起来像在 main 应用程序 和 dll 插件 中初始化静态变量 Question 如何避免动态库加载时静态变量重新初始化 或者我可能错过了一些简单的事情 更多信息 这是一个
  • Oracle:用户数据中按循环连接

    我了解 Oracle 中何时会发生循环 从理论上讲 如果一条记录既是另一个节点的父节点 又是另一个节点的子节点 那么它就可以进入循环 但我不明白为什么我的这个特定查询会陷入循环 SELECT Empno Ename Job FROM Emp
  • Github API OAuth 令牌验证

    有什么方法可以验证我的 github API 的 OAuth 令牌吗 我所说的 令牌 是指用户登录我的网站后获得的令牌 我使用 cookie 将其存储在客户端计算机上 但仅检查是否有令牌是不够的 我需要实际检查令牌是否有效 目前 这需要我提
  • 如果我访问 UserTransaction 是否意味着我使用 2 阶段提交或 XA?

    UserTransaction ut 查找 ut beginTransaction saveToFooDB statelessEjb transactionSupportedMethod 将一些内容保存到 Foo DB saveToFooD
  • mysql2 gem 出现 Gem::LoadError,但它已经在 Gemfile 中

    Gem LoadError Specified mysql2 for database adapter but the gem is not loaded Add gem mysql2 to your Gemfile 加载以下文件时发生此错
  • 使用nodejs通过ssl使用明文密码连接到MariaDB

    我正在尝试通过 ssl 连接到 mariadb 实例 var mysql require mysql var conn mysql createConnection user user password password debug tru
  • 如何找到列表的模式?

    我有一个清单 List
  • 在 Apache Spark 中,为什么 RDD.union 不保留分区器?

    众所周知 Spark中的分区器对任何 宽 操作都会产生巨大的性能影响 因此通常在操作中进行定制 我正在尝试以下代码 val rdd1 sc parallelize 1 to 50 keyBy 10 partitionBy new HashP
  • $offset 不适用于分页 codeigniter

    我正在尝试在我的搜索结果页面中实现分页 我昨天问过这个问题但没有成功使用 codeigniter 分页在搜索页面上不受限制的记录 https stackoverflow com questions 16775206 records not
  • 为什么 Perl 的 tr/\n// 随着行长度的增加而变得越来越慢?

    In perlfaq5 http faq perl org perlfaq5 html 有一个答案如何计算文件中的行数 http faq perl org perlfaq5 html How do I count the n 目前的答案建议