如何在不使用模块的情况下分析子程序?

时间:2010-04-25 15:00:47

标签: perl time profiling

我很想重新回答这个问题'看看这块砖。它属于哪种类型的房子?'

情况就是这样:我有效地被要求分析一些子程序,这些子程序既不能访问分析器(甚至Devel::DProf}也不能Time::HiRes。这个练习的目的是“找到”瓶颈。

目前,我正在每个子文件的开头和结尾处打印一个打印语句,用于记录条目和退出文件,以及time函数的结果。不理想,但鉴于情况,这是我能做的最好的事情。至少它会让我看到每个子被调用多少次。

代码在Unix下运行。我认为最接近我需要的是perlfaq8,但这似乎没有帮助(我不知道如何制作syscall,并且想知道它是否会影响代码时序不可预知)。

不是典型的日常问题......

4 个答案:

答案 0 :(得分:7)

This technique should work.

基本上,我们的想法是,如果您使用-d标志运行Perl,它将进入调试器。然后,当你运行程序时,ctrl-Break或ctrl-C应该使它在它正在做的任何事情的中间暂停。然后你可以输入T来显示堆栈,并在继续之前检查任何其他变量。

这样做大约10或20次。任何代码行(或任何函数,如果你愿意)花费相当大的时间百分比将大约出现在堆栈样本的百分比上,所以你不会错过它。

例如,如果一行代码(通常是函数调用)花费20%的时间,并且您暂停程序20次,您将在4个堆栈样本上看到该行,给出或取1.8个样本。如果你可以避免执行该行或者执行它少得多,可以节省的时间是总体执行时间减少20%。

然后你可以重复它以找到更多问题。

你说的目的是“找到”瓶颈。这种方法正是如此。测量函数执行时间只是一种非常间接的方式。

答案 1 :(得分:1)

  1. syscall而言,这篇文章中有一个很好的例子:http://www.cpan.org/scripts/date_and_time/gettimeofday

    我认为即使对于之前从未使用过系统调用的人来说也很清楚(像我一样)

  2. 请问“无法访问”的具体内容是什么?

    通常可以访问CPAN模块,即使在中心位置安装它们的情况下也不在卡中。下载模块有问题吗?在主目录中安装它?使用带模块的软件?

    如果其中一个是挂断,它可能会被修复......如果这是一些公司政策,那是无价的:(

答案 2 :(得分:0)

好吧,你可以编写自己的探查器。它没有听起来那么糟糕。分析器只是一个非常特殊的调试器。如果必须编写自己的代码,您希望阅读perldebguts手册页以获取一些优秀的首要代码。

你想要什么,以及你的老板想要什么,虽然他或她可能不知道,但是要使用Devel::NYTProf来很好地分析你的代码,并获得完成这项工作,而不是在学习如何完成工作的同时等待你部分复制它的功能。

您对“个人使用”的评论没有意义。你正在做一份工作,工作需要完成,你需要(或你的经理需要得到你)这些工作的资源。 “个人使用”似乎没有进入它。

是否有人拒绝在模块上签名将其安装在运行要测量的软件的计算机上?这是许可问题吗?是不允许在生产机器上安装任意软件(可以理解,但是在软件上线之前必须对软件进行测试 - 我希望 - 在那里进行配置)?

无法使用来自可靠来源的知名模块的原因是什么?你有没有向你的经理提出这样的情况:从头开始编写一个新的,功能较少的剖析器,而不是找到一种方法来使用既好又可用的资源呢?

答案 3 :(得分:0)

对于每个子例程,在它周围创建一个包装器,它以某种格式报告时间,您可以将其导出为R,数据库,Excel或类似的东西(CSV将是一个不错的选择)。在代码中添加类似的内容。如果您使用的Perl小于5.7(当Time :: HiRes首次添加到核心时),请使用上面提到的系统调用而不是下面的Time :: HiRes函数。

INIT {
    sub wrap_sub {
        no strict 'refs';
        my $sub = shift;
        my $subref = *{$sub}{CODE};
        return sub {
            local *__ANON__ = "wrapped_$sub";
            my $fsecs = Time::HiRes::gettimeofday();
            print STDERR "$sub,$fsecs,";
            if (wantarray) {
               @return = eval { $subref->(@_) } or die $@;
            } else {
               $return[0] = eval { $subref->(@_) } or die $@;
            }

            $fsecs = Time::HiRes::gettimeofday();
            print STDERR "$fsecs\n";
            return wantarray ? @return : $return[0];
        };
    }
    require Time::HiRes;
    my @subs = qw{the subs you want to profile};
    no strict 'refs';
    no warnings 'redefine';
    foreach my $sub (@subs) {
        *{$sub} = wrap_sub($sub);
    }    
}

用您需要分析的子替换'您要分析的子',并在需要时使用open()ed文件句柄而不是STDERR,记住您可以将输出的结果与输出分开脚本(在Unix上,使用bourne,korn和bash shell),像这样

perl ./myscript.pl 2>myscript.profile