为什么 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// 随着行长度的增加而变得越来越慢? 的相关文章

  • 如何获取列中每个不同值的计数? [复制]

    这个问题在这里已经有答案了 我有一个名为 posts 的 SQL 表 如下所示 id category 1 3 2 1 3 4 4 2 5 1 6 1 7 2 每个类别编号对应一个类别 我将如何计算每个类别出现在帖子中的次数一条 SQL 查
  • 如何从 Perl 中的字符串中去除无效的 XML 字符?

    我正在寻找一种标准的 经过批准的 可靠的方法 可以在将字符串写入 XML 文件之前从字符串中删除无效字符 我在这里讨论的是包含退格键 H 和换页符等的文本块 There has成为执行此操作的标准库 模块函数 但我找不到它 我在用着XML
  • 多个与单个 Catalyst 应用程序

    我有多个作为 FCGI 运行的 Catalyst 应用程序 将它们整合为具有多个控制器的单个控制器是否有好处 Thanks Simone 内存 大概吧 我认为每台服务器至少要保留 15MB 左右 因此如果您在 3 台服务器上运行 3 个应用
  • 检测 perl 中声明的包变量

    Given package main our f sub f sub g 1 我怎样才能确定 f 但不是 g 已宣布 即兴的 我以为 main g SCALAR 可能是未定义的 但它是一个善意标量参考值 背景 我想将一个变量导入到main
  • 将参数传递给调试器中的 perl 文件并在系统执行的文件中设置断点

    因此 我使用 perl d file pl 在 perl 调试器中运行一个文件 但 file pl 也应该接受参数 如何向 file pl 提供参数 还有一个问题 file pl 中有这一行 system file2 pl 如果 file2
  • Term::ReadLine - 我需要点击向上箭头两次才能检索历史记录

    我正在使用 Term ReadLine 并遇到一个奇怪的问题 我需要点击向上箭头两次才能从 addhistory 中检索项目 这是我正在使用的脚本 use Term ReadLine my term Term ReadLine gt new
  • Perl 的 grep 函数如何与正则表达式一起使用?

    以下 grep 函数如何工作 什么作用 0o1Iil do chars grep 0o1Iil 0 9 A Z a z use Data Dumper print Dumper chars 在 chars中生成以下内容 VAR1 0 VAR
  • Perl 单元测试只针对模块,而不针对程序吗?

    我在网上找到的文档和我拥有的书 Perl测试 或者说或者建议 Perl 的单元测试通常是在创建模块时完成的 这是真的 有没有办法使用单元测试实际程序Test More和表兄弟姐妹 当然 您可以使用测试脚本测试 更多 http search
  • 我测量运行时间的方法有缺陷吗?

    抱歉 这篇文章很长 但我只是在分析这个问题时解释一下我的思路 问题在最后 我了解测量代码运行时间的原理 它运行多次以获得平均运行时间 以考虑每次运行的差异 并获得更好地利用缓存的时间 为了测量某人的跑步时间 我想出了this https s
  • 如何构建 Perl Web 服务基础设施

    我有许多用于管理多服务器基础设施的脚本 其中一些脚本需要 root 访问权限 一些需要访问数据库 并且大多数脚本都是基于 perl 的 我想将所有这些脚本转换为非常简单的 Web 服务 可以从不同的应用程序执行 这些 Web 服务将接受常规
  • 在 FOR 循环中打印唯一值

    我有两个文件 myresult 和 annotation 两个文件中的数据似乎是范围 但事实并非如此 这就是为什么我无法将其存储在数组中 我需要使用拆分运算符 以便我可以在 for 循环中使用它并进行比较 现在我需要打印 i myresul
  • 由于 lpcap,CPAN 安装 Net::Pcap 和 Packet 模块失败

    最近 我尝试通过 cpan 安装 perl 模块 Net Packet 和 Net Pcap 但它抱怨找不到 pcap 库 所以我正在搜索 cpan 在安装模块时是否接受外部 lib include 目录 但到目前为止还没有运气 looki
  • Perl 的反引号、system 和 exec 有什么区别?

    有人可以帮帮我吗 在 Perl 中 以下之间有什么区别 exec command and system command and print command 还有其他方法可以运行 shell 命令吗 exec 执行命令并一去不复返 这就像一个
  • 如何从数据框中删除少于 5 个观察值的个体 [重复]

    这个问题在这里已经有答案了 为了澄清这个问题 我将简要描述数据 中的每一行data frame是一个观察值 列代表与该观察值相关的变量 包括 观察到什么个体 观察时间 观察地点等 我想排除 过滤观察值少于 5 个的个体 换句话说 如果 in
  • 如何将会话管理添加到简单的 Perl CGI 网页?

    我有一个简单的网页 到目前为止不需要任何登录 它是用 Perl CGI 编程的 我想知道添加会话支持以便获得登录信息的步骤 我不需要非常复杂的方法 因为网页非常简单 另外 我想要一些关于支持 Perl 会话所需的技术 库的建议 我在很多很多
  • 在 qx 运算符中将 perl 数组拆分为单独的参数

    我试图将一组参数传递给qx操作员 考虑 my files qw A txt B txt print qx ls files 这给出了错误 ls cannot access A txt B txt No such file or direct
  • 有没有办法匹配任意 Unicode 字母字符?

    我有一些文档经过 OCR 从 PDF 转换为 HTML 因此 他们最终会出现很多随机的 unicode 标点符号 而转换器会搞砸 即省略号等 他们还正确地有一堆非英语但仍然是字母字符 如 和俄语字符等 有没有办法制作一个匹配任何 unico
  • grep 两个分隔符之间的子字符串

    我有很多bash使用的脚本perl内的表达式grep为了提取两个分隔符之间的子字符串 例子 echo BeginMiddleEnd grep oP lt Begin End 问题是 当我将这些脚本移植到运行的平台时busybox 融合的 g
  • Perl:测试输入阅读器?

    有没有一种方法可以使用标准 Test 等模块自动测试 Perl 程序是否正在读取输入 例如标准输入正确吗 例如 测试一个从 STDIN 读取两个整数并打印它们之和的程序 这不是 100 清楚你的意思 我会回答假设你想编写一个测试脚本来测试你
  • 检查一个数字是 int 还是 float

    在perl中 我想检查给定变量是否包含浮点数 为了检查我正在使用的 my Var 0 02 Floating point number if int Var Var floating point number 但上面的代码对于 0 0 不起

随机推荐

  • 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 目前的答案建议